diff --git a/CHANGELOG.md b/CHANGELOG.md index 66afc0b95..1b973cf32 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,13 +1,26 @@ # Changelog -## 7.6.0 +# 7.7.1 + +- [#1529](https://github.com/okta/okta-auth-js/pull/1529) fix: persist `extraParams` passed to `/authorize` and include them during token refresh + +## 7.7.0 ### Features - [#1495](https://github.com/okta/okta-auth-js/pull/1495) add: DPoP support + +### Fixes + +- [#1508](https://github.com/okta/okta-auth-js/pull/1508) IDX: add condition to compare stateHandles when loading saved idxResponse only when useGenericRemediator option is false or undefined + + +## 7.6.0 + +### Features + - [#1507](https://github.com/okta/okta-auth-js/pull/1507) add: new method `getOrRenewAccessToken` - [#1505](https://github.com/okta/okta-auth-js/pull/1505) add: support of `revokeSessions` param for `OktaPassword` authenticator (can be used in `reset-authenticator` remediation) -- [#1508](https://github.com/okta/okta-auth-js/pull/1508) IDX: add condition to compare stateHandles when loading saved idxResponse only when useGenericRemediator option is false or undefined - [#1512](https://github.com/okta/okta-auth-js/pull/1512) add: new service `RenewOnTabActivation` ### Bug Fix diff --git a/lib/oidc/endpoints/token.ts b/lib/oidc/endpoints/token.ts index e98f2f1e5..8a257a5cc 100644 --- a/lib/oidc/endpoints/token.ts +++ b/lib/oidc/endpoints/token.ts @@ -141,8 +141,13 @@ export async function postRefreshToken( return name + '=' + encodeURIComponent(value!); }).join('&'); + let url = refreshToken.tokenUrl; + if (options.extraParams && Object.keys(options.extraParams).length >= 1) { + url += toQueryString(options.extraParams); + } + const params: TokenRequestParams = { - url: refreshToken.tokenUrl, + url, data, dpopKeyPair: options?.dpopKeyPair }; diff --git a/lib/oidc/exchangeCodeForTokens.ts b/lib/oidc/exchangeCodeForTokens.ts index 12cba44d0..b679ff020 100644 --- a/lib/oidc/exchangeCodeForTokens.ts +++ b/lib/oidc/exchangeCodeForTokens.ts @@ -37,6 +37,7 @@ export async function exchangeCodeForTokens(sdk: OktaAuthOAuthInterface, tokenPa acrValues, dpop, dpopPairId, + extraParams } = tokenParams; // postToTokenEndpoint() params @@ -64,6 +65,7 @@ export async function exchangeCodeForTokens(sdk: OktaAuthOAuthInterface, tokenPa responseType, ignoreSignature, acrValues, + extraParams }; try { diff --git a/lib/oidc/handleOAuthResponse.ts b/lib/oidc/handleOAuthResponse.ts index 02fa80cb7..ea92baddb 100644 --- a/lib/oidc/handleOAuthResponse.ts +++ b/lib/oidc/handleOAuthResponse.ts @@ -55,6 +55,7 @@ export async function handleOAuthResponse( ): Promise { const pkce = sdk.options.pkce !== false; + // The result contains an authorization_code and PKCE is enabled // `exchangeCodeForTokens` will call /token then call `handleOauthResponse` recursively with the result if (pkce && (res.code || res.interaction_code)) { @@ -106,6 +107,10 @@ export async function handleOAuthResponse( if (tokenParams.dpopPairId) { tokenDict.accessToken.dpopPairId = tokenParams.dpopPairId; } + + if (tokenParams.extraParams) { + tokenDict.accessToken.extraParams = tokenParams.extraParams; + } } if (refreshToken) { @@ -123,6 +128,10 @@ export async function handleOAuthResponse( if (tokenParams.dpopPairId) { tokenDict.refreshToken.dpopPairId = tokenParams.dpopPairId; } + + if (tokenParams.extraParams) { + tokenDict.refreshToken.extraParams = tokenParams.extraParams; + } } if (idToken) { @@ -137,6 +146,10 @@ export async function handleOAuthResponse( clientId: clientId! }; + if (tokenParams.extraParams) { + idTokenObj.extraParams = tokenParams.extraParams; + } + const validationParams: TokenVerifyParams = { clientId: clientId!, issuer: urls.issuer!, diff --git a/lib/oidc/renewToken.ts b/lib/oidc/renewToken.ts index fcf0dbdd7..d59e28b02 100644 --- a/lib/oidc/renewToken.ts +++ b/lib/oidc/renewToken.ts @@ -55,14 +55,15 @@ export async function renewToken(sdk: OktaAuthOAuthInterface, token: Token): Pro responseType = 'id_token'; } - const { scopes, authorizeUrl, userinfoUrl, issuer, dpopPairId } = token as (AccessToken & IDToken); + const { scopes, authorizeUrl, userinfoUrl, issuer, dpopPairId, extraParams } = token as (AccessToken & IDToken); return getWithoutPrompt(sdk, { responseType, scopes, authorizeUrl, userinfoUrl, issuer, - dpopPairId + dpopPairId, + extraParams }) .then(function (res) { return getSingleToken(token, res.tokens); diff --git a/lib/oidc/renewTokens.ts b/lib/oidc/renewTokens.ts index 3e47d9939..d7f853a47 100644 --- a/lib/oidc/renewTokens.ts +++ b/lib/oidc/renewTokens.ts @@ -41,6 +41,7 @@ export async function renewTokens(sdk, options?: RenewTokensParams): Promise; } export interface AccessToken extends AbstractToken { diff --git a/lib/oidc/types/meta.ts b/lib/oidc/types/meta.ts index 8b857a2b1..fd096d550 100644 --- a/lib/oidc/types/meta.ts +++ b/lib/oidc/types/meta.ts @@ -26,7 +26,8 @@ export interface OAuthTransactionMeta extends 'ignoreSignature' | 'nonce' | 'acrValues' | - 'enrollAmrValues' + 'enrollAmrValues' | + 'extraParams' > { urls: CustomUrls; diff --git a/lib/oidc/util/oauthMeta.ts b/lib/oidc/util/oauthMeta.ts index 6e00e1e6e..94f2ebeb2 100644 --- a/lib/oidc/util/oauthMeta.ts +++ b/lib/oidc/util/oauthMeta.ts @@ -20,6 +20,7 @@ export function createOAuthMeta( nonce: tokenParams.nonce!, ignoreSignature: tokenParams.ignoreSignature!, acrValues: tokenParams.acrValues, + extraParams: tokenParams.extraParams }; if (tokenParams.pkce === false) { diff --git a/package.json b/package.json index b0ff8d5e1..42b3625f3 100644 --- a/package.json +++ b/package.json @@ -2,7 +2,7 @@ "private": true, "name": "@okta/okta-auth-js", "description": "The Okta Auth SDK", - "version": "7.7.0", + "version": "7.7.1", "homepage": "https://github.com/okta/okta-auth-js", "license": "Apache-2.0", "main": "build/cjs/exports/default.js", diff --git a/samples/test/cucumber.wdio.conf.ts b/samples/test/cucumber.wdio.conf.ts index e306851ec..63d8c3ef8 100644 --- a/samples/test/cucumber.wdio.conf.ts +++ b/samples/test/cucumber.wdio.conf.ts @@ -26,7 +26,7 @@ const USE_FIREFOX = !!process.env.USE_FIREFOX; const DEBUG = process.env.DEBUG; const CI = process.env.CI; const LOG = process.env.LOG; -const defaultTimeoutInterval = DEBUG ? (24 * 60 * 60 * 1000) : 10000; +const defaultTimeoutInterval = DEBUG ? (24 * 60 * 60 * 1000) : 15000; const logLevel = LOG || 'warn'; const chromeOptions = { args: [] diff --git a/scripts/e2e/e2e-mfa.sh b/scripts/e2e/e2e-mfa.sh index acbfb7e9d..6c5cc580a 100755 --- a/scripts/e2e/e2e-mfa.sh +++ b/scripts/e2e/e2e-mfa.sh @@ -15,7 +15,7 @@ export CLIENT_ID=0oa41zpqqxar7OFl84x7 export SPA_CLIENT_ID=0oa41zpqqxar7OFl84x7 export MFA_ENABLED=true -get_vault_secret_key devex/auth-js-sdk-vars security_question_answer SECURITY_QUESTION_ANSWER -get_vault_secret_key devex/auth-js-sdk-vars a18n_api_key A18N_API_KEY +get_terminus_secret "/" security_question_answer SECURITY_QUESTION_ANSWER +get_terminus_secret "/" a18n_api_key A18N_API_KEY run_e2e diff --git a/scripts/e2e/e2e-saucelabs.sh b/scripts/e2e/e2e-saucelabs.sh index 8dfe0ae9a..1635e9d78 100755 --- a/scripts/e2e/e2e-saucelabs.sh +++ b/scripts/e2e/e2e-saucelabs.sh @@ -9,7 +9,7 @@ export TEST_RESULT_FILE_DIR="${REPO}/build2/reports/e2e-saucelabs" # run e2e tests with test/e2e/sauce.wdio.conf.js config export CLIENT_ID=0oa1xyzajgPFGWlLP4x7 -get_vault_secret_key devex/sauce-labs accessKey SAUCE_ACCESS_KEY +get_terminus_secret "/" accessKey SAUCE_ACCESS_KEY export RUN_SAUCE_TESTS=true export SAUCE_USERNAME=OktaSignInWidget diff --git a/scripts/integration.sh b/scripts/integration.sh index 282f43e72..cbbc22d68 100755 --- a/scripts/integration.sh +++ b/scripts/integration.sh @@ -8,11 +8,11 @@ export TEST_RESULT_FILE_DIR="${REPO}/build2/reports/unit" export CI=true export ISSUER=https://javascript-idx-sdk-idfirst.okta.com export USERNAME=george@acme.com -get_vault_secret_key repo_gh-okta-okta-auth-js/default password PASSWORD +get_terminus_secret "/" password PASSWORD # for myaccount password API testing export PASSWORDLESS_USERNAME=password.optional@mailinator.com -get_vault_secret_key devex/auth-js-sdk-vars a18n_api_key A18N_API_KEY +get_terminus_secret "/" a18n_api_key A18N_API_KEY # This client has refresh token enabled and password optional export CLIENT_ID=0oa3b5fp4h02UIrjZ697 diff --git a/scripts/samples/e2e-express-embedded-auth-with-sdk-features.sh b/scripts/samples/e2e-express-embedded-auth-with-sdk-features.sh index e4ef719b8..6ba3651b8 100644 --- a/scripts/samples/e2e-express-embedded-auth-with-sdk-features.sh +++ b/scripts/samples/e2e-express-embedded-auth-with-sdk-features.sh @@ -10,7 +10,7 @@ export SAMPLE_NAME=@okta/samples.express-embedded-auth-with-sdk export MAX_INSTANCES=1 # NOTE: the command below evaluates to the same PASSWORD retrieved in setup-e2e, leaving commented just in case -# get_vault_secret_key devex/prod-js-idx-sdk-vars password PASSWORD +# get_terminus_secret "/" password PASSWORD # based on run_sample_tests create_log_group "E2E Test Run" diff --git a/scripts/samples/e2e-express-embedded-auth-with-sdk-spec.sh b/scripts/samples/e2e-express-embedded-auth-with-sdk-spec.sh index 18b4a1390..d2ae8e081 100644 --- a/scripts/samples/e2e-express-embedded-auth-with-sdk-spec.sh +++ b/scripts/samples/e2e-express-embedded-auth-with-sdk-spec.sh @@ -10,7 +10,7 @@ export SAMPLE_NAME=@okta/samples.express-embedded-auth-with-sdk export MAX_INSTANCES=1 # NOTE: the command below evaluates to the same PASSWORD retrieved in setup-e2e, leaving commented just in case -# get_vault_secret_key devex/prod-js-idx-sdk-vars password PASSWORD +# get_terminus_secret "/" password PASSWORD # based on run_sample_tests create_log_group "E2E Test Run" diff --git a/scripts/samples/e2e-express-embedded-auth-with-sdk.sh b/scripts/samples/e2e-express-embedded-auth-with-sdk.sh index d93e81b67..ddb61d23c 100755 --- a/scripts/samples/e2e-express-embedded-auth-with-sdk.sh +++ b/scripts/samples/e2e-express-embedded-auth-with-sdk.sh @@ -10,7 +10,7 @@ export SAMPLE_NAME=@okta/samples.express-embedded-auth-with-sdk export MAX_INSTANCES=1 # NOTE: the command below evaluates to the same PASSWORD retrieved in setup-e2e, leaving commented just in case -# get_vault_secret_key devex/prod-js-idx-sdk-vars password PASSWORD +# get_terminus_secret "/" password PASSWORD # based on run_sample_tests create_log_group "E2E Spec Test Run" diff --git a/scripts/setup-e2e.sh b/scripts/setup-e2e.sh index 59dbfb555..4243603d0 100755 --- a/scripts/setup-e2e.sh +++ b/scripts/setup-e2e.sh @@ -10,17 +10,33 @@ if [ -n "${TEST_SUITE_ID}" ]; then setup_service java 1.8.222 # this chrome install is not used, however it will install linux deps chrome needs (via apt-get) - setup_service google-chrome-stable 118.0.5993.70-1 + # setup_service google-chrome-stable 127.0.6533.88-1 + setup_service google-chrome-stable 121.0.6167.85-1 # uses new chrome for testing installation utility (https://developer.chrome.com/blog/chrome-for-testing/) # output format: chrome@118.0.5993.70 /path/to/chrome/binary - CHROME_INSTALL=$(npx @puppeteer/browsers install chrome@stable) - echo $CHROME_INSTALL - # extract installed version - export CHROMEDRIVER_VERSION=$(echo $CHROME_INSTALL | awk '{print $1}' | cut -d'@' -f 2) - echo $CHROMEDRIVER_VERSION - # extract binary path - export CHROME_BINARY=$(echo $CHROME_INSTALL | awk '{print $2}') - echo $CHROME_BINARY + # npm i -g @puppeteer/browsers@1.x + # @puppeteer/browsers install chrome@stable] + + # OLD_NPM_REGISTRY=$(npm config get registry) + # npm config set registry https://registry.npmjs.org + # npm config get registry + + # echo "Running npx @puppeteer/browsers" + # npx @puppeteer/browsers install chrome@stable --version + # npx @puppeteer/browsers install chrome@stable + # echo "Running puppeteer install" + # CHROME_INSTALL=$(npx @puppeteer/browsers@1.x install chrome@stable) + # echo "CHROME_INSTALL: $CHROME_INSTALL" + # # extract installed version + # export CHROMEDRIVER_VERSION=$(echo $CHROME_INSTALL | awk '{print $1}' | cut -d'@' -f 2) + # echo $CHROMEDRIVER_VERSION + # # extract binary path + # export CHROME_BINARY=$(echo $CHROME_INSTALL | awk '{print $2}') + # echo $CHROME_BINARY + + # npm config set registry $OLD_NPM_REGISTRY + # wget -v https://storage.googleapis.com/chrome-for-testing-public/127.0.6533.88/linux64/chrome-linux64.zip + # echo $(ls) export CI=true else @@ -45,12 +61,18 @@ setup_e2e () { export ISSUER=https://samples-javascript.okta.com/oauth2/default export USERNAME=george@acme.com - get_vault_secret_key repo_gh-okta-okta-auth-js/default password PASSWORD + get_terminus_secret "/" password PASSWORD finish_log_group $? } run_e2e () { create_log_group "E2E Test Run" + + if [ -z "${PASSWORD}" ]; then + echo "No PASSWORD has been set! Exiting..." + exit ${TEST_FAILURE} + fi + if [ -n "${RUN_CUCUMBER}" ]; then if ! yarn test:e2e:cucumber; then echo "Cucumber tests failed! Exiting..." @@ -76,10 +98,11 @@ setup_sample_tests () { export TEST_RESULT_FILE_DIR="${REPO}/build2/reports/e2e" export USERNAME=mary@acme.com - get_vault_secret_key repo_gh-okta-okta-auth-js/default password PASSWORD + get_terminus_secret "/" password PASSWORD export ORG_OIE_ENABLED=true - get_vault_secret_key devex/auth-js-sdk-vars a18n_api_key A18N_API_KEY + get_terminus_secret "/" a18n_api_key A18N_API_KEY + # If this script is run as a bacon task, run against trexcloud environment if [[ "${BACON_TASK}" == true ]]; then @@ -87,24 +110,24 @@ setup_sample_tests () { export ISSUER=https://javascript-idx-sdk.trexcloud.com export CLIENT_ID=0oa3r1keeeFFb7VMG0g7 export SPA_CLIENT_ID=0oa3r92jj01DWBeWC0g7 - get_vault_secret_key devex/trex-js-idx-sdk-vars trex_client_secret CLIENT_SECRET - get_vault_secret_key devex/trex-js-idx-sdk-vars trex_idx_sdk_org_api_key OKTA_API_KEY + get_terminus_secret "/" trex_client_secret CLIENT_SECRET + get_terminus_secret "/" trex_idx_sdk_org_api_key OKTA_API_KEY else if [ -n "$USE_OK_14" ]; then echo "Running tests against production (ok14) org" export CLIENT_ID=0oax3dcx0sak1KKb9696 export ISSUER=https://javascript-idx-sdk-new.okta.com export ISSUER_IDFIRST=https://javascript-idx-sdk-idfirst.okta.com - get_vault_secret_key devex/prod-js-idx-sdk-vars prod_client_secret_new CLIENT_SECRET - get_vault_secret_key devex/prod-js-idx-sdk-vars prod_idx_sdk_org_api_key_new OKTA_API_KEY - get_vault_secret_key devex/prod-js-idx-sdk-vars prod_idx_idfirst_sdk_org_api_key OKTA_API_KEY_IDFIRST + get_terminus_secret "/prod/idx" prod_client_secret_new CLIENT_SECRET + get_terminus_secret "/prod/idx" prod_idx_sdk_org_api_key_new OKTA_API_KEY + get_terminus_secret "/prod/idx" prod_idx_idfirst_sdk_org_api_key OKTA_API_KEY_IDFIRST else echo "Running tests against production (ok12) org" export ISSUER=https://javascript-idx-sdk.okta.com export CLIENT_ID=0oav2oxnlYjULp0Cy5d6 export SPA_CLIENT_ID=0oa17suj5x9khaVH75d7 - get_vault_secret_key devex/prod-js-idx-sdk-vars prod_client_secret CLIENT_SECRET - get_vault_secret_key devex/prod-js-idx-sdk-vars prod_idx_sdk_org_api_key OKTA_API_KEY + get_terminus_secret "/prod/idx" prod_client_secret CLIENT_SECRET + get_terminus_secret "/prod/idx" prod_idx_sdk_org_api_key OKTA_API_KEY fi fi finish_log_group $? @@ -112,6 +135,12 @@ setup_sample_tests () { run_sample_tests () { create_log_group "E2E Test Run" + + if [ -z "${PASSWORD}" ]; then + echo "No PASSWORD has been set! Exiting..." + exit ${TEST_FAILURE} + fi + # Run the tests if ! yarn test:samples; then echo "tests failed! Exiting..." diff --git a/scripts/setup.sh b/scripts/setup.sh index b05a8d166..0c9c57e42 100755 --- a/scripts/setup.sh +++ b/scripts/setup.sh @@ -47,6 +47,14 @@ else fi } + get_terminus_secret () { + # ensures the env var is set + if [ -z "$(echo "$3")" ]; then + echo "$3 is not defined. Exiting..." + exit 1 + fi + } + junit () { echo 'noop' } diff --git a/test/apps/app/src/testApp.ts b/test/apps/app/src/testApp.ts index e8301b7fb..c7f5470a7 100644 --- a/test/apps/app/src/testApp.ts +++ b/test/apps/app/src/testApp.ts @@ -674,6 +674,9 @@ class TestApp { scopes: this.config.defaultScopes ? [] : this.config.scopes, acrValues: this.config.acrValues, state: this.config.state, + extraParams: { + foo: 'bar' + } }, options); return this.oktaAuth.token.getWithRedirect(options) .catch(e => { diff --git a/test/spec/oidc/endpoints/token.ts b/test/spec/oidc/endpoints/token.ts index 5cb4596ac..770653ee6 100644 --- a/test/spec/oidc/endpoints/token.ts +++ b/test/spec/oidc/endpoints/token.ts @@ -40,6 +40,7 @@ describe('token endpoint', function() { var endpoint = '/oauth2/v1/token'; var codeVerifier = 'superfake'; var authorizationCode = 'notreal'; + var extraParams = { foo: 'bar' }; util.itMakesCorrectRequestResponse({ title: 'requests a token', @@ -55,7 +56,7 @@ describe('token endpoint', function() { data: { client_id: CLIENT_ID, grant_type: 'authorization_code', - redirect_uri: REDIRECT_URI + redirect_uri: REDIRECT_URI, }, headers: { 'Accept': 'application/json', @@ -80,7 +81,7 @@ describe('token endpoint', function() { clientId: CLIENT_ID, redirectUri: REDIRECT_URI, authorizationCode: authorizationCode, - codeVerifier: codeVerifier, + codeVerifier: codeVerifier }, { tokenUrl: ISSUER + endpoint }); @@ -156,6 +157,29 @@ describe('token endpoint', function() { }); + describe('postRefreshToken', () => { + var authClient; + + beforeEach(function() { + spyOn(OktaAuth.features, 'isPKCESupported').and.returnValue(true); + authClient = new OktaAuth({ + issuer: 'https://auth-js-test.okta.com' + }); + }); + + it('should append extra params as query params', async () => { + var httpRequest = jest.spyOn(mocked.http, 'httpRequest').mockImplementation(); + const refreshToken = tokens.standardRefreshTokenParsed; + await postRefreshToken(authClient, { extraParams }, refreshToken); + expect(httpRequest).toHaveBeenCalled(); + expect(httpRequest).toHaveBeenLastCalledWith(expect.any(OktaAuth), expect.objectContaining({ + url: 'https://auth-js-test.okta.com/oauth2/v1/token?foo=bar', + method: 'POST', + headers: {'Content-Type': 'application/x-www-form-urlencoded' } + })); + }); + }); + describe('dpop', () => { const ctx: any = {}; diff --git a/test/spec/oidc/exchangeCodeForTokens.ts b/test/spec/oidc/exchangeCodeForTokens.ts index d2e0ac86a..19d3cca55 100644 --- a/test/spec/oidc/exchangeCodeForTokens.ts +++ b/test/spec/oidc/exchangeCodeForTokens.ts @@ -52,7 +52,13 @@ describe('exchangeCodeForTokens', () => { (postToTokenEndpoint as jest.Mock).mockResolvedValue(oauthResponse); (handleOAuthResponse as jest.Mock).mockResolvedValue(tokenResponse); const acrValues = 'foo'; - const tokenParams = { ...testParams, acrValues }; + const tokenParams = { + ...testParams, + acrValues, + extraParams: { + foo: 'bar' + } + }; const urls = getOAuthUrls(sdk); await exchangeCodeForTokens(sdk, tokenParams); expect(postToTokenEndpoint).toHaveBeenCalledWith(sdk, testParams, urls); @@ -62,7 +68,8 @@ describe('exchangeCodeForTokens', () => { redirectUri: testParams.redirectUri, responseType: ['token', 'id_token'], scopes: ['openid', 'email'], - acrValues + acrValues, + extraParams: tokenParams.extraParams }, oauthResponse, urls); }); diff --git a/test/spec/oidc/parseFromUrl.ts b/test/spec/oidc/parseFromUrl.ts index 60dd3d341..65c76995f 100644 --- a/test/spec/oidc/parseFromUrl.ts +++ b/test/spec/oidc/parseFromUrl.ts @@ -51,7 +51,8 @@ describe('token.parseFromUrl', function() { oktaAuthArgs: { pkce: false, responseMode: 'query', - responseType: ['code'] + responseType: ['code'], + extraParams: { foo: 'bar' } }, searchMock: '?code=fake' + '&state=' + oauthUtil.mockedState, @@ -65,7 +66,8 @@ describe('token.parseFromUrl', function() { tokenUrl: 'https://auth-js-test.okta.com/oauth2/v1/token', authorizeUrl: 'https://auth-js-test.okta.com/oauth2/v1/authorize', userinfoUrl: 'https://auth-js-test.okta.com/oauth2/v1/userinfo' - } + }, + extraParams: { foo: 'bar' } }, expectedResp: { code: 'fake', diff --git a/test/spec/oidc/renewTokens.ts b/test/spec/oidc/renewTokens.ts index 985e51fec..5ae4fc5eb 100644 --- a/test/spec/oidc/renewTokens.ts +++ b/test/spec/oidc/renewTokens.ts @@ -293,6 +293,23 @@ describe('token.renewTokens', function() { }); }); + it('extraParams', async () => { + const { sdk, accessToken, idToken, getWihoutPromptResponse, withoutPromptSpy } = testContext; + accessToken.extraParams = { foo: 'bar' }; + const tokens = { fake: true }; + getWihoutPromptResponse.tokens = tokens; + const res = await renewTokens(sdk, {}); + expect(res).toBe(tokens); + expect(withoutPromptSpy).toHaveBeenCalledWith(sdk, { + authorizeUrl: accessToken.authorizeUrl, + issuer: idToken.issuer, + responseType: ['token', 'id_token'], + scopes: accessToken.scopes, + userinfoUrl: accessToken.userinfoUrl, + extraParams: { foo: 'bar' } + }); + }); + }); }); diff --git a/test/spec/oidc/util/handleOAuthResponse.ts b/test/spec/oidc/util/handleOAuthResponse.ts index 6d6837796..c41774b8d 100644 --- a/test/spec/oidc/util/handleOAuthResponse.ts +++ b/test/spec/oidc/util/handleOAuthResponse.ts @@ -59,16 +59,23 @@ describe('handleOAuthResponse', () => { expect(res.tokens.refreshToken!.refreshToken).toBe('foo'); }); it('returns all tokens from the response', async () => { - const tokenParams: TokenParams = { responseType: ['token', 'id_token', 'refresh_token'], dpop: true }; + const tokenParams: TokenParams = { + responseType: ['token', 'id_token', 'refresh_token'], + dpop: true, + extraParams: { foo: 'bar' } + }; const oauthRes = { id_token: 'foo', access_token: 'blar', refresh_token: 'bloo', token_type: 'DPoP' }; const res = await handleOAuthResponse(sdk, tokenParams, oauthRes, undefined as unknown as CustomUrls); expect(res.tokens).toBeTruthy(); expect(res.tokens.accessToken).toBeTruthy(); expect(res.tokens.accessToken!.accessToken).toBe('blar'); + expect(res.tokens.accessToken!.extraParams).toEqual({ foo: 'bar' }); expect(res.tokens.idToken).toBeTruthy(); expect(res.tokens.idToken!.idToken).toBe('foo'); + expect(res.tokens.idToken!.extraParams).toEqual({ foo: 'bar' }); expect(res.tokens.refreshToken).toBeTruthy(); expect(res.tokens.refreshToken!.refreshToken).toBe('bloo'); + expect(res.tokens.refreshToken!.extraParams).toEqual({ foo: 'bar' }); }); it('prefers "scope" value from endpoint response over method parameter', async () => { const tokenParams: TokenParams = { responseType: ['token', 'id_token', 'refresh_token'], scopes: ['profile'] }; @@ -78,7 +85,6 @@ describe('handleOAuthResponse', () => { expect(res.tokens.idToken!.scopes).toEqual(['openid', 'offline_access']); expect(res.tokens.refreshToken!.scopes).toEqual(['openid', 'offline_access']); }); - describe('errors', () => { it('does not throw if response contains only "error" without "error_description"', async () => { let errorThrown = false;