Skip to content

Commit f769c9c

Browse files
docs: Add comprehensive DPoP examples to EXAMPLES.md
- Add DPoP section to table of contents - Document DPoP setup with useDpop configuration - Add createFetcher() examples (recommended approach) - Add multiple API endpoints example with separate fetchers - Add advanced manual DPoP management example - Add error handling with UseDpopNonceError - Add standalone components DPoP configuration - Include code examples for all DPoP methods: getDpopNonce, setDpopNonce, generateDpopProof, createFetcher
1 parent d46dfc4 commit f769c9c

File tree

1 file changed

+264
-10
lines changed

1 file changed

+264
-10
lines changed

EXAMPLES.md

Lines changed: 264 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
- [Call an API](#call-an-api)
99
- [Handling errors](#handling-errors)
1010
- [Organizations](#organizations)
11+
- [DPoP (Demonstrating Proof-of-Possession)](#dpop-demonstrating-proof-of-possession)
1112
- [Standalone Components and a more functional approach](#standalone-components-and-a-more-functional-approach)
1213

1314
## Add login to your application
@@ -157,7 +158,7 @@ import { AuthModule } from '@auth0/auth0-angular';
157158
clientId: 'YOUR_AUTH0_CLIENT_ID',
158159
authorizationParams: {
159160
audience: 'YOUR_AUTH0_API_IDENTIFIER',
160-
}
161+
},
161162
}),
162163
],
163164
// ...
@@ -278,7 +279,7 @@ AuthModule.forRoot({
278279
authorizationParams: {
279280
audience: 'http://my-api/',
280281
scope: 'write:orders',
281-
}
282+
},
282283
},
283284
},
284285
],
@@ -380,7 +381,266 @@ export class AppComponent {
380381
}
381382
```
382383
384+
## DPoP (Demonstrating Proof-of-Possession)
385+
386+
[DPoP](https://datatracker.ietf.org/doc/html/rfc9449) is a security mechanism that cryptographically binds access tokens to clients, providing protection against:
387+
388+
- **Token Theft** - Stolen tokens are cryptographically bound and unusable by attackers
389+
- **Replay Attacks** - Tokens are tied to specific HTTP requests
390+
- **Token Exfiltration** - Tokens require the client's private key to use
391+
392+
### Enable DPoP
393+
394+
To enable DPoP support, set `useDpop: true` in your Auth0 configuration:
395+
396+
```ts
397+
import { NgModule } from '@angular/core';
398+
import { AuthModule } from '@auth0/auth0-angular';
399+
400+
@NgModule({
401+
imports: [
402+
AuthModule.forRoot({
403+
domain: 'YOUR_AUTH0_DOMAIN',
404+
clientId: 'YOUR_AUTH0_CLIENT_ID',
405+
authorizationParams: {
406+
redirect_uri: window.location.origin,
407+
audience: 'https://api.example.com',
408+
},
409+
useDpop: true, // Enable DPoP
410+
}),
411+
],
412+
})
413+
export class AppModule {}
414+
```
415+
416+
### Using createFetcher (Recommended)
417+
418+
The simplest way to make authenticated API calls with DPoP is using the `createFetcher` method. It automatically handles tokens, DPoP proofs, and nonce management:
419+
420+
```ts
421+
import { Component } from '@angular/core';
422+
import { AuthService } from '@auth0/auth0-angular';
423+
424+
@Component({
425+
selector: 'app-data',
426+
template: `
427+
<div *ngIf="loading">Loading...</div>
428+
<div *ngIf="data">{{ data | json }}</div>
429+
<div *ngIf="error">{{ error }}</div>
430+
`,
431+
})
432+
export class DataComponent {
433+
data: any;
434+
loading = false;
435+
error: string | null = null;
436+
437+
constructor(private auth: AuthService) {}
438+
439+
async fetchData() {
440+
this.loading = true;
441+
this.error = null;
442+
443+
try {
444+
// Create fetcher - handles tokens, DPoP proofs, and nonces automatically
445+
const fetcher = this.auth.createFetcher({
446+
dpopNonceId: 'my-api',
447+
baseUrl: 'https://api.example.com',
448+
});
449+
450+
const response = await fetcher.fetchWithAuth('/protected-data');
451+
this.data = await response.json();
452+
} catch (err) {
453+
this.error = 'Failed to fetch data';
454+
console.error(err);
455+
} finally {
456+
this.loading = false;
457+
}
458+
}
459+
}
460+
```
461+
462+
The `createFetcher` method automatically:
463+
464+
- Retrieves access tokens
465+
- Adds proper `Authorization` headers (`DPoP <token>` or `Bearer <token>`)
466+
- Generates and includes DPoP proofs in the `DPoP` header
467+
- Manages DPoP nonces per API endpoint
468+
- Automatically retries on nonce errors
469+
- Handles token refreshing
470+
471+
### Multiple API Endpoints
472+
473+
When working with multiple APIs, create separate fetchers for each. Each fetcher manages its own nonces independently:
474+
475+
```ts
476+
import { Injectable } from '@angular/core';
477+
import { AuthService, Fetcher } from '@auth0/auth0-angular';
478+
479+
@Injectable({ providedIn: 'root' })
480+
export class ApiService {
481+
private internalApi: Fetcher;
482+
private partnerApi: Fetcher;
483+
484+
constructor(private auth: AuthService) {
485+
// Each fetcher manages its own nonces independently
486+
this.internalApi = this.auth.createFetcher({
487+
dpopNonceId: 'internal-api',
488+
baseUrl: 'https://internal.example.com',
489+
});
490+
491+
this.partnerApi = this.auth.createFetcher({
492+
dpopNonceId: 'partner-api',
493+
baseUrl: 'https://partner.example.com',
494+
});
495+
}
496+
497+
async getInternalData() {
498+
const response = await this.internalApi.fetchWithAuth('/data');
499+
return response.json();
500+
}
501+
502+
async getPartnerResources() {
503+
const response = await this.partnerApi.fetchWithAuth('/resources');
504+
return response.json();
505+
}
506+
507+
async getAllData() {
508+
const [internal, partner] = await Promise.all([this.getInternalData(), this.getPartnerResources()]);
509+
return { internal, partner };
510+
}
511+
}
512+
```
513+
514+
### Advanced: Manual DPoP Management
515+
516+
For scenarios requiring full control over DPoP proof generation and nonce management:
517+
518+
```ts
519+
import { Component } from '@angular/core';
520+
import { AuthService, UseDpopNonceError } from '@auth0/auth0-angular';
521+
import { firstValueFrom } from 'rxjs';
522+
523+
@Component({
524+
selector: 'app-advanced',
525+
template: `<button (click)="makeRequest()">Make Request</button>`,
526+
})
527+
export class AdvancedComponent {
528+
constructor(private auth: AuthService) {}
529+
530+
async makeRequest() {
531+
try {
532+
// 1. Get access token
533+
const token = await firstValueFrom(this.auth.getAccessTokenSilently());
534+
535+
// 2. Get current DPoP nonce for the API
536+
const nonce = await firstValueFrom(this.auth.getDpopNonce('my-api'));
537+
538+
// 3. Generate DPoP proof
539+
const proof = await firstValueFrom(
540+
this.auth.generateDpopProof({
541+
url: 'https://api.example.com/data',
542+
method: 'POST',
543+
accessToken: token!,
544+
nonce,
545+
})
546+
);
547+
548+
// 4. Make the API request
549+
const response = await fetch('https://api.example.com/data', {
550+
method: 'POST',
551+
headers: {
552+
Authorization: `DPoP ${token}`,
553+
DPoP: proof!,
554+
'Content-Type': 'application/json',
555+
},
556+
body: JSON.stringify({ data: 'example' }),
557+
});
558+
559+
// 5. Update nonce if server provides a new one
560+
const newNonce = response.headers.get('DPoP-Nonce');
561+
if (newNonce) {
562+
await firstValueFrom(this.auth.setDpopNonce(newNonce, 'my-api'));
563+
}
564+
565+
const data = await response.json();
566+
console.log('Success:', data);
567+
} catch (error) {
568+
if (error instanceof UseDpopNonceError) {
569+
console.error('DPoP nonce error:', error.message);
570+
} else {
571+
console.error('Request failed:', error);
572+
}
573+
}
574+
}
575+
}
576+
```
577+
578+
### Error Handling
579+
580+
Handle DPoP-specific errors using the `UseDpopNonceError` class:
581+
582+
```ts
583+
import { Component } from '@angular/core';
584+
import { AuthService, UseDpopNonceError } from '@auth0/auth0-angular';
585+
586+
@Component({
587+
selector: 'app-error-handling',
588+
template: `...`,
589+
})
590+
export class ErrorHandlingComponent {
591+
constructor(private auth: AuthService) {}
592+
593+
async fetchWithErrorHandling() {
594+
try {
595+
const fetcher = this.auth.createFetcher({
596+
dpopNonceId: 'my-api',
597+
baseUrl: 'https://api.example.com',
598+
});
599+
600+
const response = await fetcher.fetchWithAuth('/data');
601+
const data = await response.json();
602+
return data;
603+
} catch (error) {
604+
if (error instanceof UseDpopNonceError) {
605+
// DPoP nonce validation failed
606+
console.error('DPoP nonce error:', error.message);
607+
// The fetcher automatically retries, so this error means
608+
// the retry also failed
609+
} else {
610+
console.error('Other error:', error);
611+
}
612+
throw error;
613+
}
614+
}
615+
}
616+
```
617+
618+
### Standalone Components with DPoP
619+
620+
When using standalone components, enable DPoP in your `provideAuth0` configuration:
621+
622+
```ts
623+
import { bootstrapApplication } from '@angular/platform-browser';
624+
import { provideAuth0 } from '@auth0/auth0-angular';
625+
import { AppComponent } from './app/app.component';
626+
627+
bootstrapApplication(AppComponent, {
628+
providers: [
629+
provideAuth0({
630+
domain: 'YOUR_AUTH0_DOMAIN',
631+
clientId: 'YOUR_AUTH0_CLIENT_ID',
632+
authorizationParams: {
633+
redirect_uri: window.location.origin,
634+
audience: 'https://api.example.com',
635+
},
636+
useDpop: true, // Enable DPoP
637+
}),
638+
],
639+
});
640+
```
641+
383642
## Standalone components and a more functional approach
643+
384644
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.
385645
386646
There are a couple of difference with how you would traditionally implement our SDK:
@@ -398,17 +658,11 @@ const routes: Routes = [
398658
path: 'profile',
399659
component: ProfileComponent,
400660
canActivate: [authGuardFn],
401-
}
661+
},
402662
];
403663

404664
bootstrapApplication(AppComponent, {
405-
providers: [
406-
provideRouter(routes),
407-
provideAuth0(/* Auth Config Goes Here */),
408-
provideHttpClient(
409-
withInterceptors([authHttpInterceptorFn])
410-
)
411-
]
665+
providers: [provideRouter(routes), provideAuth0(/* Auth Config Goes Here */), provideHttpClient(withInterceptors([authHttpInterceptorFn]))],
412666
});
413667
```
414668

0 commit comments

Comments
 (0)