Skip to content

Commit 16173d0

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 2f6e62c commit 16173d0

File tree

1 file changed

+259
-0
lines changed

1 file changed

+259
-0
lines changed

EXAMPLES.md

Lines changed: 259 additions & 0 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
- [Connect Accounts for using Token Vault](#connect-accounts-for-using-token-vault)
1314

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

0 commit comments

Comments
 (0)