diff --git a/CHANGELOG.md b/CHANGELOG.md index 193b357..dc479cc 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,3 +1,10 @@ +## Unreleased + +### Changed + +* `catchToken` will now return the token and associated data if it does not detect an opener window to pass the data to +* `ImmersClient.loginWithToken` now also accepts sessionInfo parameter to be fully equivalent with regular login + ## v2.16.0 (2023-03-29) ### Added diff --git a/oneLiner.js b/oneLiner.js index 8ce951c..2c16232 100644 --- a/oneLiner.js +++ b/oneLiner.js @@ -10,7 +10,7 @@ try { console.warn(`Unable to process query arguments to script, ${err.message}`) } (async function () { - if (catchToken()) { + if (catchToken() === true) { // token was passed to opener window; this is just a popup return } diff --git a/package.json b/package.json index 2262fb5..dfdaee0 100644 --- a/package.json +++ b/package.json @@ -11,7 +11,7 @@ "test": "standard", "doc": "jsdoc -c jsdoc.config.json -d doc -r source/. -R README.md && cp -R readme_files doc/", "build": "webpack --config webpack.prod.js", - "type": "tsc index.js source/**/*.js -t es2020 --declaration --skipLibCheck --allowJs --emitDeclarationOnly --moduleResolution node --outDir types", + "type": "tsc index.js -t es2020 --declaration --skipLibCheck --allowJs --emitDeclarationOnly --moduleResolution node --outDir types", "dev": "webpack serve --open --config webpack.dev.js", "prepare": "npm run type && npm run build" }, diff --git a/source/authUtils.js b/source/authUtils.js index d4d49bc..3cd5886 100644 --- a/source/authUtils.js +++ b/source/authUtils.js @@ -41,9 +41,21 @@ export const allScopes = [ */ export const roles = ['public', 'friends', 'modAdditive', 'modFull'] +/** + * @typedef {object} TokenResult + * @property {string} token OAuth access token + * @property {string} homeImmer User's home Immers Server origin + * @property {Array} authorizedScopes Scopes granted by user (may differ from requested scopes) + * @property {object} sessionInfo Any other params returned from the authorization server with the token + */ /** * Retrieve OAuth access token and authorization details from URL after - * redirect and pass it back to the opening window if in a pop-up + * redirect and pass it back to the opening window if in a pop-up. Returns true + * if a token was found and passed from popup to opener. Returns the token response + * data if a token was found but the window is not a popup + * (e.g. to pass on to [ImmersClient.loginWithToken]{@link ImmersClient#loginWithToken}). + * Returns false if no token found. + * @returns {boolean | TokenResult} */ export function catchToken () { const hashParams = new URLSearchParams(window.location.hash.substring(1)) @@ -58,16 +70,23 @@ export function catchToken () { const sessionInfo = Object.fromEntries(hashParams) window.location.hash = '' // If this is an oauth popup, pass the results back up and close - // todo check origin - if (window.opener) { - window.opener.postMessage({ - type: 'ImmersAuth', - token, - homeImmer, - authorizedScopes, - sessionInfo - }) - return true + try { + if (window.opener?.location.origin === window.location.origin) { + window.opener.postMessage({ + type: 'ImmersAuth', + token, + homeImmer, + authorizedScopes, + sessionInfo + }) + return true + } + } catch {} + return { + token, + homeImmer, + authorizedScopes, + sessionInfo } } else if (hashParams.has('error')) { window.opener?.postMessage({ type: 'ImmersAuth', error: hashParams.get('error') }) diff --git a/source/client.js b/source/client.js index 2b27b3b..e5e047a 100644 --- a/source/client.js +++ b/source/client.js @@ -189,16 +189,18 @@ export class ImmersClient extends window.EventTarget { /** * Initialize client with an existing credential, - * e.g. one obtained through a service account + * e.g. one obtained through a service account or one returned from {@link catchToken} + * when performing a redirect based OAuth flow * @param {string} token - OAuth2 Access Token * @param {string} homeImmer - Domain (host) for user's home immer * @param {(string|string[])} authorizedScopes - Scopes authorized for the token + * @param {object} [sessionInfo] - optional session data provided alongside token * @returns {Promise} true if the login was successful */ - loginWithToken (token, homeImmer, authorizedScopes) { + loginWithToken (token, homeImmer, authorizedScopes, sessionInfo) { homeImmer = getURLPart(homeImmer, 'origin') authorizedScopes = preprocessScopes(authorizedScopes) - this.#store.credential = { token, homeImmer, authorizedScopes } + this.#store.credential = { token, homeImmer, authorizedScopes, sessionInfo } return this.restoreSession() }