-
Notifications
You must be signed in to change notification settings - Fork 241
Expand file tree
/
Copy pathWebCredentialsManager.ts
More file actions
132 lines (120 loc) · 4.16 KB
/
Copy pathWebCredentialsManager.ts
File metadata and controls
132 lines (120 loc) · 4.16 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
import type { ICredentialsManager } from '../../../core/interfaces';
import type { Credentials, SessionTransferCredentials } from '../../../types';
import {
AuthError,
CredentialsManagerError,
Credentials as CredentialsModel,
ApiCredentials,
} from '../../../core/models';
import type { Auth0Client } from '@auth0/auth0-spa-js';
export class WebCredentialsManager implements ICredentialsManager {
constructor(private client: Auth0Client) {}
async saveCredentials(_credentials: Credentials): Promise<void> {
console.warn(
'`saveCredentials` is a no-op on the web. @auth0/auth0-spa-js handles credential storage automatically.'
);
return Promise.resolve();
}
async getCredentials(
scope?: string,
_minTtl?: number,
parameters?: Record<string, any>,
forceRefresh?: boolean
): Promise<Credentials> {
try {
const tokenResponse = await this.client.getTokenSilently({
cacheMode: forceRefresh ? 'off' : 'on',
authorizationParams: { ...parameters, scope },
detailedResponse: true,
});
const claims = await this.client.getIdTokenClaims();
if (!claims || !claims.exp) {
throw new AuthError(
'ID_TOKEN_CLAIM_VALIDATION_FAILED',
'ID token or expiration claim is missing.'
);
}
return new CredentialsModel({
idToken: tokenResponse.id_token,
accessToken: tokenResponse.access_token,
tokenType: tokenResponse.token_type ?? 'Bearer',
expiresAt: claims.exp,
scope: tokenResponse.scope,
});
} catch (e: any) {
const code = e.error ?? 'GetCredentialsFailed';
const authError = new AuthError(code, e.error_description ?? e.message, {
json: e,
code,
});
throw new CredentialsManagerError(authError);
}
}
async getApiCredentials(
audience: string,
scope?: string,
_minTtl?: number,
parameters?: Record<string, any>
): Promise<ApiCredentials> {
try {
const tokenResponse = await this.client.getTokenSilently({
authorizationParams: {
...parameters,
audience: audience,
scope: scope,
},
detailedResponse: true,
});
// Calculate access token expiration from expires_in (seconds until expiration)
// This is more accurate than using ID token claims for API credentials
const nowInSeconds = Math.floor(Date.now() / 1000);
const expiresAt = nowInSeconds + (tokenResponse.expires_in ?? 3600);
return new ApiCredentials({
accessToken: tokenResponse.access_token,
tokenType: tokenResponse.token_type,
expiresAt: expiresAt,
scope: tokenResponse.scope,
});
} catch (e: any) {
const code = e.error ?? 'GetApiCredentialsFailed';
const authError = new AuthError(code, e.error_description ?? e.message, {
json: e,
code,
});
throw new CredentialsManagerError(authError);
}
}
async hasValidCredentials(): Promise<boolean> {
return this.client.isAuthenticated();
}
async clearCredentials(): Promise<void> {
try {
await this.client.logout({ openUrl: false });
} catch (e: any) {
const code = e.error ?? 'ClearCredentialsFailed';
const authError = new AuthError(code, e.error_description ?? e.message, {
json: e,
code,
});
throw new CredentialsManagerError(authError);
}
}
async getSSOCredentials(
_parameters?: Record<string, any>,
_headers?: Record<string, string>
): Promise<SessionTransferCredentials> {
const authError = new AuthError(
'UnsupportedOperation',
'Native to Web SSO is only supported on native platforms (iOS/Android). This feature is not available in web environments.',
{ code: 'unsupported_operation' }
);
throw new CredentialsManagerError(authError);
}
async clearApiCredentials(audience: string, scope?: string): Promise<void> {
const scopeInfo = scope ? ` and scope ${scope}` : '';
console.warn(
`'clearApiCredentials' for audience ${audience}${scopeInfo} is a no-op on the web. @auth0/auth0-spa-js handles credential storage automatically.`
);
return Promise.resolve();
}
}