diff --git a/__tests__/Auth0Client/getTokenSilently.test.ts b/__tests__/Auth0Client/getTokenSilently.test.ts index 0932cc37c..5eae71e7a 100644 --- a/__tests__/Auth0Client/getTokenSilently.test.ts +++ b/__tests__/Auth0Client/getTokenSilently.test.ts @@ -38,6 +38,7 @@ import { } from '../constants'; import { releaseLockSpy } from '../../__mocks__/browser-tabs-lock'; +import { DEFAULT_AUTH0_CLIENT } from '../../src/constants'; jest.mock('unfetch'); jest.mock('es-cookie'); @@ -179,13 +180,19 @@ describe('Auth0Client', () => { await getTokenSilently(auth0); - assertPost('https://auth0_domain/oauth/token', { - redirect_uri: TEST_REDIRECT_URI, - client_id: TEST_CLIENT_ID, - code_verifier: TEST_CODE_VERIFIER, - grant_type: 'authorization_code', - code: TEST_CODE - }); + assertPost( + 'https://auth0_domain/oauth/token', + { + redirect_uri: TEST_REDIRECT_URI, + client_id: TEST_CLIENT_ID, + code_verifier: TEST_CODE_VERIFIER, + grant_type: 'authorization_code', + code: TEST_CODE + }, + { + 'Auth0-Client': btoa(JSON.stringify(DEFAULT_AUTH0_CLIENT)) + } + ); }); it('calls the token endpoint with the correct params when using refresh tokens', async () => { @@ -201,12 +208,18 @@ describe('Auth0Client', () => { ignoreCache: true }); - assertPost('https://auth0_domain/oauth/token', { - redirect_uri: TEST_REDIRECT_URI, - client_id: TEST_CLIENT_ID, - grant_type: 'refresh_token', - refresh_token: TEST_REFRESH_TOKEN - }); + assertPost( + 'https://auth0_domain/oauth/token', + { + redirect_uri: TEST_REDIRECT_URI, + client_id: TEST_CLIENT_ID, + grant_type: 'refresh_token', + refresh_token: TEST_REFRESH_TOKEN + }, + { + 'Auth0-Client': btoa(JSON.stringify(DEFAULT_AUTH0_CLIENT)) + } + ); }); it('calls the token endpoint with the correct params when passing redirect uri and using refresh tokens', async () => { @@ -224,12 +237,18 @@ describe('Auth0Client', () => { ignoreCache: true }); - assertPost('https://auth0_domain/oauth/token', { - redirect_uri, - client_id: TEST_CLIENT_ID, - grant_type: 'refresh_token', - refresh_token: TEST_REFRESH_TOKEN - }); + assertPost( + 'https://auth0_domain/oauth/token', + { + redirect_uri, + client_id: TEST_CLIENT_ID, + grant_type: 'refresh_token', + refresh_token: TEST_REFRESH_TOKEN + }, + { + 'Auth0-Client': btoa(JSON.stringify(DEFAULT_AUTH0_CLIENT)) + } + ); }); it('calls the token endpoint with the correct params when not providing any redirect uri and using refresh tokens', async () => { @@ -247,12 +266,18 @@ describe('Auth0Client', () => { ignoreCache: true }); - assertPost('https://auth0_domain/oauth/token', { - redirect_uri: 'http://localhost', - client_id: TEST_CLIENT_ID, - grant_type: 'refresh_token', - refresh_token: TEST_REFRESH_TOKEN - }); + assertPost( + 'https://auth0_domain/oauth/token', + { + redirect_uri: 'http://localhost', + client_id: TEST_CLIENT_ID, + grant_type: 'refresh_token', + refresh_token: TEST_REFRESH_TOKEN + }, + { + 'Auth0-Client': btoa(JSON.stringify(DEFAULT_AUTH0_CLIENT)) + } + ); }); it('calls the token endpoint with the correct timeout when using refresh tokens', async () => { @@ -351,6 +376,7 @@ describe('Auth0Client', () => { jest.spyOn(utils, 'runIframe').mockResolvedValue({ access_token: TEST_ACCESS_TOKEN, + code: TEST_CODE, state: TEST_STATE }); @@ -359,6 +385,19 @@ describe('Auth0Client', () => { await getTokenSilently(auth0); expectToHaveBeenCalledWithAuth0ClientParam(utils.runIframe, auth0Client); + assertPost( + 'https://auth0_domain/oauth/token', + { + redirect_uri: TEST_REDIRECT_URI, + client_id: TEST_CLIENT_ID, + code_verifier: TEST_CODE_VERIFIER, + grant_type: 'authorization_code', + code: TEST_CODE + }, + { + 'Auth0-Client': btoa(JSON.stringify(auth0Client)) + } + ); }); it('refreshes the token when cache available without access token', async () => { @@ -458,6 +497,9 @@ describe('Auth0Client', () => { redirect_uri: TEST_REDIRECT_URI, refresh_token: TEST_REFRESH_TOKEN }, + { + 'Auth0-Client': btoa(JSON.stringify(DEFAULT_AUTH0_CLIENT)) + }, 1 ); @@ -474,13 +516,19 @@ describe('Auth0Client', () => { await loginWithRedirect(auth0); - assertPost('https://auth0_domain/oauth/token', { - redirect_uri: TEST_REDIRECT_URI, - client_id: TEST_CLIENT_ID, - code_verifier: TEST_CODE_VERIFIER, - grant_type: 'authorization_code', - code: TEST_CODE - }); + assertPost( + 'https://auth0_domain/oauth/token', + { + redirect_uri: TEST_REDIRECT_URI, + client_id: TEST_CLIENT_ID, + code_verifier: TEST_CODE_VERIFIER, + grant_type: 'authorization_code', + code: TEST_CODE + }, + { + 'Auth0-Client': btoa(JSON.stringify(DEFAULT_AUTH0_CLIENT)) + } + ); mockFetch.mockResolvedValueOnce( fetchResponse(true, { @@ -501,6 +549,9 @@ describe('Auth0Client', () => { redirect_uri: TEST_REDIRECT_URI, refresh_token: TEST_REFRESH_TOKEN }, + { + 'Auth0-Client': btoa(JSON.stringify(DEFAULT_AUTH0_CLIENT)) + }, 1 ); @@ -519,13 +570,19 @@ describe('Auth0Client', () => { await loginWithRedirect(auth0); - assertPost('https://auth0_domain/oauth/token', { - redirect_uri: TEST_REDIRECT_URI, - client_id: TEST_CLIENT_ID, - code_verifier: TEST_CODE_VERIFIER, - grant_type: 'authorization_code', - code: TEST_CODE - }); + assertPost( + 'https://auth0_domain/oauth/token', + { + redirect_uri: TEST_REDIRECT_URI, + client_id: TEST_CLIENT_ID, + code_verifier: TEST_CODE_VERIFIER, + grant_type: 'authorization_code', + code: TEST_CODE + }, + { + 'Auth0-Client': btoa(JSON.stringify(DEFAULT_AUTH0_CLIENT)) + } + ); const access_token = await getTokenSilently(auth0, { ignoreCache: true }); @@ -537,6 +594,9 @@ describe('Auth0Client', () => { redirect_uri: TEST_REDIRECT_URI, refresh_token: TEST_REFRESH_TOKEN }, + { + 'Auth0-Client': btoa(JSON.stringify(DEFAULT_AUTH0_CLIENT)) + }, 1 ); diff --git a/__tests__/Auth0Client/helpers.ts b/__tests__/Auth0Client/helpers.ts index fe824e531..8446bac51 100644 --- a/__tests__/Auth0Client/helpers.ts +++ b/__tests__/Auth0Client/helpers.ts @@ -26,10 +26,15 @@ const authorizationResponse: AuthenticationResult = { }; export const assertPostFn = mockFetch => { - return (url, body, callNum = 0) => { + return (url, body, headers = null, callNum = 0) => { const [actualUrl, opts] = mockFetch.mock.calls[callNum]; expect(url).toEqual(actualUrl); expect(body).toEqual(JSON.parse(opts.body)); + if (headers) { + Object.keys(headers).forEach(header => + expect(headers[header]).toEqual(opts.headers[header]) + ); + } }; }; diff --git a/__tests__/Auth0Client/loginWithPopup.test.ts b/__tests__/Auth0Client/loginWithPopup.test.ts index a3a57352a..55a548a6b 100644 --- a/__tests__/Auth0Client/loginWithPopup.test.ts +++ b/__tests__/Auth0Client/loginWithPopup.test.ts @@ -29,7 +29,11 @@ import { TEST_STATE } from '../constants'; -import { DEFAULT_POPUP_CONFIG_OPTIONS } from '../../src/constants'; +import { + DEFAULT_AUTH0_CLIENT, + DEFAULT_POPUP_CONFIG_OPTIONS +} from '../../src/constants'; +import version from '../../src/version'; jest.mock('unfetch'); jest.mock('es-cookie'); @@ -236,13 +240,19 @@ describe('Auth0Client', () => { await loginWithPopup(auth0); expect(mockWindow.open).toHaveBeenCalled(); - assertPost('https://auth0_domain/oauth/token', { - redirect_uri: TEST_REDIRECT_URI, - client_id: TEST_CLIENT_ID, - code_verifier: TEST_CODE_VERIFIER, - grant_type: 'authorization_code', - code: 'my_code' - }); + assertPost( + 'https://auth0_domain/oauth/token', + { + redirect_uri: TEST_REDIRECT_URI, + client_id: TEST_CLIENT_ID, + code_verifier: TEST_CODE_VERIFIER, + grant_type: 'authorization_code', + code: 'my_code' + }, + { + 'Auth0-Client': btoa(JSON.stringify(DEFAULT_AUTH0_CLIENT)) + } + ); }); it('uses default config', async () => { @@ -313,13 +323,19 @@ describe('Auth0Client', () => { await loginWithPopup(auth0, {}, { popup }); expect(mockWindow.open).not.toHaveBeenCalled(); - assertPost('https://auth0_domain/oauth/token', { - redirect_uri: TEST_REDIRECT_URI, - client_id: TEST_CLIENT_ID, - code_verifier: TEST_CODE_VERIFIER, - grant_type: 'authorization_code', - code: 'my_code' - }); + assertPost( + 'https://auth0_domain/oauth/token', + { + redirect_uri: TEST_REDIRECT_URI, + client_id: TEST_CLIENT_ID, + code_verifier: TEST_CODE_VERIFIER, + grant_type: 'authorization_code', + code: 'my_code' + }, + { + 'Auth0-Client': btoa(JSON.stringify(DEFAULT_AUTH0_CLIENT)) + } + ); }); it('opens popup with custom auth0Client', async () => { diff --git a/__tests__/Auth0Client/loginWithRedirect.test.ts b/__tests__/Auth0Client/loginWithRedirect.test.ts index f948d551c..43792eaa0 100644 --- a/__tests__/Auth0Client/loginWithRedirect.test.ts +++ b/__tests__/Auth0Client/loginWithRedirect.test.ts @@ -33,6 +33,7 @@ import { TEST_SCOPES, TEST_STATE } from '../constants'; +import version from '../../src/version'; jest.mock('unfetch'); jest.mock('es-cookie'); @@ -115,13 +116,24 @@ describe('Auth0Client', () => { code_challenge_method: 'S256' }); - assertPost('https://auth0_domain/oauth/token', { - redirect_uri: TEST_REDIRECT_URI, - client_id: TEST_CLIENT_ID, - code_verifier: TEST_CODE_VERIFIER, - grant_type: 'authorization_code', - code: TEST_CODE - }); + assertPost( + 'https://auth0_domain/oauth/token', + { + redirect_uri: TEST_REDIRECT_URI, + client_id: TEST_CLIENT_ID, + code_verifier: TEST_CODE_VERIFIER, + grant_type: 'authorization_code', + code: TEST_CODE + }, + { + 'Auth0-Client': btoa( + JSON.stringify({ + name: 'auth0-spa-js', + version: version + }) + ) + } + ); }); it('should log the user in using different default scope', async () => { diff --git a/__tests__/utils.test.ts b/__tests__/utils.test.ts index 3730ac19a..0ad8a5efa 100644 --- a/__tests__/utils.test.ts +++ b/__tests__/utils.test.ts @@ -24,6 +24,7 @@ import { MessageChannel } from 'worker_threads'; import unfetch from 'unfetch'; // @ts-ignore import Worker from '../src/token.worker'; +import version from '../src/version'; jest.mock('../src/token.worker'); jest.mock('unfetch'); @@ -274,19 +275,27 @@ describe('utils', () => { res({ ok: true, json: () => new Promise(ress => ress(true)) }) ) ); + const auth0Client = { + name: 'auth0-spa-js', + version: version + }; await oauthToken({ grant_type: 'authorization_code', baseUrl: 'https://test.com', client_id: 'client_idIn', code: 'codeIn', - code_verifier: 'code_verifierIn' + code_verifier: 'code_verifierIn', + auth0Client }); expect(mockUnfetch).toBeCalledWith('https://test.com/oauth/token', { body: '{"redirect_uri":"http://localhost","grant_type":"authorization_code","client_id":"client_idIn","code":"codeIn","code_verifier":"code_verifierIn"}', - headers: { 'Content-type': 'application/json' }, + headers: { + 'Content-type': 'application/json', + 'Auth0-Client': btoa(JSON.stringify(auth0Client)) + }, method: 'POST', signal: abortController.signal }); @@ -309,6 +318,10 @@ describe('utils', () => { code: 'codeIn', code_verifier: 'code_verifierIn' }; + const auth0Client = { + name: 'auth0-spa-js', + version: version + }; await oauthToken( { @@ -318,14 +331,18 @@ describe('utils', () => { code: 'codeIn', code_verifier: 'code_verifierIn', audience: '__test_audience__', - scope: '__test_scope__' + scope: '__test_scope__', + auth0Client }, worker ); expect(mockUnfetch).toBeCalledWith('https://test.com/oauth/token', { body: JSON.stringify(body), - headers: { 'Content-type': 'application/json' }, + headers: { + 'Content-type': 'application/json', + 'Auth0-Client': btoa(JSON.stringify(auth0Client)) + }, method: 'POST', signal: abortController.signal }); @@ -337,7 +354,8 @@ describe('utils', () => { audience: '__test_audience__', scope: '__test_scope__', headers: { - 'Content-type': 'application/json' + 'Content-type': 'application/json', + 'Auth0-Client': btoa(JSON.stringify(auth0Client)) }, method: 'POST', timeout: 10000, diff --git a/src/Auth0Client.ts b/src/Auth0Client.ts index 4ecc292ed..81260717d 100644 --- a/src/Auth0Client.ts +++ b/src/Auth0Client.ts @@ -32,11 +32,10 @@ import { MISSING_REFRESH_TOKEN_ERROR_MESSAGE, DEFAULT_SCOPE, RECOVERABLE_ERRORS, - DEFAULT_SESSION_CHECK_EXPIRY_DAYS + DEFAULT_SESSION_CHECK_EXPIRY_DAYS, + DEFAULT_AUTH0_CLIENT } from './constants'; -import version from './version'; - import { Auth0ClientOptions, BaseLoginOptions, @@ -200,14 +199,7 @@ export default class Auth0Client { private _url(path) { const auth0Client = encodeURIComponent( - btoa( - JSON.stringify( - this.options.auth0Client || { - name: 'auth0-spa-js', - version: version - } - ) - ) + btoa(JSON.stringify(this.options.auth0Client || DEFAULT_AUTH0_CLIENT)) ); return `${this.domainUrl}${path}&auth0Client=${auth0Client}`; } @@ -381,7 +373,8 @@ export default class Auth0Client { code_verifier, code: codeResult.code, grant_type: 'authorization_code', - redirect_uri: params.redirect_uri + redirect_uri: params.redirect_uri, + auth0Client: this.options.auth0Client } as OAuthTokenOptions, this.worker ); @@ -517,7 +510,8 @@ export default class Auth0Client { client_id: this.options.client_id, code_verifier: transaction.code_verifier, grant_type: 'authorization_code', - code + code, + auth0Client: this.options.auth0Client } as OAuthTokenOptions; // some old versions of the SDK might not have added redirect_uri to the @@ -830,7 +824,8 @@ export default class Auth0Client { code_verifier, code: codeResult.code, grant_type: 'authorization_code', - redirect_uri: params.redirect_uri + redirect_uri: params.redirect_uri, + auth0Client: this.options.auth0Client } as OAuthTokenOptions, this.worker ); @@ -899,7 +894,8 @@ export default class Auth0Client { grant_type: 'refresh_token', refresh_token: cache && cache.refresh_token, redirect_uri, - ...(timeout && { timeout }) + ...(timeout && { timeout }), + auth0Client: this.options.auth0Client } as RefreshTokenOptions, this.worker ); diff --git a/src/constants.ts b/src/constants.ts index 30b995226..63e35b67f 100644 --- a/src/constants.ts +++ b/src/constants.ts @@ -1,4 +1,5 @@ import { PopupConfigOptions } from './global'; +import version from './version'; /** * @ignore @@ -58,3 +59,11 @@ export const RECOVERABLE_ERRORS = [ * @ignore */ export const DEFAULT_SESSION_CHECK_EXPIRY_DAYS = 1; + +/** + * @ignore + */ +export const DEFAULT_AUTH0_CLIENT = { + name: 'auth0-spa-js', + version: version +}; diff --git a/src/global.ts b/src/global.ts index 769d6dc63..b05ff5134 100644 --- a/src/global.ts +++ b/src/global.ts @@ -391,6 +391,7 @@ export interface TokenEndpointOptions { client_id: string; grant_type: string; timeout?: number; + auth0Client: any; [key: string]: any; } diff --git a/src/utils.ts b/src/utils.ts index 6910b5f2a..dc0e193e2 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -10,7 +10,8 @@ import { DEFAULT_AUTHORIZE_TIMEOUT_IN_SECONDS, DEFAULT_SILENT_TOKEN_RETRY_COUNT, DEFAULT_FETCH_TIMEOUT_MS, - CLEANUP_IFRAME_TIMEOUT_IN_SECONDS + CLEANUP_IFRAME_TIMEOUT_IN_SECONDS, + DEFAULT_AUTH0_CLIENT } from './constants'; import { PopupTimeoutError, TimeoutError, GenericError } from './errors'; @@ -358,7 +359,14 @@ const getJSON = async ( }; export const oauthToken = async ( - { baseUrl, timeout, audience, scope, ...options }: TokenEndpointOptions, + { + baseUrl, + timeout, + audience, + scope, + auth0Client, + ...options + }: TokenEndpointOptions, worker: Worker ) => await getJSON( @@ -373,7 +381,10 @@ export const oauthToken = async ( ...options }), headers: { - 'Content-type': 'application/json' + 'Content-type': 'application/json', + 'Auth0-Client': btoa( + JSON.stringify(auth0Client || DEFAULT_AUTH0_CLIENT) + ) } }, worker