@stackframe/stack-shared 2.5.15 → 2.5.17
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 +13 -0
- package/dist/interface/clientInterface.d.ts +2 -0
- package/dist/interface/clientInterface.js +29 -23
- package/dist/interface/crud/current-user.d.ts +50 -0
- package/dist/interface/crud/current-user.js +2 -0
- package/dist/interface/crud/users.d.ts +100 -0
- package/dist/interface/crud/users.js +25 -2
- package/dist/interface/webhooks.d.ts +50 -0
- package/dist/known-errors.d.ts +3 -0
- package/dist/known-errors.js +5 -0
- package/dist/schema-fields.d.ts +8 -0
- package/dist/schema-fields.js +29 -0
- package/dist/utils/arrays.d.ts +1 -0
- package/dist/utils/arrays.js +3 -0
- package/dist/utils/crypto.d.ts +1 -0
- package/dist/utils/crypto.js +11 -1
- package/dist/utils/results.d.ts +2 -0
- package/dist/utils/results.js +9 -0
- package/dist/utils/strings.js +14 -2
- package/dist/utils/uuids.js +2 -2
- package/package.json +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -28,6 +28,8 @@ export declare class StackClientInterface {
|
|
|
28
28
|
prodDashboard: string;
|
|
29
29
|
prodBackend: string;
|
|
30
30
|
}>;
|
|
31
|
+
protected _networkRetry<T>(cb: () => Promise<Result<T, any>>, session?: InternalSession | null, requestType?: "client" | "server" | "admin"): Promise<T>;
|
|
32
|
+
protected _networkRetryException<T>(cb: () => Promise<T>, session?: InternalSession | null, requestType?: "client" | "server" | "admin"): Promise<T>;
|
|
31
33
|
fetchNewAccessToken(refreshToken: RefreshToken): Promise<AccessToken | null>;
|
|
32
34
|
protected sendClientRequest(path: string, requestOptions: RequestInit, session: InternalSession | null, requestType?: "client" | "server" | "admin"): Promise<Response & {
|
|
33
35
|
usedTokens: {
|
|
@@ -47,13 +47,13 @@ export class StackClientInterface {
|
|
|
47
47
|
}
|
|
48
48
|
});
|
|
49
49
|
const prodDashboard = await tryRequest(async () => {
|
|
50
|
-
const res = await fetch("https://app.
|
|
50
|
+
const res = await fetch("https://app.stack-auth.com/health");
|
|
51
51
|
if (!res.ok) {
|
|
52
52
|
throw new Error(`${res.status} ${res.statusText}: ${await res.text()}`);
|
|
53
53
|
}
|
|
54
54
|
});
|
|
55
55
|
const prodBackend = await tryRequest(async () => {
|
|
56
|
-
const res = await fetch("https://api.
|
|
56
|
+
const res = await fetch("https://api.stack-auth.com/health");
|
|
57
57
|
if (!res.ok) {
|
|
58
58
|
throw new Error(`${res.status} ${res.statusText}: ${await res.text()}`);
|
|
59
59
|
}
|
|
@@ -66,6 +66,28 @@ export class StackClientInterface {
|
|
|
66
66
|
prodBackend,
|
|
67
67
|
};
|
|
68
68
|
}
|
|
69
|
+
async _networkRetry(cb, session, requestType) {
|
|
70
|
+
const retriedResult = await Result.retry(cb, 5, { exponentialDelayBase: 1000 });
|
|
71
|
+
// try to diagnose the error for the user
|
|
72
|
+
if (retriedResult.status === "error") {
|
|
73
|
+
if (!navigator.onLine) {
|
|
74
|
+
throw new Error("Failed to send Stack network request. It seems like you are offline. (window.navigator.onLine is falsy)", { cause: retriedResult.error });
|
|
75
|
+
}
|
|
76
|
+
throw new Error(deindent `
|
|
77
|
+
Stack is unable to connect to the server. Please check your internet connection and try again.
|
|
78
|
+
|
|
79
|
+
If the problem persists, please contact Stack support and provide a screenshot of your entire browser console.
|
|
80
|
+
|
|
81
|
+
${retriedResult.error}
|
|
82
|
+
|
|
83
|
+
${JSON.stringify(await this.runNetworkDiagnostics(session, requestType), null, 2)}
|
|
84
|
+
`, { cause: retriedResult.error });
|
|
85
|
+
}
|
|
86
|
+
return retriedResult.data;
|
|
87
|
+
}
|
|
88
|
+
async _networkRetryException(cb, session, requestType) {
|
|
89
|
+
return await this._networkRetry(async () => await Result.fromThrowingAsync(cb), session, requestType);
|
|
90
|
+
}
|
|
69
91
|
async fetchNewAccessToken(refreshToken) {
|
|
70
92
|
if (!('publishableClientKey' in this.options)) {
|
|
71
93
|
// TODO support it
|
|
@@ -81,7 +103,7 @@ export class StackClientInterface {
|
|
|
81
103
|
client_secret: this.options.publishableClientKey,
|
|
82
104
|
token_endpoint_auth_method: 'client_secret_post',
|
|
83
105
|
};
|
|
84
|
-
const rawResponse = await oauth.refreshTokenGrantRequest(as, client, refreshToken.token);
|
|
106
|
+
const rawResponse = await this._networkRetryException(async () => await oauth.refreshTokenGrantRequest(as, client, refreshToken.token));
|
|
85
107
|
const response = await this._processResponse(rawResponse);
|
|
86
108
|
if (response.status === "error") {
|
|
87
109
|
const error = response.error;
|
|
@@ -108,23 +130,7 @@ export class StackClientInterface {
|
|
|
108
130
|
session ??= this.createSession({
|
|
109
131
|
refreshToken: null,
|
|
110
132
|
});
|
|
111
|
-
|
|
112
|
-
// try to diagnose the error for the user
|
|
113
|
-
if (retriedResult.status === "error") {
|
|
114
|
-
if (!navigator.onLine) {
|
|
115
|
-
throw new Error("Failed to send Stack request. It seems like you are offline. (window.navigator.onLine is falsy)", { cause: retriedResult.error });
|
|
116
|
-
}
|
|
117
|
-
throw new Error(deindent `
|
|
118
|
-
Stack is unable to connect to the server. Please check your internet connection and try again.
|
|
119
|
-
|
|
120
|
-
If the problem persists, please contact Stack support and provide a screenshot of your entire browser console.
|
|
121
|
-
|
|
122
|
-
${retriedResult.error}
|
|
123
|
-
|
|
124
|
-
${JSON.stringify(await this.runNetworkDiagnostics(session, requestType), null, 2)}
|
|
125
|
-
`, { cause: retriedResult.error });
|
|
126
|
-
}
|
|
127
|
-
return retriedResult.data;
|
|
133
|
+
return await this._networkRetry(() => this.sendClientRequestInner(path, requestOptions, session, requestType), session, requestType);
|
|
128
134
|
}
|
|
129
135
|
createSession(options) {
|
|
130
136
|
const session = new InternalSession({
|
|
@@ -213,7 +219,7 @@ export class StackClientInterface {
|
|
|
213
219
|
catch (e) {
|
|
214
220
|
if (e instanceof TypeError) {
|
|
215
221
|
// Network error, retry
|
|
216
|
-
console.
|
|
222
|
+
console.warn(`Stack detected a network error while fetching ${url}, retrying.`, e, { url });
|
|
217
223
|
return Result.error(e);
|
|
218
224
|
}
|
|
219
225
|
throw e;
|
|
@@ -492,7 +498,7 @@ export class StackClientInterface {
|
|
|
492
498
|
client_secret: this.options.publishableClientKey,
|
|
493
499
|
token_endpoint_auth_method: 'client_secret_post',
|
|
494
500
|
};
|
|
495
|
-
const params = oauth.validateAuthResponse(as, client, options.oauthParams, options.state);
|
|
501
|
+
const params = await this._networkRetryException(async () => oauth.validateAuthResponse(as, client, options.oauthParams, options.state));
|
|
496
502
|
if (oauth.isOAuth2Error(params)) {
|
|
497
503
|
throw new StackAssertionError("Error validating outer OAuth response", { params }); // Handle OAuth 2.0 redirect error
|
|
498
504
|
}
|
|
@@ -551,7 +557,7 @@ export class StackClientInterface {
|
|
|
551
557
|
return user;
|
|
552
558
|
}
|
|
553
559
|
async listCurrentUserTeamPermissions(options, session) {
|
|
554
|
-
const response = await this.sendClientRequest(`/team-permissions?team_id=${options.teamId}
|
|
560
|
+
const response = await this.sendClientRequest(`/team-permissions?team_id=${options.teamId}&user_id=me&recursive=${options.recursive}`, {}, session);
|
|
555
561
|
const result = await response.json();
|
|
556
562
|
return result.items;
|
|
557
563
|
}
|
|
@@ -16,6 +16,29 @@ export declare const currentUserCrud: import("../../crud").CrudSchemaFromOptions
|
|
|
16
16
|
signed_up_at_millis: number;
|
|
17
17
|
has_password: NonNullable<boolean | undefined>;
|
|
18
18
|
auth_with_email: NonNullable<boolean | undefined>;
|
|
19
|
+
auth_methods: ({
|
|
20
|
+
type: "password";
|
|
21
|
+
identifier: string;
|
|
22
|
+
} | {
|
|
23
|
+
type: "otp";
|
|
24
|
+
contact_channel: {
|
|
25
|
+
type: "email";
|
|
26
|
+
email: string;
|
|
27
|
+
};
|
|
28
|
+
} | {
|
|
29
|
+
type: "oauth";
|
|
30
|
+
provider: {
|
|
31
|
+
type: string;
|
|
32
|
+
provider_user_id: string;
|
|
33
|
+
};
|
|
34
|
+
})[];
|
|
35
|
+
connected_accounts: {
|
|
36
|
+
type: "oauth";
|
|
37
|
+
provider: {
|
|
38
|
+
type: string;
|
|
39
|
+
provider_user_id: string;
|
|
40
|
+
};
|
|
41
|
+
}[];
|
|
19
42
|
} & {
|
|
20
43
|
selected_team: {
|
|
21
44
|
id: string;
|
|
@@ -38,6 +61,8 @@ export declare const currentUserCrud: import("../../crud").CrudSchemaFromOptions
|
|
|
38
61
|
has_password: undefined;
|
|
39
62
|
auth_with_email: undefined;
|
|
40
63
|
oauth_providers: undefined;
|
|
64
|
+
auth_methods: undefined;
|
|
65
|
+
connected_accounts: undefined;
|
|
41
66
|
client_metadata: undefined;
|
|
42
67
|
server_metadata: undefined;
|
|
43
68
|
}, "">;
|
|
@@ -62,6 +87,29 @@ export declare const currentUserCrud: import("../../crud").CrudSchemaFromOptions
|
|
|
62
87
|
id: string;
|
|
63
88
|
account_id: string;
|
|
64
89
|
}[];
|
|
90
|
+
auth_methods: ({
|
|
91
|
+
type: "password";
|
|
92
|
+
identifier: string;
|
|
93
|
+
} | {
|
|
94
|
+
type: "otp";
|
|
95
|
+
contact_channel: {
|
|
96
|
+
type: "email";
|
|
97
|
+
email: string;
|
|
98
|
+
};
|
|
99
|
+
} | {
|
|
100
|
+
type: "oauth";
|
|
101
|
+
provider: {
|
|
102
|
+
type: string;
|
|
103
|
+
provider_user_id: string;
|
|
104
|
+
};
|
|
105
|
+
})[];
|
|
106
|
+
connected_accounts: {
|
|
107
|
+
type: "oauth";
|
|
108
|
+
provider: {
|
|
109
|
+
type: string;
|
|
110
|
+
provider_user_id: string;
|
|
111
|
+
};
|
|
112
|
+
}[];
|
|
65
113
|
client_metadata: {} | null;
|
|
66
114
|
server_metadata: {} | null;
|
|
67
115
|
} | null, import("yup").AnyObject, {
|
|
@@ -81,6 +129,8 @@ export declare const currentUserCrud: import("../../crud").CrudSchemaFromOptions
|
|
|
81
129
|
has_password: undefined;
|
|
82
130
|
auth_with_email: undefined;
|
|
83
131
|
oauth_providers: undefined;
|
|
132
|
+
auth_methods: undefined;
|
|
133
|
+
connected_accounts: undefined;
|
|
84
134
|
client_metadata: undefined;
|
|
85
135
|
server_metadata: undefined;
|
|
86
136
|
}, "">;
|
|
@@ -20,6 +20,8 @@ const clientReadSchema = usersCrudServerReadSchema.pick([
|
|
|
20
20
|
"auth_with_email",
|
|
21
21
|
"oauth_providers",
|
|
22
22
|
"selected_team_id",
|
|
23
|
+
"auth_methods",
|
|
24
|
+
"connected_accounts",
|
|
23
25
|
]).concat(yupObject({
|
|
24
26
|
selected_team: teamsCrudClientReadSchema.nullable().defined(),
|
|
25
27
|
})).nullable().defined(); // TODO: next-release: make required
|
|
@@ -41,6 +41,29 @@ export declare const usersCrudServerReadSchema: import("yup").ObjectSchema<{
|
|
|
41
41
|
id: string;
|
|
42
42
|
account_id: string;
|
|
43
43
|
}[];
|
|
44
|
+
auth_methods: ({
|
|
45
|
+
type: "password";
|
|
46
|
+
identifier: string;
|
|
47
|
+
} | {
|
|
48
|
+
type: "otp";
|
|
49
|
+
contact_channel: {
|
|
50
|
+
type: "email";
|
|
51
|
+
email: string;
|
|
52
|
+
};
|
|
53
|
+
} | {
|
|
54
|
+
type: "oauth";
|
|
55
|
+
provider: {
|
|
56
|
+
type: string;
|
|
57
|
+
provider_user_id: string;
|
|
58
|
+
};
|
|
59
|
+
})[];
|
|
60
|
+
connected_accounts: {
|
|
61
|
+
type: "oauth";
|
|
62
|
+
provider: {
|
|
63
|
+
type: string;
|
|
64
|
+
provider_user_id: string;
|
|
65
|
+
};
|
|
66
|
+
}[];
|
|
44
67
|
client_metadata: {} | null;
|
|
45
68
|
server_metadata: {} | null;
|
|
46
69
|
}, import("yup").AnyObject, {
|
|
@@ -60,6 +83,8 @@ export declare const usersCrudServerReadSchema: import("yup").ObjectSchema<{
|
|
|
60
83
|
has_password: undefined;
|
|
61
84
|
auth_with_email: undefined;
|
|
62
85
|
oauth_providers: undefined;
|
|
86
|
+
auth_methods: undefined;
|
|
87
|
+
connected_accounts: undefined;
|
|
63
88
|
client_metadata: undefined;
|
|
64
89
|
server_metadata: undefined;
|
|
65
90
|
}, "">;
|
|
@@ -114,6 +139,29 @@ export declare const usersCrud: import("../../crud").CrudSchemaFromOptions<{
|
|
|
114
139
|
id: string;
|
|
115
140
|
account_id: string;
|
|
116
141
|
}[];
|
|
142
|
+
auth_methods: ({
|
|
143
|
+
type: "password";
|
|
144
|
+
identifier: string;
|
|
145
|
+
} | {
|
|
146
|
+
type: "otp";
|
|
147
|
+
contact_channel: {
|
|
148
|
+
type: "email";
|
|
149
|
+
email: string;
|
|
150
|
+
};
|
|
151
|
+
} | {
|
|
152
|
+
type: "oauth";
|
|
153
|
+
provider: {
|
|
154
|
+
type: string;
|
|
155
|
+
provider_user_id: string;
|
|
156
|
+
};
|
|
157
|
+
})[];
|
|
158
|
+
connected_accounts: {
|
|
159
|
+
type: "oauth";
|
|
160
|
+
provider: {
|
|
161
|
+
type: string;
|
|
162
|
+
provider_user_id: string;
|
|
163
|
+
};
|
|
164
|
+
}[];
|
|
117
165
|
client_metadata: {} | null;
|
|
118
166
|
server_metadata: {} | null;
|
|
119
167
|
}, import("yup").AnyObject, {
|
|
@@ -133,6 +181,8 @@ export declare const usersCrud: import("../../crud").CrudSchemaFromOptions<{
|
|
|
133
181
|
has_password: undefined;
|
|
134
182
|
auth_with_email: undefined;
|
|
135
183
|
oauth_providers: undefined;
|
|
184
|
+
auth_methods: undefined;
|
|
185
|
+
connected_accounts: undefined;
|
|
136
186
|
client_metadata: undefined;
|
|
137
187
|
server_metadata: undefined;
|
|
138
188
|
}, "">;
|
|
@@ -238,6 +288,29 @@ export declare const userCreatedWebhookEvent: {
|
|
|
238
288
|
id: string;
|
|
239
289
|
account_id: string;
|
|
240
290
|
}[];
|
|
291
|
+
auth_methods: ({
|
|
292
|
+
type: "password";
|
|
293
|
+
identifier: string;
|
|
294
|
+
} | {
|
|
295
|
+
type: "otp";
|
|
296
|
+
contact_channel: {
|
|
297
|
+
type: "email";
|
|
298
|
+
email: string;
|
|
299
|
+
};
|
|
300
|
+
} | {
|
|
301
|
+
type: "oauth";
|
|
302
|
+
provider: {
|
|
303
|
+
type: string;
|
|
304
|
+
provider_user_id: string;
|
|
305
|
+
};
|
|
306
|
+
})[];
|
|
307
|
+
connected_accounts: {
|
|
308
|
+
type: "oauth";
|
|
309
|
+
provider: {
|
|
310
|
+
type: string;
|
|
311
|
+
provider_user_id: string;
|
|
312
|
+
};
|
|
313
|
+
}[];
|
|
241
314
|
client_metadata: {} | null;
|
|
242
315
|
server_metadata: {} | null;
|
|
243
316
|
}, import("yup").AnyObject, {
|
|
@@ -257,6 +330,8 @@ export declare const userCreatedWebhookEvent: {
|
|
|
257
330
|
has_password: undefined;
|
|
258
331
|
auth_with_email: undefined;
|
|
259
332
|
oauth_providers: undefined;
|
|
333
|
+
auth_methods: undefined;
|
|
334
|
+
connected_accounts: undefined;
|
|
260
335
|
client_metadata: undefined;
|
|
261
336
|
server_metadata: undefined;
|
|
262
337
|
}, "">;
|
|
@@ -289,6 +364,29 @@ export declare const userUpdatedWebhookEvent: {
|
|
|
289
364
|
id: string;
|
|
290
365
|
account_id: string;
|
|
291
366
|
}[];
|
|
367
|
+
auth_methods: ({
|
|
368
|
+
type: "password";
|
|
369
|
+
identifier: string;
|
|
370
|
+
} | {
|
|
371
|
+
type: "otp";
|
|
372
|
+
contact_channel: {
|
|
373
|
+
type: "email";
|
|
374
|
+
email: string;
|
|
375
|
+
};
|
|
376
|
+
} | {
|
|
377
|
+
type: "oauth";
|
|
378
|
+
provider: {
|
|
379
|
+
type: string;
|
|
380
|
+
provider_user_id: string;
|
|
381
|
+
};
|
|
382
|
+
})[];
|
|
383
|
+
connected_accounts: {
|
|
384
|
+
type: "oauth";
|
|
385
|
+
provider: {
|
|
386
|
+
type: string;
|
|
387
|
+
provider_user_id: string;
|
|
388
|
+
};
|
|
389
|
+
}[];
|
|
292
390
|
client_metadata: {} | null;
|
|
293
391
|
server_metadata: {} | null;
|
|
294
392
|
}, import("yup").AnyObject, {
|
|
@@ -308,6 +406,8 @@ export declare const userUpdatedWebhookEvent: {
|
|
|
308
406
|
has_password: undefined;
|
|
309
407
|
auth_with_email: undefined;
|
|
310
408
|
oauth_providers: undefined;
|
|
409
|
+
auth_methods: undefined;
|
|
410
|
+
connected_accounts: undefined;
|
|
311
411
|
client_metadata: undefined;
|
|
312
412
|
server_metadata: undefined;
|
|
313
413
|
}, "">;
|
|
@@ -22,12 +22,35 @@ export const usersCrudServerReadSchema = fieldSchema.yupObject({
|
|
|
22
22
|
profile_image_url: fieldSchema.profileImageUrlSchema.nullable().defined(),
|
|
23
23
|
signed_up_at_millis: fieldSchema.signedUpAtMillisSchema.required(),
|
|
24
24
|
has_password: fieldSchema.yupBoolean().required().meta({ openapiField: { description: 'Whether the user has a password associated with their account', exampleValue: true } }),
|
|
25
|
-
|
|
25
|
+
/**
|
|
26
|
+
* @deprecated
|
|
27
|
+
*/
|
|
28
|
+
auth_with_email: fieldSchema.yupBoolean().required().meta({ openapiField: { hidden: true, description: 'Whether the user can authenticate with their primary e-mail. If set to true, the user can log-in with credentials and/or magic link, if enabled in the project settings.', exampleValue: true } }),
|
|
29
|
+
/**
|
|
30
|
+
* @deprecated
|
|
31
|
+
*/
|
|
26
32
|
oauth_providers: fieldSchema.yupArray(fieldSchema.yupObject({
|
|
27
33
|
id: fieldSchema.yupString().required(),
|
|
28
34
|
account_id: fieldSchema.yupString().required(),
|
|
29
35
|
email: fieldSchema.yupString().nullable(),
|
|
30
|
-
}).required()).required().meta({ openapiField: { description: 'A list of OAuth providers connected to this account', exampleValue: [{ id: 'google', account_id: '12345', email: 'john.doe@gmail.com' }] } }),
|
|
36
|
+
}).required()).required().meta({ openapiField: { hidden: true, description: 'A list of OAuth providers connected to this account', exampleValue: [{ id: 'google', account_id: '12345', email: 'john.doe@gmail.com' }] } }),
|
|
37
|
+
auth_methods: fieldSchema.yupArray(fieldSchema.yupUnion(fieldSchema.yupObject({
|
|
38
|
+
type: fieldSchema.yupString().oneOf(['password']).required(),
|
|
39
|
+
identifier: fieldSchema.yupString().required(),
|
|
40
|
+
}).required(), fieldSchema.yupObject({
|
|
41
|
+
type: fieldSchema.yupString().oneOf(['otp']).required(),
|
|
42
|
+
contact_channel: fieldSchema.yupObject({
|
|
43
|
+
type: fieldSchema.yupString().oneOf(['email']).required(),
|
|
44
|
+
email: fieldSchema.yupString().required(),
|
|
45
|
+
}).required(),
|
|
46
|
+
}).required(), fieldSchema.yupObject({
|
|
47
|
+
type: fieldSchema.yupString().oneOf(['oauth']).required(),
|
|
48
|
+
provider: fieldSchema.userOAuthProviderSchema.required(),
|
|
49
|
+
}).required())).required().meta({ openapiField: { description: 'A list of authentication methods available for this user to sign in with', exampleValue: [{ "contact_channel": { "email": "john.doe@gmail.com", "type": "email", }, "type": "otp", }] } }),
|
|
50
|
+
connected_accounts: fieldSchema.yupArray(fieldSchema.yupUnion(fieldSchema.yupObject({
|
|
51
|
+
type: fieldSchema.yupString().oneOf(['oauth']).required(),
|
|
52
|
+
provider: fieldSchema.userOAuthProviderSchema.required(),
|
|
53
|
+
}).required())).required().meta({ openapiField: { description: 'A list of connected accounts to this user', exampleValue: [{ "provider": { "provider_user_id": "12345", "type": "google", }, "type": "oauth", }] } }),
|
|
31
54
|
client_metadata: fieldSchema.userClientMetadataSchema,
|
|
32
55
|
server_metadata: fieldSchema.userServerMetadataSchema,
|
|
33
56
|
}).required();
|
|
@@ -31,6 +31,29 @@ export declare const webhookEvents: readonly [{
|
|
|
31
31
|
id: string;
|
|
32
32
|
account_id: string;
|
|
33
33
|
}[];
|
|
34
|
+
auth_methods: ({
|
|
35
|
+
type: "password";
|
|
36
|
+
identifier: string;
|
|
37
|
+
} | {
|
|
38
|
+
type: "otp";
|
|
39
|
+
contact_channel: {
|
|
40
|
+
type: "email";
|
|
41
|
+
email: string;
|
|
42
|
+
};
|
|
43
|
+
} | {
|
|
44
|
+
type: "oauth";
|
|
45
|
+
provider: {
|
|
46
|
+
type: string;
|
|
47
|
+
provider_user_id: string;
|
|
48
|
+
};
|
|
49
|
+
})[];
|
|
50
|
+
connected_accounts: {
|
|
51
|
+
type: "oauth";
|
|
52
|
+
provider: {
|
|
53
|
+
type: string;
|
|
54
|
+
provider_user_id: string;
|
|
55
|
+
};
|
|
56
|
+
}[];
|
|
34
57
|
client_metadata: {} | null;
|
|
35
58
|
server_metadata: {} | null;
|
|
36
59
|
}, yup.AnyObject, {
|
|
@@ -50,6 +73,8 @@ export declare const webhookEvents: readonly [{
|
|
|
50
73
|
has_password: undefined;
|
|
51
74
|
auth_with_email: undefined;
|
|
52
75
|
oauth_providers: undefined;
|
|
76
|
+
auth_methods: undefined;
|
|
77
|
+
connected_accounts: undefined;
|
|
53
78
|
client_metadata: undefined;
|
|
54
79
|
server_metadata: undefined;
|
|
55
80
|
}, "">;
|
|
@@ -81,6 +106,29 @@ export declare const webhookEvents: readonly [{
|
|
|
81
106
|
id: string;
|
|
82
107
|
account_id: string;
|
|
83
108
|
}[];
|
|
109
|
+
auth_methods: ({
|
|
110
|
+
type: "password";
|
|
111
|
+
identifier: string;
|
|
112
|
+
} | {
|
|
113
|
+
type: "otp";
|
|
114
|
+
contact_channel: {
|
|
115
|
+
type: "email";
|
|
116
|
+
email: string;
|
|
117
|
+
};
|
|
118
|
+
} | {
|
|
119
|
+
type: "oauth";
|
|
120
|
+
provider: {
|
|
121
|
+
type: string;
|
|
122
|
+
provider_user_id: string;
|
|
123
|
+
};
|
|
124
|
+
})[];
|
|
125
|
+
connected_accounts: {
|
|
126
|
+
type: "oauth";
|
|
127
|
+
provider: {
|
|
128
|
+
type: string;
|
|
129
|
+
provider_user_id: string;
|
|
130
|
+
};
|
|
131
|
+
}[];
|
|
84
132
|
client_metadata: {} | null;
|
|
85
133
|
server_metadata: {} | null;
|
|
86
134
|
}, yup.AnyObject, {
|
|
@@ -100,6 +148,8 @@ export declare const webhookEvents: readonly [{
|
|
|
100
148
|
has_password: undefined;
|
|
101
149
|
auth_with_email: undefined;
|
|
102
150
|
oauth_providers: undefined;
|
|
151
|
+
auth_methods: undefined;
|
|
152
|
+
connected_accounts: undefined;
|
|
103
153
|
client_metadata: undefined;
|
|
104
154
|
server_metadata: undefined;
|
|
105
155
|
}, "">;
|
package/dist/known-errors.d.ts
CHANGED
|
@@ -360,5 +360,8 @@ export declare const KnownErrors: {
|
|
|
360
360
|
InvalidStandardOAuthProviderId: KnownErrorConstructor<KnownError & KnownErrorBrand<"INVALID_STANDARD_OAUTH_PROVIDER_ID">, [any]> & {
|
|
361
361
|
errorCode: "INVALID_STANDARD_OAUTH_PROVIDER_ID";
|
|
362
362
|
};
|
|
363
|
+
InvalidAuthorizationCode: KnownErrorConstructor<KnownError & KnownErrorBrand<"INVALID_AUTHORIZATION_CODE">, []> & {
|
|
364
|
+
errorCode: "INVALID_AUTHORIZATION_CODE";
|
|
365
|
+
};
|
|
363
366
|
};
|
|
364
367
|
export {};
|
package/dist/known-errors.js
CHANGED
|
@@ -502,6 +502,10 @@ const InvalidStandardOAuthProviderId = createKnownErrorConstructor(KnownError, "
|
|
|
502
502
|
provider_id: providerId,
|
|
503
503
|
},
|
|
504
504
|
], (json) => [json.provider_id]);
|
|
505
|
+
const InvalidAuthorizationCode = createKnownErrorConstructor(KnownError, "INVALID_AUTHORIZATION_CODE", () => [
|
|
506
|
+
400,
|
|
507
|
+
"The given authorization code is invalid.",
|
|
508
|
+
], () => []);
|
|
505
509
|
export const KnownErrors = {
|
|
506
510
|
UnsupportedError,
|
|
507
511
|
BodyParsingError,
|
|
@@ -583,6 +587,7 @@ export const KnownErrors = {
|
|
|
583
587
|
TeamPermissionRequired,
|
|
584
588
|
InvalidSharedOAuthProviderId,
|
|
585
589
|
InvalidStandardOAuthProviderId,
|
|
590
|
+
InvalidAuthorizationCode,
|
|
586
591
|
};
|
|
587
592
|
// ensure that all known error codes are unique
|
|
588
593
|
const knownErrorCodes = new Set();
|
package/dist/schema-fields.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export declare function yupMixed<A extends {}>(...args: Parameters<typeof yup.mi
|
|
|
12
12
|
export declare function yupArray<A extends yup.Maybe<yup.AnyObject> = yup.AnyObject, B = any>(...args: Parameters<typeof yup.array<A, B>>): yup.ArraySchema<B[] | undefined, A, undefined, "">;
|
|
13
13
|
export declare function yupTuple<T extends [unknown, ...unknown[]]>(...args: Parameters<typeof yup.tuple<T>>): yup.TupleSchema<T | undefined, yup.AnyObject, undefined, "">;
|
|
14
14
|
export declare function yupObject<A extends yup.Maybe<yup.AnyObject>, B extends yup.ObjectShape>(...args: Parameters<typeof yup.object<A, B>>): yup.ObjectSchema<yup.TypeFromShape<B, yup.AnyObject> extends infer T ? T extends yup.TypeFromShape<B, yup.AnyObject> ? T extends {} ? { [k in keyof T]: T[k]; } : T : never : never, yup.AnyObject, yup.DefaultFromShape<B> extends infer T_1 ? T_1 extends yup.DefaultFromShape<B> ? T_1 extends {} ? { [k_1 in keyof T_1]: T_1[k_1]; } : T_1 : never : never, "">;
|
|
15
|
+
export declare function yupUnion<T extends yup.ISchema<any>[]>(...args: T): yup.ISchema<yup.InferType<T[number]>>;
|
|
15
16
|
export declare const adaptSchema: yup.MixedSchema<typeof StackAdaptSentinel | undefined, yup.AnyObject, undefined, "">;
|
|
16
17
|
/**
|
|
17
18
|
* Yup's URL schema does not recognize some URLs (including `http://localhost`) as a valid URL. This schema is a workaround for that.
|
|
@@ -63,6 +64,13 @@ export declare const profileImageUrlSchema: yup.StringSchema<string | undefined,
|
|
|
63
64
|
export declare const signedUpAtMillisSchema: yup.NumberSchema<number | undefined, yup.AnyObject, undefined, "">;
|
|
64
65
|
export declare const userClientMetadataSchema: yup.MixedSchema<{} | null, yup.AnyObject, undefined, "">;
|
|
65
66
|
export declare const userServerMetadataSchema: yup.MixedSchema<{} | null, yup.AnyObject, undefined, "">;
|
|
67
|
+
export declare const userOAuthProviderSchema: yup.ObjectSchema<{
|
|
68
|
+
type: string;
|
|
69
|
+
provider_user_id: string;
|
|
70
|
+
}, yup.AnyObject, {
|
|
71
|
+
type: undefined;
|
|
72
|
+
provider_user_id: undefined;
|
|
73
|
+
}, "">;
|
|
66
74
|
export declare const signInEmailSchema: yup.StringSchema<string | undefined, yup.AnyObject, undefined, "">;
|
|
67
75
|
export declare const emailOtpSignInCallbackUrlSchema: yup.StringSchema<string | undefined, yup.AnyObject, undefined, "">;
|
|
68
76
|
export declare const emailVerificationCallbackUrlSchema: yup.StringSchema<string | undefined, yup.AnyObject, undefined, "">;
|
package/dist/schema-fields.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as yup from "yup";
|
|
2
|
+
import { StackAssertionError } from "./utils/errors";
|
|
2
3
|
import { allProviders } from "./utils/oauth";
|
|
3
4
|
import { isUuid } from "./utils/uuids";
|
|
4
5
|
const _idDescription = (identify) => `The unique identifier of this ${identify}`;
|
|
@@ -55,6 +56,30 @@ export function yupObject(...args) {
|
|
|
55
56
|
return object.default(undefined);
|
|
56
57
|
}
|
|
57
58
|
/* eslint-enable no-restricted-syntax */
|
|
59
|
+
export function yupUnion(...args) {
|
|
60
|
+
if (args.length === 0)
|
|
61
|
+
throw new Error('yupUnion must have at least one schema');
|
|
62
|
+
const [first] = args;
|
|
63
|
+
const firstDesc = first.describe();
|
|
64
|
+
for (const schema of args) {
|
|
65
|
+
const desc = schema.describe();
|
|
66
|
+
if (desc.type !== firstDesc.type)
|
|
67
|
+
throw new StackAssertionError(`yupUnion must have schemas of the same type (got: ${firstDesc.type} and ${desc.type})`, { first, schema, firstDesc, desc });
|
|
68
|
+
}
|
|
69
|
+
return yupMixed().required().test('is-one-of', 'Invalid value', async (value, context) => {
|
|
70
|
+
const errors = [];
|
|
71
|
+
for (const schema of args) {
|
|
72
|
+
try {
|
|
73
|
+
await schema.validate(value, context.options);
|
|
74
|
+
return true;
|
|
75
|
+
}
|
|
76
|
+
catch (e) {
|
|
77
|
+
errors.push(e);
|
|
78
|
+
}
|
|
79
|
+
}
|
|
80
|
+
throw new AggregateError(errors, 'Invalid value; must be one of the provided schemas');
|
|
81
|
+
});
|
|
82
|
+
}
|
|
58
83
|
// Common
|
|
59
84
|
export const adaptSchema = yupMixed();
|
|
60
85
|
/**
|
|
@@ -160,6 +185,10 @@ export const profileImageUrlSchema = yupString().meta({ openapiField: { descript
|
|
|
160
185
|
export const signedUpAtMillisSchema = yupNumber().meta({ openapiField: { description: _signedUpAtMillisDescription, exampleValue: 1630000000000 } });
|
|
161
186
|
export const userClientMetadataSchema = jsonSchema.meta({ openapiField: { description: _clientMetaDataDescription('user'), exampleValue: { key: 'value' } } });
|
|
162
187
|
export const userServerMetadataSchema = jsonSchema.meta({ openapiField: { description: _serverMetaDataDescription('user'), exampleValue: { key: 'value' } } });
|
|
188
|
+
export const userOAuthProviderSchema = yupObject({
|
|
189
|
+
type: yupString().required(),
|
|
190
|
+
provider_user_id: yupString().required(),
|
|
191
|
+
});
|
|
163
192
|
// Auth
|
|
164
193
|
export const signInEmailSchema = emailSchema.meta({ openapiField: { description: 'The email to sign in with.', exampleValue: 'johndoe@example.com' } });
|
|
165
194
|
export const emailOtpSignInCallbackUrlSchema = urlSchema.meta({ openapiField: { description: 'The base callback URL to construct the magic link from. A query argument `code` with the verification code will be appended to it. The page should then make a request to the `/auth/otp/sign-in` endpoint.', exampleValue: 'https://example.com/handler/magic-link-callback' } });
|
package/dist/utils/arrays.d.ts
CHANGED
|
@@ -13,3 +13,4 @@ export declare function rotateLeft(arr: readonly any[], n: number): any[];
|
|
|
13
13
|
export declare function rotateRight(arr: readonly any[], n: number): any[];
|
|
14
14
|
export declare function shuffle<T>(arr: readonly T[]): T[];
|
|
15
15
|
export declare function outerProduct<T, U>(arr1: readonly T[], arr2: readonly U[]): [T, U][];
|
|
16
|
+
export declare function unique<T>(arr: readonly T[]): T[];
|
package/dist/utils/arrays.js
CHANGED
package/dist/utils/crypto.d.ts
CHANGED
package/dist/utils/crypto.js
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
import { encodeBase32 } from "./bytes";
|
|
2
|
+
import { StackAssertionError } from "./errors";
|
|
2
3
|
import { globalVar } from "./globals";
|
|
4
|
+
export function generateRandomValues(array) {
|
|
5
|
+
if (!globalVar.crypto) {
|
|
6
|
+
throw new StackAssertionError("Crypto API is not available in this environment. Are you using an old browser?");
|
|
7
|
+
}
|
|
8
|
+
if (!globalVar.crypto.getRandomValues) {
|
|
9
|
+
throw new StackAssertionError("crypto.getRandomValues is not available in this environment. Are you using an old browser?");
|
|
10
|
+
}
|
|
11
|
+
return globalVar.crypto.getRandomValues(array);
|
|
12
|
+
}
|
|
3
13
|
/**
|
|
4
14
|
* Generates a secure alphanumeric string using the system's cryptographically secure
|
|
5
15
|
* random number generator.
|
|
@@ -7,7 +17,7 @@ import { globalVar } from "./globals";
|
|
|
7
17
|
export function generateSecureRandomString(minBitsOfEntropy = 224) {
|
|
8
18
|
const base32CharactersCount = Math.ceil(minBitsOfEntropy / 5);
|
|
9
19
|
const bytesCount = Math.ceil(base32CharactersCount * 5 / 8);
|
|
10
|
-
const randomBytes =
|
|
20
|
+
const randomBytes = generateRandomValues(new Uint8Array(bytesCount));
|
|
11
21
|
const str = encodeBase32(randomBytes);
|
|
12
22
|
return str.slice(str.length - base32CharactersCount).toLowerCase();
|
|
13
23
|
}
|
package/dist/utils/results.d.ts
CHANGED
|
@@ -12,6 +12,7 @@ export type AsyncResult<T, E = unknown, P = void> = Result<T, E> | ({
|
|
|
12
12
|
});
|
|
13
13
|
export declare const Result: {
|
|
14
14
|
fromThrowing: typeof fromThrowing;
|
|
15
|
+
fromThrowingAsync: typeof fromThrowingAsync;
|
|
15
16
|
fromPromise: typeof promiseToResult;
|
|
16
17
|
ok<T>(data: T): {
|
|
17
18
|
status: "ok";
|
|
@@ -60,6 +61,7 @@ declare function pending<P>(progress: P): AsyncResult<never, never, P> & {
|
|
|
60
61
|
};
|
|
61
62
|
declare function promiseToResult<T>(promise: Promise<T>): Promise<Result<T>>;
|
|
62
63
|
declare function fromThrowing<T>(fn: () => T): Result<T, unknown>;
|
|
64
|
+
declare function fromThrowingAsync<T>(fn: () => Promise<T>): Promise<Result<T, unknown>>;
|
|
63
65
|
declare function mapResult<T, U, E = unknown, P = unknown>(result: Result<T, E>, fn: (data: T) => U): Result<U, E>;
|
|
64
66
|
declare function mapResult<T, U, E = unknown, P = unknown>(result: AsyncResult<T, E, P>, fn: (data: T) => U): AsyncResult<U, E, P>;
|
|
65
67
|
declare class RetryError extends AggregateError {
|
package/dist/utils/results.js
CHANGED
|
@@ -2,6 +2,7 @@ import { wait } from "./promises";
|
|
|
2
2
|
import { deindent } from "./strings";
|
|
3
3
|
export const Result = {
|
|
4
4
|
fromThrowing,
|
|
5
|
+
fromThrowingAsync,
|
|
5
6
|
fromPromise: promiseToResult,
|
|
6
7
|
ok(data) {
|
|
7
8
|
return {
|
|
@@ -71,6 +72,14 @@ function fromThrowing(fn) {
|
|
|
71
72
|
return Result.error(error);
|
|
72
73
|
}
|
|
73
74
|
}
|
|
75
|
+
async function fromThrowingAsync(fn) {
|
|
76
|
+
try {
|
|
77
|
+
return Result.ok(await fn());
|
|
78
|
+
}
|
|
79
|
+
catch (error) {
|
|
80
|
+
return Result.error(error);
|
|
81
|
+
}
|
|
82
|
+
}
|
|
74
83
|
function mapResult(result, fn) {
|
|
75
84
|
if (result.status === "error")
|
|
76
85
|
return {
|
package/dist/utils/strings.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { findLastIndex } from "./arrays";
|
|
1
|
+
import { findLastIndex, unique } from "./arrays";
|
|
2
2
|
import { StackAssertionError } from "./errors";
|
|
3
3
|
import { filterUndefined } from "./objects";
|
|
4
4
|
export function typedToLowercase(s) {
|
|
@@ -207,6 +207,9 @@ export function nicify(value, options = {}) {
|
|
|
207
207
|
return `[${resValues.join(", ")}]`;
|
|
208
208
|
}
|
|
209
209
|
}
|
|
210
|
+
if (value instanceof URL) {
|
|
211
|
+
return `URL(${JSON.stringify(value.toString())})`;
|
|
212
|
+
}
|
|
210
213
|
const constructorName = [null, Object.prototype].includes(Object.getPrototypeOf(value)) ? null : (nicifiableClassNameOverrides.get(value.constructor) ?? value.constructor.name);
|
|
211
214
|
const constructorString = constructorName ? `${nicifyPropertyString(constructorName)} ` : "";
|
|
212
215
|
const entries = getNicifiableEntries(value).filter(([k]) => !hideFields.includes(k));
|
|
@@ -256,7 +259,16 @@ function nicifyPropertyString(str) {
|
|
|
256
259
|
return JSON.stringify(str);
|
|
257
260
|
}
|
|
258
261
|
function getNicifiableKeys(value) {
|
|
259
|
-
|
|
262
|
+
const overridden = ("getNicifiableKeys" in value ? value.getNicifiableKeys?.bind(value) : null)?.();
|
|
263
|
+
if (overridden != null)
|
|
264
|
+
return overridden;
|
|
265
|
+
const keys = Object.keys(value).sort();
|
|
266
|
+
if (value instanceof Error) {
|
|
267
|
+
if (value.cause)
|
|
268
|
+
keys.unshift("cause");
|
|
269
|
+
keys.unshift("message", "stack");
|
|
270
|
+
}
|
|
271
|
+
return unique(keys);
|
|
260
272
|
}
|
|
261
273
|
function getNicifiableEntries(value) {
|
|
262
274
|
const recordLikes = [Headers];
|
package/dist/utils/uuids.js
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { generateRandomValues } from "./crypto";
|
|
2
2
|
export function generateUuid() {
|
|
3
3
|
// crypto.randomUuid is not supported in all browsers, so this is a polyfill
|
|
4
|
-
return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c => (+c ^
|
|
4
|
+
return "10000000-1000-4000-8000-100000000000".replace(/[018]/g, c => (+c ^ generateRandomValues(new Uint8Array(1))[0] & 15 >> +c / 4).toString(16));
|
|
5
5
|
}
|
|
6
6
|
export function isUuid(str) {
|
|
7
7
|
return /^[0-9a-f]{8}-[0-9a-f]{4}-4[0-9a-f]{3}-[89ab][0-9a-f]{3}-[0-9a-f]{12}$/.test(str);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@stackframe/stack-shared",
|
|
3
|
-
"version": "2.5.
|
|
3
|
+
"version": "2.5.17",
|
|
4
4
|
"main": "./dist/index.js",
|
|
5
5
|
"types": "./dist/index.d.ts",
|
|
6
6
|
"files": [
|
|
@@ -36,7 +36,7 @@
|
|
|
36
36
|
"jose": "^5.2.2",
|
|
37
37
|
"oauth4webapi": "^2.10.3",
|
|
38
38
|
"uuid": "^9.0.1",
|
|
39
|
-
"@stackframe/stack-sc": "2.5.
|
|
39
|
+
"@stackframe/stack-sc": "2.5.17"
|
|
40
40
|
},
|
|
41
41
|
"devDependencies": {
|
|
42
42
|
"rimraf": "^5.0.5",
|