Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
86 changes: 76 additions & 10 deletions EXAMPLES.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
- [Handling errors](#handling-errors)
- [Organizations](#organizations)
- [Standalone Components and a more functional approach](#standalone-components-and-a-more-functional-approach)
- [Connect Accounts for using Token Vault](#connect-accounts-for-using-token-vault)

## Add login to your application

Expand Down Expand Up @@ -157,7 +158,7 @@ import { AuthModule } from '@auth0/auth0-angular';
clientId: 'YOUR_AUTH0_CLIENT_ID',
authorizationParams: {
audience: 'YOUR_AUTH0_API_IDENTIFIER',
}
},
}),
],
// ...
Expand Down Expand Up @@ -278,7 +279,7 @@ AuthModule.forRoot({
authorizationParams: {
audience: 'http://my-api/',
scope: 'write:orders',
}
},
},
},
],
Expand Down Expand Up @@ -381,6 +382,7 @@ export class AppComponent {
```

## Standalone components and a more functional approach

As of Angular 15, the Angular team is putting standalone components, as well as a more functional approach, in favor of the traditional use of NgModules and class-based approach.

There are a couple of difference with how you would traditionally implement our SDK:
Expand All @@ -398,18 +400,82 @@ const routes: Routes = [
path: 'profile',
component: ProfileComponent,
canActivate: [authGuardFn],
}
},
];

bootstrapApplication(AppComponent, {
providers: [
provideRouter(routes),
provideAuth0(/* Auth Config Goes Here */),
provideHttpClient(
withInterceptors([authHttpInterceptorFn])
)
]
providers: [provideRouter(routes), provideAuth0(/* Auth Config Goes Here */), provideHttpClient(withInterceptors([authHttpInterceptorFn]))],
});
```

Note that `provideAuth0` should **never** be provided to components, but only at the root level of your application.

## Connect Accounts for using Token Vault

The Connect Accounts feature uses the Auth0 My Account API to allow users to link multiple third party accounts to a single Auth0 user profile.

When using Connected Accounts, Auth0 acquires tokens from upstream Identity Providers (like Google) and stores them in a secure [Token Vault](https://auth0.com/docs/secure/tokens/token-vault). These tokens can then be used to access third-party APIs (like Google Calendar) on behalf of the user.

The tokens in the Token Vault are then accessible to [Resource Servers](https://auth0.com/docs/get-started/apis) (APIs) configured in Auth0. The SPA application can then issue requests to the API, which can retrieve the tokens from the Token Vault and use them to access the third-party APIs.

This is particularly useful for applications that require access to different resources on behalf of a user, like AI Agents.

### Configure the SDK

The SDK must be configured with an audience (an API Identifier) - this will be the resource server that uses the tokens from the Token Vault.

The SDK must also be configured to use refresh tokens and MRRT ([Multiple Resource Refresh Tokens](https://auth0.com/docs/secure/tokens/refresh-tokens/multi-resource-refresh-token)) since we will use the refresh token grant to get Access Tokens for the My Account API in addition to the API we are calling.

The My Account API requires DPoP tokens, so we also need to enable DPoP.

```ts
AuthModule.forRoot({
domain: '<AUTH0_DOMAIN>',
clientId: '<AUTH0_CLIENT_ID>',
useRefreshTokens: true,
useMrrt: true,
useDpop: true,
authorizationParams: {
redirect_uri: '<MY_CALLBACK_URL>',
},
});
```

### Login to the application

Use the login methods to authenticate to the application and get a refresh and access token for the API.

```ts
// Login specifying any scopes for the Auth0 API
this.auth
.loginWithRedirect({
authorizationParams: {
audience: '<AUTH0_API_IDENTIFIER>',
scope: 'openid profile email read:calendar',
},
})
.subscribe();
```

### Connect to a third party account

Use the `connectAccountWithRedirect` method to redirect the user to the third party Identity Provider to connect their account.

```ts
// Start the connect flow by redirecting to the third party API's login, defined as an Auth0 connection
this.auth
.connectAccountWithRedirect({
connection: '<CONNECTION eg, google-apps-connection>',
scopes: ['<SCOPE eg https://www.googleapis.com/auth/calendar.acls.readonly>'],
authorizationParams: {
// additional authorization params to forward to the authorization server
},
})
.subscribe();
```

You can now call the API with your access token and the API can use [Access Token Exchange with Token Vault](https://auth0.com/docs/secure/tokens/token-vault/access-token-exchange-with-token-vault) to get tokens from the Token Vault to access third party APIs on behalf of the user.

> **Important**
>
> You must enable Offline Access from the Connection Permissions settings to be able to use the connection with Connected Accounts.
26 changes: 26 additions & 0 deletions projects/auth0-angular/src/lib/auth.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -62,6 +62,7 @@ describe('AuthService', () => {
appState: undefined,
} as any);
jest.spyOn(auth0Client, 'loginWithRedirect').mockResolvedValue();
jest.spyOn(auth0Client, 'connectAccountWithRedirect').mockResolvedValue();
jest.spyOn(auth0Client, 'loginWithPopup').mockResolvedValue();
jest.spyOn(auth0Client, 'checkSession').mockResolvedValue();
jest.spyOn(auth0Client, 'isAuthenticated').mockResolvedValue(false);
Expand Down Expand Up @@ -669,6 +670,31 @@ describe('AuthService', () => {
expect(auth0Client.loginWithRedirect).toHaveBeenCalledWith(options);
});

it('should call `connectAccountWithRedirect`', async () => {
const service = createService();
const options = { connection: 'google-oauth2' };
await service.connectAccountWithRedirect(options).toPromise();
expect(auth0Client.connectAccountWithRedirect).toHaveBeenCalledWith(
options
);
});

it('should call `connectAccountWithRedirect` and pass all options', async () => {
const options = {
connection: 'github',
scopes: ['openid', 'profile', 'email'],
authorization_params: { audience: 'https://api.github.com' },
redirectUri: 'http://localhost:3000/callback',
appState: { returnTo: '/profile' },
};

const service = createService();
await service.connectAccountWithRedirect(options).toPromise();
expect(auth0Client.connectAccountWithRedirect).toHaveBeenCalledWith(
options
);
});

it('should call `loginWithPopup`', (done) => {
const service = createService();
loaded(service).subscribe(() => {
Expand Down
27 changes: 27 additions & 0 deletions projects/auth0-angular/src/lib/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import {
RedirectLoginResult,
GetTokenSilentlyVerboseResponse,
ConnectAccountRedirectResult,
RedirectConnectAccountOptions,
CustomFetchMinimalOutput,
Fetcher,
FetcherConfig,
Expand Down Expand Up @@ -142,6 +143,32 @@ export class AuthService<TAppState extends AppState = AppState>
return from(this.auth0Client.loginWithRedirect(options));
}

/**
* ```js
* connectAccountWithRedirect({
* connection: 'google-oauth2',
* scopes: ['openid', 'profile', 'email', 'https://www.googleapis.com/auth/drive.readonly'],
* authorization_params: {
* // additional authorization params to forward to the authorization server
* }
* });
* ```
*
* Redirects to the `/connect` URL using the parameters
* provided as arguments. This then redirects to the connection's login page
* where the user can authenticate and authorize the account to be connected.
*
* If connecting the account is successful, `handleRedirectCallback` will be called
* with the details of the connected account.
*
* @param options The connect account options
*/
connectAccountWithRedirect(
options: RedirectConnectAccountOptions<TAppState>
): Observable<void> {
return from(this.auth0Client.connectAccountWithRedirect(options));
}

/**
* ```js
* await loginWithPopup(options);
Expand Down
1 change: 1 addition & 0 deletions projects/auth0-angular/src/public-api.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ export {
PopupConfigOptions,
GetTokenWithPopupOptions,
GetTokenSilentlyOptions,
RedirectConnectAccountOptions,
ICache,
Cacheable,
LocalStorageCache,
Expand Down