@auth0/auth0-spa-js 2.4.1 → 2.6.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +1 -1
- package/dist/auth0-spa-js.development.js +298 -43
- package/dist/auth0-spa-js.development.js.map +1 -1
- package/dist/auth0-spa-js.production.esm.js +1 -1
- package/dist/auth0-spa-js.production.esm.js.map +1 -1
- package/dist/auth0-spa-js.production.js +1 -1
- package/dist/auth0-spa-js.production.js.map +1 -1
- package/dist/auth0-spa-js.worker.development.js +34 -2
- package/dist/auth0-spa-js.worker.development.js.map +1 -1
- package/dist/auth0-spa-js.worker.production.js +1 -1
- package/dist/auth0-spa-js.worker.production.js.map +1 -1
- package/dist/lib/auth0-spa-js.cjs.js +309 -44
- package/dist/lib/auth0-spa-js.cjs.js.map +1 -1
- package/dist/typings/Auth0Client.d.ts +42 -2
- package/dist/typings/Auth0Client.utils.d.ts +32 -0
- package/dist/typings/MyAccountApiClient.d.ts +92 -0
- package/dist/typings/api.d.ts +1 -1
- package/dist/typings/cache/cache-manager.d.ts +18 -1
- package/dist/typings/errors.d.ts +10 -0
- package/dist/typings/fetcher.d.ts +11 -7
- package/dist/typings/global.d.ts +97 -0
- package/dist/typings/http.d.ts +2 -2
- package/dist/typings/index.d.ts +2 -1
- package/dist/typings/transaction-manager.d.ts +15 -4
- package/dist/typings/version.d.ts +1 -1
- package/dist/typings/worker/worker.types.d.ts +1 -0
- package/package.json +1 -1
- package/src/Auth0Client.ts +282 -25
- package/src/Auth0Client.utils.ts +66 -0
- package/src/MyAccountApiClient.ts +158 -0
- package/src/api.ts +7 -1
- package/src/cache/cache-manager.ts +82 -7
- package/src/errors.ts +18 -0
- package/src/fetcher.ts +30 -18
- package/src/global.ts +112 -4
- package/src/http.ts +12 -5
- package/src/index.ts +5 -0
- package/src/transaction-manager.ts +17 -4
- package/src/utils.ts +1 -0
- package/src/version.ts +1 -1
- package/src/worker/token.worker.ts +60 -9
- package/src/worker/worker.types.ts +1 -0
|
@@ -0,0 +1,158 @@
|
|
|
1
|
+
import { AuthorizationParams } from './global';
|
|
2
|
+
import { Fetcher } from './fetcher';
|
|
3
|
+
|
|
4
|
+
interface ConnectRequest {
|
|
5
|
+
/** The name of the connection to link the account with (e.g., 'google-oauth2', 'facebook'). */
|
|
6
|
+
connection: string;
|
|
7
|
+
/** The URI to redirect to after the connection process completes. */
|
|
8
|
+
redirect_uri: string;
|
|
9
|
+
/** An opaque value used to maintain state between the request and callback. */
|
|
10
|
+
state?: string;
|
|
11
|
+
/** A string value used to associate a Client session with an ID Token, and to mitigate replay attacks. */
|
|
12
|
+
nonce?: string;
|
|
13
|
+
/** The PKCE code challenge derived from the code verifier. */
|
|
14
|
+
code_challenge?: string;
|
|
15
|
+
/** The method used to derive the code challenge. Required when code_challenge is provided. */
|
|
16
|
+
code_challenge_method?: 'S256';
|
|
17
|
+
authorization_params?: AuthorizationParams;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
interface ConnectResponse {
|
|
21
|
+
/** The base URI to initiate the account connection flow. */
|
|
22
|
+
connect_uri: string;
|
|
23
|
+
/** The authentication session identifier. */
|
|
24
|
+
auth_session: string;
|
|
25
|
+
/** Parameters to be used with the connect URI. */
|
|
26
|
+
connect_params: {
|
|
27
|
+
/** The ticket identifier to be used with the connection URI. */
|
|
28
|
+
ticket: string;
|
|
29
|
+
};
|
|
30
|
+
/** The number of seconds until the ticket expires. */
|
|
31
|
+
expires_in: number;
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
interface CompleteRequest {
|
|
35
|
+
/** The authentication session identifier */
|
|
36
|
+
auth_session: string;
|
|
37
|
+
/** The authorization code returned from the connect flow */
|
|
38
|
+
connect_code: string;
|
|
39
|
+
/** The redirect URI used in the original request */
|
|
40
|
+
redirect_uri: string;
|
|
41
|
+
/** The PKCE code verifier */
|
|
42
|
+
code_verifier?: string;
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
export interface CompleteResponse {
|
|
46
|
+
/** The unique identifier of the connected account */
|
|
47
|
+
id: string;
|
|
48
|
+
/** The connection name */
|
|
49
|
+
connection: string;
|
|
50
|
+
/** The access type, always 'offline' */
|
|
51
|
+
access_type: 'offline';
|
|
52
|
+
/** Array of scopes granted */
|
|
53
|
+
scopes?: string[];
|
|
54
|
+
/** ISO date string of when the connected account was created */
|
|
55
|
+
created_at: string;
|
|
56
|
+
/** ISO date string of when the refresh token expires (optional) */
|
|
57
|
+
expires_at?: string;
|
|
58
|
+
}
|
|
59
|
+
|
|
60
|
+
// Validation error returned from MyAccount API
|
|
61
|
+
export interface ErrorResponse {
|
|
62
|
+
type: string;
|
|
63
|
+
status: number;
|
|
64
|
+
title: string;
|
|
65
|
+
detail: string;
|
|
66
|
+
validation_errors?: {
|
|
67
|
+
detail: string;
|
|
68
|
+
field?: string;
|
|
69
|
+
pointer?: string;
|
|
70
|
+
source?: string;
|
|
71
|
+
}[];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* Subset of the MyAccount API that handles the connect accounts flow.
|
|
76
|
+
*/
|
|
77
|
+
export class MyAccountApiClient {
|
|
78
|
+
constructor(
|
|
79
|
+
private myAccountFetcher: Fetcher<Response>,
|
|
80
|
+
private apiBase: string
|
|
81
|
+
) {}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* Get a ticket for the connect account flow.
|
|
85
|
+
*/
|
|
86
|
+
async connectAccount(params: ConnectRequest): Promise<ConnectResponse> {
|
|
87
|
+
const res = await this.myAccountFetcher.fetchWithAuth(
|
|
88
|
+
`${this.apiBase}v1/connected-accounts/connect`,
|
|
89
|
+
{
|
|
90
|
+
method: 'POST',
|
|
91
|
+
headers: { 'Content-Type': 'application/json' },
|
|
92
|
+
body: JSON.stringify(params)
|
|
93
|
+
}
|
|
94
|
+
);
|
|
95
|
+
return this._handleResponse(res);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Verify the redirect from the connect account flow and complete the connecting of the account.
|
|
100
|
+
*/
|
|
101
|
+
async completeAccount(params: CompleteRequest): Promise<CompleteResponse> {
|
|
102
|
+
const res = await this.myAccountFetcher.fetchWithAuth(
|
|
103
|
+
`${this.apiBase}v1/connected-accounts/complete`,
|
|
104
|
+
{
|
|
105
|
+
method: 'POST',
|
|
106
|
+
headers: { 'Content-Type': 'application/json' },
|
|
107
|
+
body: JSON.stringify(params)
|
|
108
|
+
}
|
|
109
|
+
);
|
|
110
|
+
return this._handleResponse(res);
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
private async _handleResponse(res: Response) {
|
|
114
|
+
let body: any;
|
|
115
|
+
try {
|
|
116
|
+
body = await res.text();
|
|
117
|
+
body = JSON.parse(body);
|
|
118
|
+
} catch (err) {
|
|
119
|
+
throw new MyAccountApiError({
|
|
120
|
+
type: 'invalid_json',
|
|
121
|
+
status: res.status,
|
|
122
|
+
title: 'Invalid JSON response',
|
|
123
|
+
detail: body || String(err)
|
|
124
|
+
});
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
if (res.ok) {
|
|
128
|
+
return body;
|
|
129
|
+
} else {
|
|
130
|
+
throw new MyAccountApiError(body);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
export class MyAccountApiError extends Error {
|
|
136
|
+
public readonly type: string;
|
|
137
|
+
public readonly status: number;
|
|
138
|
+
public readonly title: string;
|
|
139
|
+
public readonly detail: string;
|
|
140
|
+
public readonly validation_errors?: ErrorResponse['validation_errors'];
|
|
141
|
+
|
|
142
|
+
constructor({
|
|
143
|
+
type,
|
|
144
|
+
status,
|
|
145
|
+
title,
|
|
146
|
+
detail,
|
|
147
|
+
validation_errors
|
|
148
|
+
}: ErrorResponse) {
|
|
149
|
+
super(detail);
|
|
150
|
+
this.name = 'MyAccountApiError';
|
|
151
|
+
this.type = type;
|
|
152
|
+
this.status = status;
|
|
153
|
+
this.title = title;
|
|
154
|
+
this.detail = detail;
|
|
155
|
+
this.validation_errors = validation_errors;
|
|
156
|
+
Object.setPrototypeOf(this, MyAccountApiError.prototype);
|
|
157
|
+
}
|
|
158
|
+
}
|
package/src/api.ts
CHANGED
|
@@ -12,6 +12,7 @@ export async function oauthToken(
|
|
|
12
12
|
scope,
|
|
13
13
|
auth0Client,
|
|
14
14
|
useFormData,
|
|
15
|
+
useMrrt,
|
|
15
16
|
dpop,
|
|
16
17
|
...options
|
|
17
18
|
}: TokenEndpointOptions,
|
|
@@ -20,10 +21,14 @@ export async function oauthToken(
|
|
|
20
21
|
const isTokenExchange =
|
|
21
22
|
options.grant_type === 'urn:ietf:params:oauth:grant-type:token-exchange';
|
|
22
23
|
|
|
24
|
+
const refreshWithMrrt =
|
|
25
|
+
options.grant_type === 'refresh_token' && useMrrt;
|
|
26
|
+
|
|
23
27
|
const allParams = {
|
|
24
28
|
...options,
|
|
25
29
|
...(isTokenExchange && audience && { audience }),
|
|
26
|
-
...(isTokenExchange && scope && { scope })
|
|
30
|
+
...(isTokenExchange && scope && { scope }),
|
|
31
|
+
...(refreshWithMrrt && { audience, scope })
|
|
27
32
|
};
|
|
28
33
|
|
|
29
34
|
const body = useFormData
|
|
@@ -51,6 +56,7 @@ export async function oauthToken(
|
|
|
51
56
|
},
|
|
52
57
|
worker,
|
|
53
58
|
useFormData,
|
|
59
|
+
useMrrt,
|
|
54
60
|
isDpopSupported ? dpop : undefined
|
|
55
61
|
);
|
|
56
62
|
}
|
|
@@ -69,7 +69,9 @@ export class CacheManager {
|
|
|
69
69
|
|
|
70
70
|
async get(
|
|
71
71
|
cacheKey: CacheKey,
|
|
72
|
-
expiryAdjustmentSeconds = DEFAULT_EXPIRY_ADJUSTMENT_SECONDS
|
|
72
|
+
expiryAdjustmentSeconds = DEFAULT_EXPIRY_ADJUSTMENT_SECONDS,
|
|
73
|
+
useMrrt = false,
|
|
74
|
+
cacheMode?: string
|
|
73
75
|
): Promise<Partial<CacheEntry> | undefined> {
|
|
74
76
|
let wrappedEntry = await this.cache.get<WrappedCacheEntry>(
|
|
75
77
|
cacheKey.toKey()
|
|
@@ -85,6 +87,13 @@ export class CacheManager {
|
|
|
85
87
|
if (matchedKey) {
|
|
86
88
|
wrappedEntry = await this.cache.get<WrappedCacheEntry>(matchedKey);
|
|
87
89
|
}
|
|
90
|
+
|
|
91
|
+
// To refresh using MRRT we need to send a request to the server
|
|
92
|
+
// If cacheMode is 'cache-only', this will make us unable to call the server
|
|
93
|
+
// so it won't be needed to find a valid refresh token
|
|
94
|
+
if (!matchedKey && useMrrt && cacheMode !== 'cache-only') {
|
|
95
|
+
return this.getEntryWithRefreshToken(cacheKey, keys);
|
|
96
|
+
}
|
|
88
97
|
}
|
|
89
98
|
|
|
90
99
|
// If we still don't have an entry, exit.
|
|
@@ -97,12 +106,7 @@ export class CacheManager {
|
|
|
97
106
|
|
|
98
107
|
if (wrappedEntry.expiresAt - expiryAdjustmentSeconds < nowSeconds) {
|
|
99
108
|
if (wrappedEntry.body.refresh_token) {
|
|
100
|
-
wrappedEntry
|
|
101
|
-
refresh_token: wrappedEntry.body.refresh_token
|
|
102
|
-
};
|
|
103
|
-
|
|
104
|
-
await this.cache.set(cacheKey.toKey(), wrappedEntry);
|
|
105
|
-
return wrappedEntry.body;
|
|
109
|
+
return this.modifiedCachedEntry(wrappedEntry, cacheKey);
|
|
106
110
|
}
|
|
107
111
|
|
|
108
112
|
await this.cache.remove(cacheKey.toKey());
|
|
@@ -114,6 +118,24 @@ export class CacheManager {
|
|
|
114
118
|
return wrappedEntry.body;
|
|
115
119
|
}
|
|
116
120
|
|
|
121
|
+
private async modifiedCachedEntry(wrappedEntry: WrappedCacheEntry, cacheKey: CacheKey): Promise<Partial<CacheEntry>> {
|
|
122
|
+
// We need to keep audience and scope in order to check them later when doing refresh
|
|
123
|
+
// using MRRT. See getScopeToRequest method.
|
|
124
|
+
wrappedEntry.body = {
|
|
125
|
+
refresh_token: wrappedEntry.body.refresh_token,
|
|
126
|
+
audience: wrappedEntry.body.audience,
|
|
127
|
+
scope: wrappedEntry.body.scope,
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
await this.cache.set(cacheKey.toKey(), wrappedEntry);
|
|
131
|
+
|
|
132
|
+
return {
|
|
133
|
+
refresh_token: wrappedEntry.body.refresh_token,
|
|
134
|
+
audience: wrappedEntry.body.audience,
|
|
135
|
+
scope: wrappedEntry.body.scope,
|
|
136
|
+
};
|
|
137
|
+
}
|
|
138
|
+
|
|
117
139
|
async set(entry: CacheEntry): Promise<void> {
|
|
118
140
|
const cacheKey = new CacheKey({
|
|
119
141
|
clientId: entry.client_id,
|
|
@@ -207,4 +229,57 @@ export class CacheManager {
|
|
|
207
229
|
);
|
|
208
230
|
})[0];
|
|
209
231
|
}
|
|
232
|
+
|
|
233
|
+
/**
|
|
234
|
+
* Returns the first entry that contains a refresh_token that satisfies the following conditions
|
|
235
|
+
* The keys inside the cache are in the format {prefix}::{clientId}::{audience}::{scope}.
|
|
236
|
+
* - `prefix` is strict equal to Auth0's internally configured `keyPrefix`
|
|
237
|
+
* - `clientId` is strict equal to the `cacheKey.clientId`
|
|
238
|
+
* @param keyToMatch The provided cache key
|
|
239
|
+
* @param allKeys A list of existing cache keys
|
|
240
|
+
*/
|
|
241
|
+
private async getEntryWithRefreshToken(keyToMatch: CacheKey, allKeys: Array<string>): Promise<Partial<CacheEntry> | undefined> {
|
|
242
|
+
for (const key of allKeys) {
|
|
243
|
+
const cacheKey = CacheKey.fromKey(key);
|
|
244
|
+
|
|
245
|
+
if (cacheKey.prefix === CACHE_KEY_PREFIX &&
|
|
246
|
+
cacheKey.clientId === keyToMatch.clientId) {
|
|
247
|
+
const cachedEntry = await this.cache.get<WrappedCacheEntry>(key);
|
|
248
|
+
|
|
249
|
+
if (cachedEntry?.body?.refresh_token) {
|
|
250
|
+
return this.modifiedCachedEntry(cachedEntry, keyToMatch);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
return undefined;
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* Updates in the cache all entries that has a match with previous refresh_token with the
|
|
260
|
+
* new refresh_token obtained from the server
|
|
261
|
+
* @param oldRefreshToken Old refresh_token used on refresh
|
|
262
|
+
* @param newRefreshToken New refresh_token obtained from the server after refresh
|
|
263
|
+
*/
|
|
264
|
+
async updateEntry(
|
|
265
|
+
oldRefreshToken: string,
|
|
266
|
+
newRefreshToken: string,
|
|
267
|
+
): Promise<void> {
|
|
268
|
+
const allKeys = await this.getCacheKeys();
|
|
269
|
+
|
|
270
|
+
if (!allKeys) return;
|
|
271
|
+
|
|
272
|
+
for (const key of allKeys) {
|
|
273
|
+
const entry = await this.cache.get<WrappedCacheEntry>(key);
|
|
274
|
+
|
|
275
|
+
if (entry?.body?.refresh_token === oldRefreshToken) {
|
|
276
|
+
const cacheEntry = {
|
|
277
|
+
...entry.body,
|
|
278
|
+
refresh_token: newRefreshToken,
|
|
279
|
+
} as CacheEntry;
|
|
280
|
+
|
|
281
|
+
await this.set(cacheEntry);
|
|
282
|
+
}
|
|
283
|
+
}
|
|
284
|
+
}
|
|
210
285
|
}
|
package/src/errors.ts
CHANGED
|
@@ -35,6 +35,24 @@ export class AuthenticationError extends GenericError {
|
|
|
35
35
|
}
|
|
36
36
|
}
|
|
37
37
|
|
|
38
|
+
/**
|
|
39
|
+
* Thrown when handling the redirect callback for the connect flow fails, will be one of Auth0's
|
|
40
|
+
* Authentication API's Standard Error Responses: https://auth0.com/docs/api/authentication?javascript#standard-error-responses
|
|
41
|
+
*/
|
|
42
|
+
export class ConnectError extends GenericError {
|
|
43
|
+
constructor(
|
|
44
|
+
error: string,
|
|
45
|
+
error_description: string,
|
|
46
|
+
public connection: string,
|
|
47
|
+
public state: string,
|
|
48
|
+
public appState: any = null
|
|
49
|
+
) {
|
|
50
|
+
super(error, error_description);
|
|
51
|
+
//https://github.com/Microsoft/TypeScript-wiki/blob/master/Breaking-Changes.md#extending-built-ins-like-error-array-and-map-may-no-longer-work
|
|
52
|
+
Object.setPrototypeOf(this, ConnectError.prototype);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
|
|
38
56
|
/**
|
|
39
57
|
* Thrown when silent auth times out (usually due to a configuration issue) or
|
|
40
58
|
* when network requests to the Auth server timeout.
|
package/src/fetcher.ts
CHANGED
|
@@ -15,7 +15,12 @@ export type CustomFetchImpl<TOutput extends CustomFetchMinimalOutput> = (
|
|
|
15
15
|
req: Request
|
|
16
16
|
) => Promise<TOutput>;
|
|
17
17
|
|
|
18
|
-
type
|
|
18
|
+
export type AuthParams = {
|
|
19
|
+
scope?: string[];
|
|
20
|
+
audience?: string;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
type AccessTokenFactory = (authParams?: AuthParams) => Promise<string>;
|
|
19
24
|
|
|
20
25
|
export type FetcherConfig<TOutput extends CustomFetchMinimalOutput> = {
|
|
21
26
|
getAccessToken?: AccessTokenFactory;
|
|
@@ -26,7 +31,7 @@ export type FetcherConfig<TOutput extends CustomFetchMinimalOutput> = {
|
|
|
26
31
|
|
|
27
32
|
export type FetcherHooks = {
|
|
28
33
|
isDpopEnabled: () => boolean;
|
|
29
|
-
getAccessToken:
|
|
34
|
+
getAccessToken: AccessTokenFactory;
|
|
30
35
|
getDpopNonce: () => Promise<string | undefined>;
|
|
31
36
|
setDpopNonce: (nonce: string) => Promise<void>;
|
|
32
37
|
generateDpopProof: (params: {
|
|
@@ -83,10 +88,10 @@ export class Fetcher<TOutput extends CustomFetchMinimalOutput> {
|
|
|
83
88
|
throw new TypeError('`url` must be absolute or `baseUrl` non-empty.');
|
|
84
89
|
}
|
|
85
90
|
|
|
86
|
-
protected getAccessToken(): Promise<string> {
|
|
91
|
+
protected getAccessToken(authParams?: AuthParams): Promise<string> {
|
|
87
92
|
return this.config.getAccessToken
|
|
88
|
-
? this.config.getAccessToken()
|
|
89
|
-
: this.hooks.getAccessToken();
|
|
93
|
+
? this.config.getAccessToken(authParams)
|
|
94
|
+
: this.hooks.getAccessToken(authParams);
|
|
90
95
|
}
|
|
91
96
|
|
|
92
97
|
protected buildBaseRequest(
|
|
@@ -109,10 +114,10 @@ export class Fetcher<TOutput extends CustomFetchMinimalOutput> {
|
|
|
109
114
|
);
|
|
110
115
|
}
|
|
111
116
|
|
|
112
|
-
protected
|
|
117
|
+
protected setAuthorizationHeader(
|
|
113
118
|
request: Request,
|
|
114
119
|
accessToken: string
|
|
115
|
-
):
|
|
120
|
+
): void {
|
|
116
121
|
request.headers.set(
|
|
117
122
|
'authorization',
|
|
118
123
|
`${this.config.dpopNonceId ? 'DPoP' : 'Bearer'} ${accessToken}`
|
|
@@ -139,8 +144,8 @@ export class Fetcher<TOutput extends CustomFetchMinimalOutput> {
|
|
|
139
144
|
request.headers.set('dpop', dpopProof);
|
|
140
145
|
}
|
|
141
146
|
|
|
142
|
-
protected async prepareRequest(request: Request) {
|
|
143
|
-
const accessToken = await this.getAccessToken();
|
|
147
|
+
protected async prepareRequest(request: Request, authParams?: AuthParams) {
|
|
148
|
+
const accessToken = await this.getAccessToken(authParams);
|
|
144
149
|
|
|
145
150
|
this.setAuthorizationHeader(request, accessToken);
|
|
146
151
|
|
|
@@ -195,11 +200,12 @@ export class Fetcher<TOutput extends CustomFetchMinimalOutput> {
|
|
|
195
200
|
protected async internalFetchWithAuth(
|
|
196
201
|
info: RequestInfo | URL,
|
|
197
202
|
init: RequestInit | undefined,
|
|
198
|
-
callbacks: FetchWithAuthCallbacks<TOutput
|
|
203
|
+
callbacks: FetchWithAuthCallbacks<TOutput>,
|
|
204
|
+
authParams?: AuthParams
|
|
199
205
|
): Promise<TOutput> {
|
|
200
206
|
const request = this.buildBaseRequest(info, init);
|
|
201
207
|
|
|
202
|
-
await this.prepareRequest(request);
|
|
208
|
+
await this.prepareRequest(request, authParams);
|
|
203
209
|
|
|
204
210
|
const response = await this.config.fetch(request);
|
|
205
211
|
|
|
@@ -208,17 +214,23 @@ export class Fetcher<TOutput extends CustomFetchMinimalOutput> {
|
|
|
208
214
|
|
|
209
215
|
public fetchWithAuth(
|
|
210
216
|
info: RequestInfo | URL,
|
|
211
|
-
init?: RequestInit
|
|
217
|
+
init?: RequestInit,
|
|
218
|
+
authParams?: AuthParams
|
|
212
219
|
): Promise<TOutput> {
|
|
213
220
|
const callbacks: FetchWithAuthCallbacks<TOutput> = {
|
|
214
221
|
onUseDpopNonceError: () =>
|
|
215
|
-
this.internalFetchWithAuth(
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
222
|
+
this.internalFetchWithAuth(
|
|
223
|
+
info,
|
|
224
|
+
init,
|
|
225
|
+
{
|
|
226
|
+
...callbacks,
|
|
227
|
+
// Retry on a `use_dpop_nonce` error, but just once.
|
|
228
|
+
onUseDpopNonceError: undefined
|
|
229
|
+
},
|
|
230
|
+
authParams
|
|
231
|
+
)
|
|
220
232
|
};
|
|
221
233
|
|
|
222
|
-
return this.internalFetchWithAuth(info, init, callbacks);
|
|
234
|
+
return this.internalFetchWithAuth(info, init, callbacks, authParams);
|
|
223
235
|
}
|
|
224
236
|
}
|
package/src/global.ts
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { ICache } from './cache';
|
|
2
2
|
import type { Dpop } from './dpop/dpop';
|
|
3
|
+
import { CompleteResponse } from './MyAccountApiClient';
|
|
3
4
|
|
|
4
5
|
export interface AuthorizationParams {
|
|
5
6
|
/**
|
|
@@ -265,14 +266,20 @@ export interface Auth0ClientOptions extends BaseLoginOptions {
|
|
|
265
266
|
|
|
266
267
|
/**
|
|
267
268
|
* If provided, the SDK will load the token worker from this URL instead of the integrated `blob`. An example of when this is useful is if you have strict
|
|
268
|
-
* Content-Security-Policy (CSP) and wish to avoid needing to set `worker-src: blob:`. We recommend either serving the worker, which you can find in the module
|
|
269
|
-
* at `<module_path>/dist/auth0-spa-js.worker.production.js`, from the same host as your application or using the Auth0 CDN
|
|
269
|
+
* Content-Security-Policy (CSP) and wish to avoid needing to set `worker-src: blob:`. We recommend either serving the worker, which you can find in the module
|
|
270
|
+
* at `<module_path>/dist/auth0-spa-js.worker.production.js`, from the same host as your application or using the Auth0 CDN
|
|
270
271
|
* `https://cdn.auth0.com/js/auth0-spa-js/<version>/auth0-spa-js.worker.production.js`.
|
|
271
|
-
*
|
|
272
|
+
*
|
|
272
273
|
* **Note**: The worker is only used when `useRefreshTokens: true`, `cacheLocation: 'memory'`, and the `cache` is not custom.
|
|
273
274
|
*/
|
|
274
275
|
workerUrl?: string;
|
|
275
276
|
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* If `true`, the SDK will allow the refreshing of tokens using MRRT
|
|
280
|
+
*/
|
|
281
|
+
useMrrt?: boolean;
|
|
282
|
+
|
|
276
283
|
/**
|
|
277
284
|
* If `true`, DPoP (OAuth 2.0 Demonstrating Proof of Possession, RFC9449)
|
|
278
285
|
* will be used to cryptographically bind tokens to this specific browser
|
|
@@ -347,14 +354,29 @@ export interface RedirectLoginOptions<TAppState = any>
|
|
|
347
354
|
openUrl?: (url: string) => Promise<void> | void;
|
|
348
355
|
}
|
|
349
356
|
|
|
357
|
+
/**
|
|
358
|
+
* The types of responses expected from the authorization server.
|
|
359
|
+
* - `code`: used for the standard login flow.
|
|
360
|
+
* - `connect_code`: used for the connect account flow.
|
|
361
|
+
*/
|
|
362
|
+
export enum ResponseType {
|
|
363
|
+
Code = 'code',
|
|
364
|
+
ConnectCode = 'connect_code'
|
|
365
|
+
}
|
|
366
|
+
|
|
350
367
|
export interface RedirectLoginResult<TAppState = any> {
|
|
351
368
|
/**
|
|
352
369
|
* State stored when the redirect request was made
|
|
353
370
|
*/
|
|
354
371
|
appState?: TAppState;
|
|
372
|
+
|
|
373
|
+
/**
|
|
374
|
+
* The type of response, for login it will be `code`
|
|
375
|
+
*/
|
|
376
|
+
response_type: ResponseType.Code;
|
|
355
377
|
}
|
|
356
378
|
|
|
357
|
-
export interface PopupLoginOptions extends BaseLoginOptions {}
|
|
379
|
+
export interface PopupLoginOptions extends BaseLoginOptions { }
|
|
358
380
|
|
|
359
381
|
export interface PopupConfigOptions {
|
|
360
382
|
/**
|
|
@@ -517,12 +539,98 @@ export interface LogoutOptions extends LogoutUrlOptions {
|
|
|
517
539
|
openUrl?: false | ((url: string) => Promise<void> | void);
|
|
518
540
|
}
|
|
519
541
|
|
|
542
|
+
export interface RedirectConnectAccountOptions<TAppState = any> {
|
|
543
|
+
/**
|
|
544
|
+
* The name of the connection to link (e.g. 'google-oauth2').
|
|
545
|
+
*/
|
|
546
|
+
connection: string;
|
|
547
|
+
|
|
548
|
+
/**
|
|
549
|
+
* Additional authorization parameters for the request.
|
|
550
|
+
*
|
|
551
|
+
* @example
|
|
552
|
+
* await auth0.connectAccountWithRedirect({
|
|
553
|
+
* connection: 'google-oauth2',
|
|
554
|
+
* authorization_params: {
|
|
555
|
+
* scope: 'https://www.googleapis.com/auth/calendar'
|
|
556
|
+
* access_type: 'offline'
|
|
557
|
+
* }
|
|
558
|
+
* });
|
|
559
|
+
*
|
|
560
|
+
* @example
|
|
561
|
+
* await auth0.connectAccountWithRedirect({
|
|
562
|
+
* connection: 'github',
|
|
563
|
+
* authorization_params: {
|
|
564
|
+
* scope: 'repo user',
|
|
565
|
+
* audience: 'https://api.github.com'
|
|
566
|
+
* }
|
|
567
|
+
* });
|
|
568
|
+
*/
|
|
569
|
+
authorization_params?: AuthorizationParams;
|
|
570
|
+
|
|
571
|
+
/**
|
|
572
|
+
* The URI to redirect back to after connecting the account.
|
|
573
|
+
*/
|
|
574
|
+
redirectUri?: string;
|
|
575
|
+
|
|
576
|
+
/**
|
|
577
|
+
* Optional application state to persist through the transaction.
|
|
578
|
+
*
|
|
579
|
+
* @example
|
|
580
|
+
* await auth0.connectAccountWithRedirect({
|
|
581
|
+
* connection: 'google-oauth2',
|
|
582
|
+
* appState: { returnTo: '/settings' }
|
|
583
|
+
* });
|
|
584
|
+
*/
|
|
585
|
+
appState?: TAppState;
|
|
586
|
+
|
|
587
|
+
/**
|
|
588
|
+
* Optional function to handle the redirect URL.
|
|
589
|
+
*
|
|
590
|
+
* @example
|
|
591
|
+
* await auth0.connectAccountWithRedirect({
|
|
592
|
+
* connection: 'google-oauth2',
|
|
593
|
+
* openUrl: async (url) => { myBrowserApi.open(url); }
|
|
594
|
+
* });
|
|
595
|
+
*/
|
|
596
|
+
openUrl?: (url: string) => Promise<void>;
|
|
597
|
+
}
|
|
598
|
+
|
|
599
|
+
/**
|
|
600
|
+
* The result returned after a successful account connection redirect.
|
|
601
|
+
*
|
|
602
|
+
* Combines the redirect login result (including any persisted app state)
|
|
603
|
+
* with the complete response from the My Account API.
|
|
604
|
+
*
|
|
605
|
+
* @template TAppState - The type of application state persisted through the transaction.
|
|
606
|
+
* @example
|
|
607
|
+
* const result = await auth0.connectAccountWithRedirect(options);
|
|
608
|
+
* console.log(result.appState); // Access persisted app state
|
|
609
|
+
* console.log(result.connection); // The connection of the account you connected to.
|
|
610
|
+
* console.log(result.response_type === 'connect_code'); // The response type will be 'connect_code'
|
|
611
|
+
*/
|
|
612
|
+
export type ConnectAccountRedirectResult<TAppState = any> = CompleteResponse & {
|
|
613
|
+
/**
|
|
614
|
+
* State stored when the redirect request was made
|
|
615
|
+
*/
|
|
616
|
+
appState?: TAppState;
|
|
617
|
+
|
|
618
|
+
/**
|
|
619
|
+
* The type of response, for connect account it will be `connect_code`
|
|
620
|
+
*/
|
|
621
|
+
response_type: ResponseType.ConnectCode;
|
|
622
|
+
};
|
|
623
|
+
|
|
520
624
|
/**
|
|
521
625
|
* @ignore
|
|
522
626
|
*/
|
|
523
627
|
export interface AuthenticationResult {
|
|
524
628
|
state: string;
|
|
525
629
|
code?: string;
|
|
630
|
+
/**
|
|
631
|
+
* This is for the redirect from the connect account flow.
|
|
632
|
+
*/
|
|
633
|
+
connect_code?: string;
|
|
526
634
|
error?: string;
|
|
527
635
|
error_description?: string;
|
|
528
636
|
}
|
package/src/http.ts
CHANGED
|
@@ -65,7 +65,8 @@ const fetchWithWorker = async (
|
|
|
65
65
|
fetchOptions: FetchOptions,
|
|
66
66
|
timeout: number,
|
|
67
67
|
worker: Worker,
|
|
68
|
-
useFormData?: boolean
|
|
68
|
+
useFormData?: boolean,
|
|
69
|
+
useMrrt?: boolean
|
|
69
70
|
) => {
|
|
70
71
|
return sendMessage(
|
|
71
72
|
{
|
|
@@ -76,7 +77,8 @@ const fetchWithWorker = async (
|
|
|
76
77
|
timeout,
|
|
77
78
|
fetchUrl,
|
|
78
79
|
fetchOptions,
|
|
79
|
-
useFormData
|
|
80
|
+
useFormData,
|
|
81
|
+
useMrrt
|
|
80
82
|
},
|
|
81
83
|
worker
|
|
82
84
|
);
|
|
@@ -89,7 +91,8 @@ export const switchFetch = async (
|
|
|
89
91
|
fetchOptions: FetchOptions,
|
|
90
92
|
worker?: Worker,
|
|
91
93
|
useFormData?: boolean,
|
|
92
|
-
timeout = DEFAULT_FETCH_TIMEOUT_MS
|
|
94
|
+
timeout = DEFAULT_FETCH_TIMEOUT_MS,
|
|
95
|
+
useMrrt?: boolean,
|
|
93
96
|
): Promise<any> => {
|
|
94
97
|
if (worker) {
|
|
95
98
|
return fetchWithWorker(
|
|
@@ -99,7 +102,8 @@ export const switchFetch = async (
|
|
|
99
102
|
fetchOptions,
|
|
100
103
|
timeout,
|
|
101
104
|
worker,
|
|
102
|
-
useFormData
|
|
105
|
+
useFormData,
|
|
106
|
+
useMrrt
|
|
103
107
|
);
|
|
104
108
|
} else {
|
|
105
109
|
return fetchWithoutWorker(fetchUrl, fetchOptions, timeout);
|
|
@@ -114,6 +118,7 @@ export async function getJSON<T>(
|
|
|
114
118
|
options: FetchOptions,
|
|
115
119
|
worker?: Worker,
|
|
116
120
|
useFormData?: boolean,
|
|
121
|
+
useMrrt?: boolean,
|
|
117
122
|
dpop?: Pick<Dpop, 'generateProof' | 'getNonce' | 'setNonce'>,
|
|
118
123
|
isDpopRetry?: boolean
|
|
119
124
|
): Promise<T> {
|
|
@@ -139,7 +144,8 @@ export async function getJSON<T>(
|
|
|
139
144
|
options,
|
|
140
145
|
worker,
|
|
141
146
|
useFormData,
|
|
142
|
-
timeout
|
|
147
|
+
timeout,
|
|
148
|
+
useMrrt,
|
|
143
149
|
);
|
|
144
150
|
fetchError = null;
|
|
145
151
|
break;
|
|
@@ -210,6 +216,7 @@ export async function getJSON<T>(
|
|
|
210
216
|
options,
|
|
211
217
|
worker,
|
|
212
218
|
useFormData,
|
|
219
|
+
useMrrt,
|
|
213
220
|
dpop,
|
|
214
221
|
true // !
|
|
215
222
|
);
|
package/src/index.ts
CHANGED
|
@@ -23,6 +23,7 @@ export async function createAuth0Client(options: Auth0ClientOptions) {
|
|
|
23
23
|
export { Auth0Client };
|
|
24
24
|
|
|
25
25
|
export {
|
|
26
|
+
ConnectError,
|
|
26
27
|
GenericError,
|
|
27
28
|
AuthenticationError,
|
|
28
29
|
TimeoutError,
|
|
@@ -48,3 +49,7 @@ export {
|
|
|
48
49
|
} from './cache';
|
|
49
50
|
|
|
50
51
|
export { type FetcherConfig } from './fetcher';
|
|
52
|
+
|
|
53
|
+
export {
|
|
54
|
+
MyAccountApiError
|
|
55
|
+
} from './MyAccountApiClient';
|