Skip to content
Merged
Show file tree
Hide file tree
Changes from 3 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
72 changes: 62 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](#connect-accounts)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can call this 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,68 @@ 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

Link multiple identity providers to a single Auth0 user profile, allowing users to authenticate with any of their connected accounts.

**Note:** User must be logged in first.

### Configuration

Enable `useRefreshTokens` and `useMrrt` in your Auth0 configuration:

```ts
AuthModule.forRoot({
domain: 'YOUR_AUTH0_DOMAIN',
clientId: 'YOUR_AUTH0_CLIENT_ID',
useRefreshTokens: true,
useMrrt: true,
});
```

### Usage

Use `connectAccountWithRedirect` to link an additional account:

```ts
import { Component } from '@angular/core';
import { AuthService } from '@auth0/auth0-angular';

@Component({
selector: 'app-connect-account',
template: `<button (click)="connectAccount()">Connect Google</button>`,
})
export class ConnectAccountComponent {
constructor(private auth: AuthService) {}

connectAccount(): void {
this.auth
.connectAccountWithRedirect({
connection: 'google-oauth2',
scopes: ['openid', 'profile', 'email'],
appState: { returnTo: '/profile' },
})
.subscribe();
}
}
```

After redirect, you can access connection details via the `appState$` observable:

```ts
this.auth.appState$.subscribe((appState) => {
if (appState?.connectedAccount) {
console.log(`Connected to ${appState.connectedAccount.connection}`);
}
});
```
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