diff --git a/kbase-extension/static/kbase/js/api/auth.js b/kbase-extension/static/kbase/js/api/auth.js index 15d37fa022..6107187885 100644 --- a/kbase-extension/static/kbase/js/api/auth.js +++ b/kbase-extension/static/kbase/js/api/auth.js @@ -24,19 +24,18 @@ define(['bluebird', 'jquery', 'narrativeConfig'], (Promise, $, Config) => { }, }; - const DEFAULT_TOKEN_LIFE = 14 * 24 * 60 * 60 * 1000; // millis - /** * Meant for managing auth or session cookies (mainly auth cookies as set by * a developer working locally - which is why this is very very simple). * Get a cookie "object" (key-value pairs) as input. - * If it's missing name or value, does nothing. - * Default expiration time is 14 days. - * domain, expires, and max-age are optional - * expires is expected to be the timestamp (ms since epoch) it will expire + * If it's missing name, does nothing. + * expires is REQUIRED and is expected to be the timestamp (ms since epoch) + * at which the cookie expires. It must be sourced from the auth service + * (a token's real expiration) rather than a hardcoded client-side lifetime, + * so the cookie always tracks the actual token lifetime. + * domain and max-age are optional. * default fields are: * - path = '/' - * - expires = now + 14 days * @param {object} cookie * - has the cookie keys: name, value, path, expires, max-age, domain * - adds secure=true, samesite=Lax for KBase use. @@ -47,16 +46,18 @@ define(['bluebird', 'jquery', 'narrativeConfig'], (Promise, $, Config) => { if (!cookie.name) { return; } + if (!Number.isInteger(cookie.expires)) { + throw new Error( + 'setCookie requires an integer `expires` (ms since epoch) sourced from the auth service' + ); + } const name = encodeURIComponent(cookie.name); const value = encodeURIComponent(cookie.value || ''); const props = { - expires: Date.now() + DEFAULT_TOKEN_LIFE, // gets translated to GMT string + expires: cookie.expires, // gets translated to GMT string path: '/', samesite: 'Lax', }; - if (Number.isInteger(cookie.expires)) { - props.expires = cookie.expires; - } // Default to secure cookies global setting if not specified. if (typeof cookie.secure === 'undefined') { diff --git a/kbase-extension/static/kbase/js/narrativeLogin.js b/kbase-extension/static/kbase/js/narrativeLogin.js index c96111ee8f..74cf9e8f07 100644 --- a/kbase-extension/static/kbase/js/narrativeLogin.js +++ b/kbase-extension/static/kbase/js/narrativeLogin.js @@ -80,14 +80,27 @@ define([ buttons: [ $('') .append('OK') - .click(() => { - dialog.hide(); + .click(async () => { const newToken = $inputField.val(); + // Look up the token's real expiration from the auth service + // rather than assuming a fixed lifetime, so the dev cookie + // matches the actual token lifetime. + let tokenInfo; + try { + tokenInfo = await authClient.getTokenInfo(newToken); + } catch (error) { + alert( + 'That auth token could not be validated against the auth service. Please check the token and try again.' + ); + return; + } + dialog.hide(); authClient.setCookie({ name: 'kbase_session', value: newToken, domain: 'localhost', secure: false, + expires: tokenInfo.expires, }); location.reload(); }), diff --git a/test/unit/spec/api/authSpec.js b/test/unit/spec/api/authSpec.js index 25cca20c31..5ee3f9a958 100644 --- a/test/unit/spec/api/authSpec.js +++ b/test/unit/spec/api/authSpec.js @@ -451,6 +451,7 @@ define(['api/auth', 'narrativeConfig', 'uuid', 'testUtil'], (Auth, Config, Uuid, name: backupCookieName, value: backupCookieValue, domain: 'localhost', + expires: Date.now() + 14 * 1000 * 60 * 60 * 24, }); // The backup cookie should be set.