@ai-sdk/mcp 1.0.45 → 1.0.47
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/CHANGELOG.md +12 -0
- package/dist/index.d.mts +11 -1
- package/dist/index.d.ts +11 -1
- package/dist/index.js +193 -25
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +193 -25
- package/dist/index.mjs.map +1 -1
- package/package.json +1 -1
- package/src/index.ts +4 -1
- package/src/tool/oauth-types.ts +18 -14
- package/src/tool/oauth.ts +263 -21
package/package.json
CHANGED
package/src/index.ts
CHANGED
|
@@ -21,7 +21,10 @@ export type {
|
|
|
21
21
|
ClientCapabilities as MCPClientCapabilities,
|
|
22
22
|
} from './tool/types';
|
|
23
23
|
export { auth, UnauthorizedError } from './tool/oauth';
|
|
24
|
-
export type {
|
|
24
|
+
export type {
|
|
25
|
+
OAuthAuthorizationServerInformation,
|
|
26
|
+
OAuthClientProvider,
|
|
27
|
+
} from './tool/oauth';
|
|
25
28
|
export type {
|
|
26
29
|
OAuthClientInformation,
|
|
27
30
|
OAuthClientMetadata,
|
package/src/tool/oauth-types.ts
CHANGED
|
@@ -1,18 +1,4 @@
|
|
|
1
1
|
import { z } from 'zod/v4';
|
|
2
|
-
/**
|
|
3
|
-
* OAuth 2.1 token response
|
|
4
|
-
*/
|
|
5
|
-
export const OAuthTokensSchema = z
|
|
6
|
-
.object({
|
|
7
|
-
access_token: z.string(),
|
|
8
|
-
id_token: z.string().optional(), // Optional for OAuth 2.1, but necessary in OpenID Connect
|
|
9
|
-
token_type: z.string(),
|
|
10
|
-
expires_in: z.number().optional(),
|
|
11
|
-
scope: z.string().optional(),
|
|
12
|
-
refresh_token: z.string().optional(),
|
|
13
|
-
})
|
|
14
|
-
.strip();
|
|
15
|
-
|
|
16
2
|
/**
|
|
17
3
|
* Reusable URL validation that disallows javascript: scheme
|
|
18
4
|
*/
|
|
@@ -42,6 +28,22 @@ export const SafeUrlSchema = z
|
|
|
42
28
|
{ message: 'URL cannot use javascript:, data:, or vbscript: scheme' },
|
|
43
29
|
);
|
|
44
30
|
|
|
31
|
+
/**
|
|
32
|
+
* OAuth 2.1 token response
|
|
33
|
+
*/
|
|
34
|
+
export const OAuthTokensSchema = z
|
|
35
|
+
.object({
|
|
36
|
+
access_token: z.string(),
|
|
37
|
+
id_token: z.string().optional(), // Optional for OAuth 2.1, but necessary in OpenID Connect
|
|
38
|
+
token_type: z.string(),
|
|
39
|
+
expires_in: z.number().optional(),
|
|
40
|
+
scope: z.string().optional(),
|
|
41
|
+
refresh_token: z.string().optional(),
|
|
42
|
+
authorization_server: SafeUrlSchema.optional(),
|
|
43
|
+
token_endpoint: SafeUrlSchema.optional(),
|
|
44
|
+
})
|
|
45
|
+
.strip();
|
|
46
|
+
|
|
45
47
|
export const OAuthProtectedResourceMetadataSchema = z
|
|
46
48
|
.object({
|
|
47
49
|
resource: z.string().url(),
|
|
@@ -118,6 +120,8 @@ export const OAuthClientInformationSchema = z
|
|
|
118
120
|
client_secret: z.string().optional(),
|
|
119
121
|
client_id_issued_at: z.number().optional(),
|
|
120
122
|
client_secret_expires_at: z.number().optional(),
|
|
123
|
+
authorization_server: SafeUrlSchema.optional(),
|
|
124
|
+
token_endpoint: SafeUrlSchema.optional(),
|
|
121
125
|
})
|
|
122
126
|
.strip();
|
|
123
127
|
|
package/src/tool/oauth.ts
CHANGED
|
@@ -31,6 +31,11 @@ import { parseJSON, type FetchFunction } from '@ai-sdk/provider-utils';
|
|
|
31
31
|
|
|
32
32
|
export type AuthResult = 'AUTHORIZED' | 'REDIRECT';
|
|
33
33
|
|
|
34
|
+
export interface OAuthAuthorizationServerInformation {
|
|
35
|
+
authorizationServerUrl: string;
|
|
36
|
+
tokenEndpoint: string;
|
|
37
|
+
}
|
|
38
|
+
|
|
34
39
|
export interface OAuthClientProvider {
|
|
35
40
|
/**
|
|
36
41
|
* Returns current access token if present; undefined otherwise.
|
|
@@ -83,6 +88,13 @@ export interface OAuthClientProvider {
|
|
|
83
88
|
saveClientInformation?(
|
|
84
89
|
clientInformation: OAuthClientInformation,
|
|
85
90
|
): void | Promise<void>;
|
|
91
|
+
authorizationServerInformation?():
|
|
92
|
+
| OAuthAuthorizationServerInformation
|
|
93
|
+
| undefined
|
|
94
|
+
| Promise<OAuthAuthorizationServerInformation | undefined>;
|
|
95
|
+
saveAuthorizationServerInformation?(
|
|
96
|
+
authorizationServerInformation: OAuthAuthorizationServerInformation,
|
|
97
|
+
): void | Promise<void>;
|
|
86
98
|
state?(): string | Promise<string>;
|
|
87
99
|
saveState?(state: string): void | Promise<void>;
|
|
88
100
|
storedState?(): string | undefined | Promise<string | undefined>;
|
|
@@ -99,6 +111,158 @@ export class UnauthorizedError extends Error {
|
|
|
99
111
|
}
|
|
100
112
|
}
|
|
101
113
|
|
|
114
|
+
function normalizeUrl(url: string | URL): string {
|
|
115
|
+
return new URL(url).href;
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
function createAuthorizationServerInformation(
|
|
119
|
+
authorizationServerUrl: string | URL,
|
|
120
|
+
metadata?: AuthorizationServerMetadata,
|
|
121
|
+
): OAuthAuthorizationServerInformation {
|
|
122
|
+
return {
|
|
123
|
+
authorizationServerUrl: normalizeUrl(authorizationServerUrl),
|
|
124
|
+
tokenEndpoint: normalizeUrl(
|
|
125
|
+
metadata?.token_endpoint
|
|
126
|
+
? new URL(metadata.token_endpoint)
|
|
127
|
+
: new URL('/token', authorizationServerUrl),
|
|
128
|
+
),
|
|
129
|
+
};
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function addAuthorizationServerInformationToTokens(
|
|
133
|
+
tokens: OAuthTokens,
|
|
134
|
+
authorizationServerInformation: OAuthAuthorizationServerInformation,
|
|
135
|
+
): OAuthTokens {
|
|
136
|
+
return {
|
|
137
|
+
...tokens,
|
|
138
|
+
authorization_server: authorizationServerInformation.authorizationServerUrl,
|
|
139
|
+
token_endpoint: authorizationServerInformation.tokenEndpoint,
|
|
140
|
+
};
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
function addAuthorizationServerInformationToClientInformation<
|
|
144
|
+
CLIENT_INFORMATION extends OAuthClientInformation,
|
|
145
|
+
>(
|
|
146
|
+
clientInformation: CLIENT_INFORMATION,
|
|
147
|
+
authorizationServerInformation: OAuthAuthorizationServerInformation,
|
|
148
|
+
): CLIENT_INFORMATION {
|
|
149
|
+
return {
|
|
150
|
+
...clientInformation,
|
|
151
|
+
authorization_server: authorizationServerInformation.authorizationServerUrl,
|
|
152
|
+
token_endpoint: authorizationServerInformation.tokenEndpoint,
|
|
153
|
+
};
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function getAuthorizationServerInformationFromCredentials(credentials?: {
|
|
157
|
+
authorization_server?: string;
|
|
158
|
+
token_endpoint?: string;
|
|
159
|
+
}): OAuthAuthorizationServerInformation | undefined {
|
|
160
|
+
if (!credentials?.authorization_server || !credentials.token_endpoint) {
|
|
161
|
+
return undefined;
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
return {
|
|
165
|
+
authorizationServerUrl: normalizeUrl(credentials.authorization_server),
|
|
166
|
+
tokenEndpoint: normalizeUrl(credentials.token_endpoint),
|
|
167
|
+
};
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
async function getStoredAuthorizationServerInformation({
|
|
171
|
+
provider,
|
|
172
|
+
clientInformation,
|
|
173
|
+
tokens,
|
|
174
|
+
}: {
|
|
175
|
+
provider: OAuthClientProvider;
|
|
176
|
+
clientInformation: OAuthClientInformation;
|
|
177
|
+
tokens?: OAuthTokens;
|
|
178
|
+
}): Promise<OAuthAuthorizationServerInformation | undefined> {
|
|
179
|
+
const tokenAuthorizationServerInformation =
|
|
180
|
+
getAuthorizationServerInformationFromCredentials(tokens);
|
|
181
|
+
if (tokenAuthorizationServerInformation) {
|
|
182
|
+
return tokenAuthorizationServerInformation;
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
const providerAuthorizationServerInformation =
|
|
186
|
+
await provider.authorizationServerInformation?.();
|
|
187
|
+
if (providerAuthorizationServerInformation) {
|
|
188
|
+
return {
|
|
189
|
+
authorizationServerUrl: normalizeUrl(
|
|
190
|
+
providerAuthorizationServerInformation.authorizationServerUrl,
|
|
191
|
+
),
|
|
192
|
+
tokenEndpoint: normalizeUrl(
|
|
193
|
+
providerAuthorizationServerInformation.tokenEndpoint,
|
|
194
|
+
),
|
|
195
|
+
};
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
return getAuthorizationServerInformationFromCredentials(clientInformation);
|
|
199
|
+
}
|
|
200
|
+
|
|
201
|
+
async function saveAuthorizationServerInformation({
|
|
202
|
+
provider,
|
|
203
|
+
clientInformation,
|
|
204
|
+
authorizationServerInformation,
|
|
205
|
+
}: {
|
|
206
|
+
provider: OAuthClientProvider;
|
|
207
|
+
clientInformation: OAuthClientInformation;
|
|
208
|
+
authorizationServerInformation: OAuthAuthorizationServerInformation;
|
|
209
|
+
}): Promise<boolean> {
|
|
210
|
+
if (provider.saveAuthorizationServerInformation) {
|
|
211
|
+
await provider.saveAuthorizationServerInformation(
|
|
212
|
+
authorizationServerInformation,
|
|
213
|
+
);
|
|
214
|
+
return true;
|
|
215
|
+
}
|
|
216
|
+
|
|
217
|
+
if (provider.saveClientInformation) {
|
|
218
|
+
await provider.saveClientInformation(
|
|
219
|
+
addAuthorizationServerInformationToClientInformation(
|
|
220
|
+
clientInformation,
|
|
221
|
+
authorizationServerInformation,
|
|
222
|
+
),
|
|
223
|
+
);
|
|
224
|
+
return true;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
return false;
|
|
228
|
+
}
|
|
229
|
+
|
|
230
|
+
function assertResourceMetadataUrlSameOrigin(
|
|
231
|
+
serverUrl: string | URL,
|
|
232
|
+
resourceMetadataUrl?: URL,
|
|
233
|
+
): void {
|
|
234
|
+
if (!resourceMetadataUrl) {
|
|
235
|
+
return;
|
|
236
|
+
}
|
|
237
|
+
|
|
238
|
+
const expectedOrigin = new URL(serverUrl).origin;
|
|
239
|
+
if (resourceMetadataUrl.origin !== expectedOrigin) {
|
|
240
|
+
throw new MCPClientOAuthError({
|
|
241
|
+
message: `OAuth protected resource metadata URL ${resourceMetadataUrl.href} must have the same origin as the MCP server URL ${expectedOrigin}`,
|
|
242
|
+
});
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
function assertAuthorizationServerInformationMatches({
|
|
247
|
+
storedAuthorizationServerInformation,
|
|
248
|
+
currentAuthorizationServerInformation,
|
|
249
|
+
}: {
|
|
250
|
+
storedAuthorizationServerInformation: OAuthAuthorizationServerInformation;
|
|
251
|
+
currentAuthorizationServerInformation: OAuthAuthorizationServerInformation;
|
|
252
|
+
}): void {
|
|
253
|
+
if (
|
|
254
|
+
storedAuthorizationServerInformation.authorizationServerUrl !==
|
|
255
|
+
currentAuthorizationServerInformation.authorizationServerUrl ||
|
|
256
|
+
storedAuthorizationServerInformation.tokenEndpoint !==
|
|
257
|
+
currentAuthorizationServerInformation.tokenEndpoint
|
|
258
|
+
) {
|
|
259
|
+
throw new MCPClientOAuthError({
|
|
260
|
+
message:
|
|
261
|
+
'OAuth authorization server metadata does not match the metadata that issued the stored credentials',
|
|
262
|
+
});
|
|
263
|
+
}
|
|
264
|
+
}
|
|
265
|
+
|
|
102
266
|
/**
|
|
103
267
|
* Extracts the OAuth 2.0 Protected Resource Metadata URL from a WWW-Authenticate header (RFC9728).
|
|
104
268
|
* Looks for a resource="..." parameter.
|
|
@@ -668,7 +832,12 @@ export async function exchangeAuthorization(
|
|
|
668
832
|
});
|
|
669
833
|
|
|
670
834
|
if (addClientAuthentication) {
|
|
671
|
-
addClientAuthentication(
|
|
835
|
+
await addClientAuthentication(
|
|
836
|
+
headers,
|
|
837
|
+
params,
|
|
838
|
+
authorizationServerUrl,
|
|
839
|
+
metadata,
|
|
840
|
+
);
|
|
672
841
|
} else {
|
|
673
842
|
const supportedMethods =
|
|
674
843
|
metadata?.token_endpoint_auth_methods_supported ?? [];
|
|
@@ -755,7 +924,12 @@ export async function refreshAuthorization(
|
|
|
755
924
|
});
|
|
756
925
|
|
|
757
926
|
if (addClientAuthentication) {
|
|
758
|
-
addClientAuthentication(
|
|
927
|
+
await addClientAuthentication(
|
|
928
|
+
headers,
|
|
929
|
+
params,
|
|
930
|
+
authorizationServerUrl,
|
|
931
|
+
metadata,
|
|
932
|
+
);
|
|
759
933
|
} else {
|
|
760
934
|
const supportedMethods =
|
|
761
935
|
metadata?.token_endpoint_auth_methods_supported ?? [];
|
|
@@ -910,6 +1084,11 @@ async function authInternal(
|
|
|
910
1084
|
): Promise<AuthResult> {
|
|
911
1085
|
let resourceMetadata: OAuthProtectedResourceMetadata | undefined;
|
|
912
1086
|
let authorizationServerUrl: string | URL | undefined;
|
|
1087
|
+
|
|
1088
|
+
/** Reject Protected Resource Metadata URLs outside the configured MCP server origin. */
|
|
1089
|
+
assertResourceMetadataUrlSameOrigin(serverUrl, resourceMetadataUrl);
|
|
1090
|
+
|
|
1091
|
+
/** Discover PRM and select its advertised authorization server. */
|
|
913
1092
|
try {
|
|
914
1093
|
resourceMetadata = await discoverOAuthProtectedResourceMetadata(
|
|
915
1094
|
serverUrl,
|
|
@@ -924,27 +1103,29 @@ async function authInternal(
|
|
|
924
1103
|
}
|
|
925
1104
|
} catch {}
|
|
926
1105
|
|
|
927
|
-
/**
|
|
928
|
-
* If we don't get a valid authorization server metadata from protected resource metadata,
|
|
929
|
-
* fallback to the legacy MCP spec's implementation (version 2025-03-26): MCP server acts as the Authorization server.
|
|
930
|
-
*/
|
|
1106
|
+
/** Fall back to legacy MCP behavior where the MCP server is the Authorization Server */
|
|
931
1107
|
if (!authorizationServerUrl) {
|
|
932
1108
|
authorizationServerUrl = serverUrl;
|
|
933
1109
|
}
|
|
934
1110
|
|
|
1111
|
+
/** Validate and select the resource value sent to the AS */
|
|
935
1112
|
const resource: URL | undefined = await selectResourceURL(
|
|
936
1113
|
serverUrl,
|
|
937
1114
|
provider,
|
|
938
1115
|
resourceMetadata,
|
|
939
1116
|
);
|
|
940
1117
|
|
|
1118
|
+
/** Discover AS metadata and derive the credential pin for this flow */
|
|
941
1119
|
const metadata = await discoverAuthorizationServerMetadata(
|
|
942
1120
|
authorizationServerUrl,
|
|
943
1121
|
{
|
|
944
1122
|
fetchFn,
|
|
945
1123
|
},
|
|
946
1124
|
);
|
|
1125
|
+
const currentAuthorizationServerInformation =
|
|
1126
|
+
createAuthorizationServerInformation(authorizationServerUrl, metadata);
|
|
947
1127
|
|
|
1128
|
+
/** Load or register client credentials with the AS pin attached. */
|
|
948
1129
|
let clientInformation = await Promise.resolve(provider.clientInformation());
|
|
949
1130
|
if (!clientInformation) {
|
|
950
1131
|
if (authorizationCode !== undefined) {
|
|
@@ -965,11 +1146,14 @@ async function authInternal(
|
|
|
965
1146
|
fetchFn,
|
|
966
1147
|
});
|
|
967
1148
|
|
|
968
|
-
|
|
969
|
-
|
|
1149
|
+
clientInformation = addAuthorizationServerInformationToClientInformation(
|
|
1150
|
+
fullInformation,
|
|
1151
|
+
currentAuthorizationServerInformation,
|
|
1152
|
+
);
|
|
1153
|
+
await provider.saveClientInformation(clientInformation);
|
|
970
1154
|
}
|
|
971
1155
|
|
|
972
|
-
|
|
1156
|
+
/** On callback, validate state and AS pin before code exchange */
|
|
973
1157
|
if (authorizationCode !== undefined) {
|
|
974
1158
|
if (provider.storedState) {
|
|
975
1159
|
const expectedState = await provider.storedState();
|
|
@@ -980,6 +1164,22 @@ async function authInternal(
|
|
|
980
1164
|
}
|
|
981
1165
|
}
|
|
982
1166
|
|
|
1167
|
+
const storedAuthorizationServerInformation =
|
|
1168
|
+
await getStoredAuthorizationServerInformation({
|
|
1169
|
+
provider,
|
|
1170
|
+
clientInformation,
|
|
1171
|
+
});
|
|
1172
|
+
if (!storedAuthorizationServerInformation) {
|
|
1173
|
+
throw new MCPClientOAuthError({
|
|
1174
|
+
message:
|
|
1175
|
+
'Stored OAuth authorization server metadata is required when exchanging an authorization code',
|
|
1176
|
+
});
|
|
1177
|
+
}
|
|
1178
|
+
assertAuthorizationServerInformationMatches({
|
|
1179
|
+
storedAuthorizationServerInformation,
|
|
1180
|
+
currentAuthorizationServerInformation,
|
|
1181
|
+
});
|
|
1182
|
+
|
|
983
1183
|
const codeVerifier = await provider.codeVerifier();
|
|
984
1184
|
const tokens = await exchangeAuthorization(authorizationServerUrl, {
|
|
985
1185
|
metadata,
|
|
@@ -992,27 +1192,55 @@ async function authInternal(
|
|
|
992
1192
|
fetchFn: fetchFn,
|
|
993
1193
|
});
|
|
994
1194
|
|
|
995
|
-
await provider.saveTokens(
|
|
1195
|
+
await provider.saveTokens(
|
|
1196
|
+
addAuthorizationServerInformationToTokens(
|
|
1197
|
+
tokens,
|
|
1198
|
+
currentAuthorizationServerInformation,
|
|
1199
|
+
),
|
|
1200
|
+
);
|
|
996
1201
|
return 'AUTHORIZED';
|
|
997
1202
|
}
|
|
998
1203
|
|
|
999
1204
|
const tokens = await provider.tokens();
|
|
1000
1205
|
|
|
1001
|
-
|
|
1206
|
+
/** Refresh only when stored credentials match the current AS pin */
|
|
1002
1207
|
if (tokens?.refresh_token) {
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
metadata,
|
|
1208
|
+
const storedAuthorizationServerInformation =
|
|
1209
|
+
await getStoredAuthorizationServerInformation({
|
|
1210
|
+
provider,
|
|
1007
1211
|
clientInformation,
|
|
1008
|
-
|
|
1009
|
-
resource,
|
|
1010
|
-
addClientAuthentication: provider.addClientAuthentication,
|
|
1011
|
-
fetchFn,
|
|
1212
|
+
tokens,
|
|
1012
1213
|
});
|
|
1013
1214
|
|
|
1014
|
-
|
|
1015
|
-
|
|
1215
|
+
if (storedAuthorizationServerInformation) {
|
|
1216
|
+
assertAuthorizationServerInformationMatches({
|
|
1217
|
+
storedAuthorizationServerInformation,
|
|
1218
|
+
currentAuthorizationServerInformation,
|
|
1219
|
+
});
|
|
1220
|
+
} else {
|
|
1221
|
+
await provider.invalidateCredentials?.('tokens');
|
|
1222
|
+
}
|
|
1223
|
+
|
|
1224
|
+
try {
|
|
1225
|
+
if (storedAuthorizationServerInformation) {
|
|
1226
|
+
// Attempt to refresh the token
|
|
1227
|
+
const newTokens = await refreshAuthorization(authorizationServerUrl, {
|
|
1228
|
+
metadata,
|
|
1229
|
+
clientInformation,
|
|
1230
|
+
refreshToken: tokens.refresh_token,
|
|
1231
|
+
resource,
|
|
1232
|
+
addClientAuthentication: provider.addClientAuthentication,
|
|
1233
|
+
fetchFn,
|
|
1234
|
+
});
|
|
1235
|
+
|
|
1236
|
+
await provider.saveTokens(
|
|
1237
|
+
addAuthorizationServerInformationToTokens(
|
|
1238
|
+
newTokens,
|
|
1239
|
+
currentAuthorizationServerInformation,
|
|
1240
|
+
),
|
|
1241
|
+
);
|
|
1242
|
+
return 'AUTHORIZED';
|
|
1243
|
+
}
|
|
1016
1244
|
} catch (error) {
|
|
1017
1245
|
if (
|
|
1018
1246
|
// If this is a ServerError, or an unknown type, log it out and try to continue. Otherwise, escalate so we can fix things and retry.
|
|
@@ -1027,6 +1255,7 @@ async function authInternal(
|
|
|
1027
1255
|
}
|
|
1028
1256
|
}
|
|
1029
1257
|
|
|
1258
|
+
/** Start authorization and persist the AS pin before redirecting */
|
|
1030
1259
|
const state = provider.state ? await provider.state() : undefined;
|
|
1031
1260
|
if (state && provider.saveState) {
|
|
1032
1261
|
await provider.saveState(state);
|
|
@@ -1045,6 +1274,19 @@ async function authInternal(
|
|
|
1045
1274
|
},
|
|
1046
1275
|
);
|
|
1047
1276
|
|
|
1277
|
+
const savedAuthorizationServerInformation =
|
|
1278
|
+
await saveAuthorizationServerInformation({
|
|
1279
|
+
provider,
|
|
1280
|
+
clientInformation,
|
|
1281
|
+
authorizationServerInformation: currentAuthorizationServerInformation,
|
|
1282
|
+
});
|
|
1283
|
+
if (!savedAuthorizationServerInformation) {
|
|
1284
|
+
throw new MCPClientOAuthError({
|
|
1285
|
+
message:
|
|
1286
|
+
'OAuth authorization server metadata must be saveable before starting authorization',
|
|
1287
|
+
});
|
|
1288
|
+
}
|
|
1289
|
+
|
|
1048
1290
|
await provider.saveCodeVerifier(codeVerifier);
|
|
1049
1291
|
await provider.redirectToAuthorization(authorizationUrl);
|
|
1050
1292
|
return 'REDIRECT';
|