diff --git a/app/services/auth.js b/app/services/auth.js index 9b2ee4b77a..9a3e2d0bb0 100644 --- a/app/services/auth.js +++ b/app/services/auth.js @@ -93,6 +93,33 @@ export default Service.extend({ window.addEventListener('focus', () => this.checkAuth()); }, + /** + * Sets a domain-wide ( .travis-ci.com ) cookie indicating whether the user + * is currently signed in (1) or signed out (0). The cookie is consumed by + * the marketing / WordPress site to perform automatic redirects. + * + * Note: Cookie cannot be marked HttpOnly because it must be readable by + * client-side JavaScript on marketing pages. + */ + setSharedLoginCookie(isLoggedIn) { + try { + const host = window.location.hostname; + const cfg = config.loginStateCookie || {}; + const name = cfg.name; + const days = cfg.expiryDays || 90; + const expires = new Date(Date.now() + days * 864e5).toUTCString(); + const domainPart = /travis-ci\.com$/.test(host) ? '; Domain=.travis-ci.com' : ''; + const securePart = window.location.protocol === 'https:' ? '; Secure' : ''; + const value = isLoggedIn ? '1' : '0'; // (1=logged in,0=out) + document.cookie = `${name}=${value}; Expires=${expires}; Path=/${domainPart}${securePart}; SameSite=Lax`; + } catch (e) {} + }, + + clearSharedLoginCookie() { + this.setSharedLoginCookie(false); + }, + + checkAuth() { if (!this.currentUser || !this.storage) return; @@ -141,6 +168,7 @@ export default Service.extend({ this.router.transitionTo('signin'); } catch (e) {} } + this.clearSharedLoginCookie(); }, afterSignOut(callback) { @@ -198,6 +226,7 @@ export default Service.extend({ this.set('state', STATE.SIGNED_IN); Travis.trigger('user:signed_in', currentUser); Travis.trigger('user:refreshed', currentUser); + this.setSharedLoginCookie(true); }) .catch(error => { if (!didCancel(error)) { diff --git a/config/environment.js b/config/environment.js index b352500ef9..8f73fcb2f0 100644 --- a/config/environment.js +++ b/config/environment.js @@ -53,6 +53,12 @@ module.exports = function (environment) { plans, screens, tailwind, + // login state cookie (used by marketing site for redirect decisions) + loginStateCookie: { + name: 'logged_in_to_app', + expiryDays: + parseInt(process.env.LOGGED_IN_TO_APP_COOKIE_EXPIRY, 10) || 90, + }, EmberENV: { FEATURES: { // Here you can enable experimental features on an ember canary build diff --git a/tests/acceptance/profile/billing-test.js b/tests/acceptance/profile/billing-test.js index dfea88b32e..14f566328e 100644 --- a/tests/acceptance/profile/billing-test.js +++ b/tests/acceptance/profile/billing-test.js @@ -446,7 +446,7 @@ module('Acceptance | profile/billing', function (hooks) { test('view billing on a manual plan with no invoices', async function (assert) { this.subscription.source = 'manual'; this.subscription.status = undefined; - this.subscription.valid_to = new Date(2025, 7, 16).toISOString(); + this.subscription.valid_to = new Date(2099, 1, 1).toISOString(); await profilePage.visit(); await profilePage.billing.visit();