Skip to content

Commit

Permalink
Added support for AuthZeroClientConfig.audience (#3869)
Browse files Browse the repository at this point in the history
* Added support for `AuthZeroClientConfig.audience`

* Simplify - audience defaulted at client creation time
  • Loading branch information
amcclain authored Dec 24, 2024
1 parent e3c2445 commit 77d6ea0
Show file tree
Hide file tree
Showing 2 changed files with 40 additions and 21 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@
* Support for "global" views.
* New `SessionStorageService` and associated persistence provider provides support for saving
tab local data across reloads.
* Added support for `AuthZeroClientConfig.audience` to support improved configuration of Auth0 OAuth
clients requesting access tokens, covering cases when third-party cookies are blocked.

### ⚙️ Technical

Expand Down
59 changes: 38 additions & 21 deletions security/authzero/AuthZeroClient.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@
*
* Copyright © 2024 Extremely Heavy Industries Inc.
*/
import {Auth0Client, Auth0ClientOptions} from '@auth0/auth0-spa-js';
import type {Auth0ClientOptions} from '@auth0/auth0-spa-js';
import {Auth0Client} from '@auth0/auth0-spa-js';
import {XH} from '@xh/hoist/core';
import {never, wait} from '@xh/hoist/promise';
import {Token, TokenMap} from '@xh/hoist/security/Token';
Expand All @@ -14,9 +15,23 @@ import {flatMap, union} from 'lodash';
import {BaseOAuthClient, BaseOAuthClientConfig} from '../BaseOAuthClient';

export interface AuthZeroClientConfig extends BaseOAuthClientConfig<AuthZeroTokenSpec> {
/** Domain of your app registered with Auth0 */
/** Domain of your app registered with Auth0. */
domain: string;

/**
* Audience to pass to interactive login and ID token requests.
*
* If you are also requesting an *access* token for a single audience, pass that value here to
* ensure that the initial login/token request returns a ready-to-use access token (and refresh
* token) with a single request to the Auth0 API, instead of requiring two.
*
* This also avoids issues with browsers that block third party cookies when running on
* localhost or with an Auth0 domain that does not match the app's own domain. In those cases,
* Auth0 must use refresh tokens to obtain access tokens, and a single audience allows that
* exchange to work without extra user interaction that this class does not currently support.
*/
audience?: string;

/**
* Additional options for the Auth0Client ctor. Will be deep merged with defaults, with options
* supplied here taking precedence. Use with care, as overriding defaults may have unintended
Expand All @@ -31,9 +46,7 @@ export interface AuthZeroTokenSpec {

/**
* Audience (i.e. API) identifier for AccessToken. Must be registered with Auth0.
*
* Note that this is required to ensure that issued token is a JWT and not
* an opaque string.
* Note that this is required to ensure that issued token is a JWT and not an opaque string.
*/
audience: string;
}
Expand Down Expand Up @@ -95,7 +108,9 @@ export class AuthZeroClient extends BaseOAuthClient<AuthZeroClientConfig, AuthZe

protected override async doLoginPopupAsync(): Promise<void> {
try {
await this.client.loginWithPopup({authorizationParams: {scope: this.loginScope}});
await this.client.loginWithPopup({
authorizationParams: {scope: this.loginScope}
});
await this.noteUserAuthenticatedAsync();
} catch (e) {
const msg = e.message?.toLowerCase();
Expand Down Expand Up @@ -167,25 +182,27 @@ export class AuthZeroClient extends BaseOAuthClient<AuthZeroClientConfig, AuthZe
// Private implementation
//------------------------
private createClient(): Auth0Client {
const {clientId, domain, authZeroClientOptions} = this.config;
const {clientId, domain, audience, authZeroClientOptions} = this.config;
throwIf(!domain, 'Missing Auth0 "domain". Please review your config.');

return new Auth0Client(
mergeDeep(
{
clientId,
domain,
useRefreshTokens: true,
useRefreshTokensFallback: true,
authorizationParams: {
scope: this.loginScope,
redirect_uri: this.redirectUrl
},
cacheLocation: 'localstorage'
const mergedConfig = mergeDeep(
{
clientId,
domain,
useRefreshTokens: true,
useRefreshTokensFallback: true,
authorizationParams: {
scope: this.loginScope,
redirect_uri: this.redirectUrl,
audience
},
authZeroClientOptions
)
cacheLocation: 'localstorage'
},
authZeroClientOptions
);

this.logDebug(`Creating Auth0Client with merged config`, mergedConfig);
return new Auth0Client(mergedConfig);
}

private get loginScope(): string {
Expand Down

0 comments on commit 77d6ea0

Please sign in to comment.