@stackframe/stack-shared 2.8.1 → 2.8.3
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/interface/adminInterface.d.ts +7 -7
- package/dist/interface/adminInterface.js +4 -4
- package/dist/interface/clientInterface.d.ts +43 -1
- package/dist/interface/clientInterface.js +67 -3
- package/dist/interface/crud/current-user.d.ts +1 -1
- package/dist/interface/crud/{api-keys.d.ts → internal-api-keys.d.ts} +7 -7
- package/dist/interface/crud/{api-keys.js → internal-api-keys.js} +10 -10
- package/dist/interface/crud/project-api-keys.d.ts +188 -0
- package/dist/interface/crud/project-api-keys.js +76 -0
- package/dist/interface/crud/projects.d.ts +28 -0
- package/dist/interface/crud/projects.js +6 -0
- package/dist/interface/crud/team-member-profiles.d.ts +2 -2
- package/dist/interface/crud/users.d.ts +4 -4
- package/dist/interface/serverInterface.d.ts +2 -1
- package/dist/interface/serverInterface.js +4 -0
- package/dist/interface/webhooks.d.ts +34 -2
- package/dist/interface/webhooks.js +3 -0
- package/dist/known-errors.d.ts +24 -1
- package/dist/known-errors.js +30 -4
- package/dist/schema-fields.d.ts +1 -1
- package/dist/schema-fields.js +2 -1
- package/dist/utils/api-keys.d.ts +23 -0
- package/dist/utils/api-keys.js +75 -0
- package/dist/utils/bytes.d.ts +3 -0
- package/dist/utils/bytes.js +55 -6
- package/dist/utils/errors.js +1 -5
- package/dist/utils/hashes.d.ts +1 -1
- package/dist/utils/hashes.js +1 -3
- package/dist/utils/objects.d.ts +8 -0
- package/dist/utils/objects.js +11 -0
- package/dist/utils/results.js +5 -5
- package/dist/utils/strings.d.ts +1 -0
- package/dist/utils/strings.js +73 -17
- package/dist/utils/strings.nicify.test.js +214 -0
- package/dist/utils/types.d.ts +17 -0
- package/package.json +2 -1
- package/dist/utils/strings.test.js +0 -26
- /package/dist/utils/{strings.test.d.ts → strings.nicify.test.d.ts} +0 -0
|
@@ -48,6 +48,8 @@ export declare const projectsCrudAdminReadSchema: import("yup").ObjectSchema<{
|
|
|
48
48
|
passkey_enabled: boolean;
|
|
49
49
|
client_team_creation_enabled: boolean;
|
|
50
50
|
client_user_deletion_enabled: boolean;
|
|
51
|
+
allow_user_api_keys: boolean;
|
|
52
|
+
allow_team_api_keys: boolean;
|
|
51
53
|
oauth_providers: {
|
|
52
54
|
client_id?: string | undefined;
|
|
53
55
|
client_secret?: string | undefined;
|
|
@@ -101,6 +103,8 @@ export declare const projectsCrudAdminReadSchema: import("yup").ObjectSchema<{
|
|
|
101
103
|
passkey_enabled: undefined;
|
|
102
104
|
client_team_creation_enabled: undefined;
|
|
103
105
|
client_user_deletion_enabled: undefined;
|
|
106
|
+
allow_user_api_keys: undefined;
|
|
107
|
+
allow_team_api_keys: undefined;
|
|
104
108
|
oauth_providers: undefined;
|
|
105
109
|
enabled_oauth_providers: undefined;
|
|
106
110
|
domains: undefined;
|
|
@@ -130,6 +134,8 @@ export declare const projectsCrudClientReadSchema: import("yup").ObjectSchema<{
|
|
|
130
134
|
passkey_enabled: boolean;
|
|
131
135
|
client_team_creation_enabled: boolean;
|
|
132
136
|
client_user_deletion_enabled: boolean;
|
|
137
|
+
allow_user_api_keys: boolean;
|
|
138
|
+
allow_team_api_keys: boolean;
|
|
133
139
|
enabled_oauth_providers: {
|
|
134
140
|
id: "google" | "github" | "microsoft" | "spotify" | "facebook" | "discord" | "gitlab" | "bitbucket" | "linkedin" | "apple" | "x";
|
|
135
141
|
}[];
|
|
@@ -144,6 +150,8 @@ export declare const projectsCrudClientReadSchema: import("yup").ObjectSchema<{
|
|
|
144
150
|
passkey_enabled: undefined;
|
|
145
151
|
client_team_creation_enabled: undefined;
|
|
146
152
|
client_user_deletion_enabled: undefined;
|
|
153
|
+
allow_user_api_keys: undefined;
|
|
154
|
+
allow_team_api_keys: undefined;
|
|
147
155
|
enabled_oauth_providers: undefined;
|
|
148
156
|
};
|
|
149
157
|
}, "">;
|
|
@@ -159,6 +167,8 @@ export declare const projectsCrudAdminUpdateSchema: import("yup").ObjectSchema<{
|
|
|
159
167
|
passkey_enabled?: boolean | undefined;
|
|
160
168
|
client_team_creation_enabled?: boolean | undefined;
|
|
161
169
|
client_user_deletion_enabled?: boolean | undefined;
|
|
170
|
+
allow_user_api_keys?: boolean | undefined;
|
|
171
|
+
allow_team_api_keys?: boolean | undefined;
|
|
162
172
|
oauth_providers?: {
|
|
163
173
|
client_id?: string | undefined;
|
|
164
174
|
client_secret?: string | undefined;
|
|
@@ -211,6 +221,8 @@ export declare const projectsCrudAdminCreateSchema: import("yup").ObjectSchema<{
|
|
|
211
221
|
passkey_enabled?: boolean | undefined;
|
|
212
222
|
client_team_creation_enabled?: boolean | undefined;
|
|
213
223
|
client_user_deletion_enabled?: boolean | undefined;
|
|
224
|
+
allow_user_api_keys?: boolean | undefined;
|
|
225
|
+
allow_team_api_keys?: boolean | undefined;
|
|
214
226
|
oauth_providers?: {
|
|
215
227
|
client_id?: string | undefined;
|
|
216
228
|
client_secret?: string | undefined;
|
|
@@ -265,6 +277,8 @@ export declare const projectsCrud: import("../../crud").CrudSchemaFromOptions<{
|
|
|
265
277
|
passkey_enabled: boolean;
|
|
266
278
|
client_team_creation_enabled: boolean;
|
|
267
279
|
client_user_deletion_enabled: boolean;
|
|
280
|
+
allow_user_api_keys: boolean;
|
|
281
|
+
allow_team_api_keys: boolean;
|
|
268
282
|
enabled_oauth_providers: {
|
|
269
283
|
id: "google" | "github" | "microsoft" | "spotify" | "facebook" | "discord" | "gitlab" | "bitbucket" | "linkedin" | "apple" | "x";
|
|
270
284
|
}[];
|
|
@@ -279,6 +293,8 @@ export declare const projectsCrud: import("../../crud").CrudSchemaFromOptions<{
|
|
|
279
293
|
passkey_enabled: undefined;
|
|
280
294
|
client_team_creation_enabled: undefined;
|
|
281
295
|
client_user_deletion_enabled: undefined;
|
|
296
|
+
allow_user_api_keys: undefined;
|
|
297
|
+
allow_team_api_keys: undefined;
|
|
282
298
|
enabled_oauth_providers: undefined;
|
|
283
299
|
};
|
|
284
300
|
}, "">;
|
|
@@ -298,6 +314,8 @@ export declare const projectsCrud: import("../../crud").CrudSchemaFromOptions<{
|
|
|
298
314
|
passkey_enabled: boolean;
|
|
299
315
|
client_team_creation_enabled: boolean;
|
|
300
316
|
client_user_deletion_enabled: boolean;
|
|
317
|
+
allow_user_api_keys: boolean;
|
|
318
|
+
allow_team_api_keys: boolean;
|
|
301
319
|
oauth_providers: {
|
|
302
320
|
client_id?: string | undefined;
|
|
303
321
|
client_secret?: string | undefined;
|
|
@@ -351,6 +369,8 @@ export declare const projectsCrud: import("../../crud").CrudSchemaFromOptions<{
|
|
|
351
369
|
passkey_enabled: undefined;
|
|
352
370
|
client_team_creation_enabled: undefined;
|
|
353
371
|
client_user_deletion_enabled: undefined;
|
|
372
|
+
allow_user_api_keys: undefined;
|
|
373
|
+
allow_team_api_keys: undefined;
|
|
354
374
|
oauth_providers: undefined;
|
|
355
375
|
enabled_oauth_providers: undefined;
|
|
356
376
|
domains: undefined;
|
|
@@ -382,6 +402,8 @@ export declare const projectsCrud: import("../../crud").CrudSchemaFromOptions<{
|
|
|
382
402
|
passkey_enabled?: boolean | undefined;
|
|
383
403
|
client_team_creation_enabled?: boolean | undefined;
|
|
384
404
|
client_user_deletion_enabled?: boolean | undefined;
|
|
405
|
+
allow_user_api_keys?: boolean | undefined;
|
|
406
|
+
allow_team_api_keys?: boolean | undefined;
|
|
385
407
|
oauth_providers?: {
|
|
386
408
|
client_id?: string | undefined;
|
|
387
409
|
client_secret?: string | undefined;
|
|
@@ -464,6 +486,8 @@ export declare const internalProjectsCrud: import("../../crud").CrudSchemaFromOp
|
|
|
464
486
|
passkey_enabled: boolean;
|
|
465
487
|
client_team_creation_enabled: boolean;
|
|
466
488
|
client_user_deletion_enabled: boolean;
|
|
489
|
+
allow_user_api_keys: boolean;
|
|
490
|
+
allow_team_api_keys: boolean;
|
|
467
491
|
oauth_providers: {
|
|
468
492
|
client_id?: string | undefined;
|
|
469
493
|
client_secret?: string | undefined;
|
|
@@ -517,6 +541,8 @@ export declare const internalProjectsCrud: import("../../crud").CrudSchemaFromOp
|
|
|
517
541
|
passkey_enabled: undefined;
|
|
518
542
|
client_team_creation_enabled: undefined;
|
|
519
543
|
client_user_deletion_enabled: undefined;
|
|
544
|
+
allow_user_api_keys: undefined;
|
|
545
|
+
allow_team_api_keys: undefined;
|
|
520
546
|
oauth_providers: undefined;
|
|
521
547
|
enabled_oauth_providers: undefined;
|
|
522
548
|
domains: undefined;
|
|
@@ -548,6 +574,8 @@ export declare const internalProjectsCrud: import("../../crud").CrudSchemaFromOp
|
|
|
548
574
|
passkey_enabled?: boolean | undefined;
|
|
549
575
|
client_team_creation_enabled?: boolean | undefined;
|
|
550
576
|
client_user_deletion_enabled?: boolean | undefined;
|
|
577
|
+
allow_user_api_keys?: boolean | undefined;
|
|
578
|
+
allow_team_api_keys?: boolean | undefined;
|
|
551
579
|
oauth_providers?: {
|
|
552
580
|
client_id?: string | undefined;
|
|
553
581
|
client_secret?: string | undefined;
|
|
@@ -68,6 +68,8 @@ export const projectsCrudAdminReadSchema = yupObject({
|
|
|
68
68
|
// TODO: remove this
|
|
69
69
|
client_team_creation_enabled: schemaFields.projectClientTeamCreationEnabledSchema.defined(),
|
|
70
70
|
client_user_deletion_enabled: schemaFields.projectClientUserDeletionEnabledSchema.defined(),
|
|
71
|
+
allow_user_api_keys: schemaFields.yupBoolean().defined(),
|
|
72
|
+
allow_team_api_keys: schemaFields.yupBoolean().defined(),
|
|
71
73
|
oauth_providers: yupArray(oauthProviderSchema.defined()).defined(),
|
|
72
74
|
enabled_oauth_providers: yupArray(enabledOAuthProviderSchema.defined()).defined().meta({ openapiField: { hidden: true } }),
|
|
73
75
|
domains: yupArray(domainSchema.defined()).defined(),
|
|
@@ -89,6 +91,8 @@ export const projectsCrudClientReadSchema = yupObject({
|
|
|
89
91
|
passkey_enabled: schemaFields.projectPasskeyEnabledSchema.defined(),
|
|
90
92
|
client_team_creation_enabled: schemaFields.projectClientTeamCreationEnabledSchema.defined(),
|
|
91
93
|
client_user_deletion_enabled: schemaFields.projectClientUserDeletionEnabledSchema.defined(),
|
|
94
|
+
allow_user_api_keys: schemaFields.yupBoolean().defined(),
|
|
95
|
+
allow_team_api_keys: schemaFields.yupBoolean().defined(),
|
|
92
96
|
enabled_oauth_providers: yupArray(enabledOAuthProviderSchema.defined()).defined().meta({ openapiField: { hidden: true } }),
|
|
93
97
|
}).defined(),
|
|
94
98
|
}).defined();
|
|
@@ -104,6 +108,8 @@ export const projectsCrudAdminUpdateSchema = yupObject({
|
|
|
104
108
|
client_team_creation_enabled: schemaFields.projectClientTeamCreationEnabledSchema.optional(),
|
|
105
109
|
client_user_deletion_enabled: schemaFields.projectClientUserDeletionEnabledSchema.optional(),
|
|
106
110
|
allow_localhost: schemaFields.projectAllowLocalhostSchema.optional(),
|
|
111
|
+
allow_user_api_keys: schemaFields.yupBoolean().optional(),
|
|
112
|
+
allow_team_api_keys: schemaFields.yupBoolean().optional(),
|
|
107
113
|
email_config: emailConfigSchema.optional().default(undefined),
|
|
108
114
|
domains: yupArray(domainSchema.defined()).optional().default(undefined),
|
|
109
115
|
oauth_providers: yupArray(oauthProviderSchema.defined()).optional().default(undefined),
|
|
@@ -40,8 +40,8 @@ export declare const teamMemberProfilesCrudServerReadSchema: import("yup").Objec
|
|
|
40
40
|
client_read_only_metadata?: {} | null | undefined;
|
|
41
41
|
server_metadata?: {} | null | undefined;
|
|
42
42
|
id: string;
|
|
43
|
-
created_at_millis: number;
|
|
44
43
|
display_name: string;
|
|
44
|
+
created_at_millis: number;
|
|
45
45
|
profile_image_url: string | null;
|
|
46
46
|
} | null;
|
|
47
47
|
signed_up_at_millis: number;
|
|
@@ -135,8 +135,8 @@ export declare const teamMemberProfilesCrud: import("../../crud").CrudSchemaFrom
|
|
|
135
135
|
client_read_only_metadata?: {} | null | undefined;
|
|
136
136
|
server_metadata?: {} | null | undefined;
|
|
137
137
|
id: string;
|
|
138
|
-
created_at_millis: number;
|
|
139
138
|
display_name: string;
|
|
139
|
+
created_at_millis: number;
|
|
140
140
|
profile_image_url: string | null;
|
|
141
141
|
} | null;
|
|
142
142
|
signed_up_at_millis: number;
|
|
@@ -43,8 +43,8 @@ export declare const usersCrudServerReadSchema: import("yup").ObjectSchema<{
|
|
|
43
43
|
client_read_only_metadata?: {} | null | undefined;
|
|
44
44
|
server_metadata?: {} | null | undefined;
|
|
45
45
|
id: string;
|
|
46
|
-
created_at_millis: number;
|
|
47
46
|
display_name: string;
|
|
47
|
+
created_at_millis: number;
|
|
48
48
|
profile_image_url: string | null;
|
|
49
49
|
} | null;
|
|
50
50
|
selected_team_id: string | null;
|
|
@@ -148,8 +148,8 @@ export declare const usersCrud: import("../../crud").CrudSchemaFromOptions<{
|
|
|
148
148
|
client_read_only_metadata?: {} | null | undefined;
|
|
149
149
|
server_metadata?: {} | null | undefined;
|
|
150
150
|
id: string;
|
|
151
|
-
created_at_millis: number;
|
|
152
151
|
display_name: string;
|
|
152
|
+
created_at_millis: number;
|
|
153
153
|
profile_image_url: string | null;
|
|
154
154
|
} | null;
|
|
155
155
|
selected_team_id: string | null;
|
|
@@ -316,8 +316,8 @@ export declare const userCreatedWebhookEvent: {
|
|
|
316
316
|
client_read_only_metadata?: {} | null | undefined;
|
|
317
317
|
server_metadata?: {} | null | undefined;
|
|
318
318
|
id: string;
|
|
319
|
-
created_at_millis: number;
|
|
320
319
|
display_name: string;
|
|
320
|
+
created_at_millis: number;
|
|
321
321
|
profile_image_url: string | null;
|
|
322
322
|
} | null;
|
|
323
323
|
selected_team_id: string | null;
|
|
@@ -387,8 +387,8 @@ export declare const userUpdatedWebhookEvent: {
|
|
|
387
387
|
client_read_only_metadata?: {} | null | undefined;
|
|
388
388
|
server_metadata?: {} | null | undefined;
|
|
389
389
|
id: string;
|
|
390
|
-
created_at_millis: number;
|
|
391
390
|
display_name: string;
|
|
391
|
+
created_at_millis: number;
|
|
392
392
|
profile_image_url: string | null;
|
|
393
393
|
} | null;
|
|
394
394
|
selected_team_id: string | null;
|
|
@@ -5,11 +5,11 @@ import { ClientInterfaceOptions, StackClientInterface } from "./clientInterface"
|
|
|
5
5
|
import { ContactChannelsCrud } from "./crud/contact-channels";
|
|
6
6
|
import { CurrentUserCrud } from "./crud/current-user";
|
|
7
7
|
import { ConnectedAccountAccessTokenCrud } from "./crud/oauth";
|
|
8
|
+
import { ProjectPermissionsCrud } from "./crud/project-permissions";
|
|
8
9
|
import { SessionsCrud } from "./crud/sessions";
|
|
9
10
|
import { TeamInvitationCrud } from "./crud/team-invitation";
|
|
10
11
|
import { TeamMemberProfilesCrud } from "./crud/team-member-profiles";
|
|
11
12
|
import { TeamMembershipsCrud } from "./crud/team-memberships";
|
|
12
|
-
import { ProjectPermissionsCrud } from "./crud/project-permissions";
|
|
13
13
|
import { TeamPermissionsCrud } from "./crud/team-permissions";
|
|
14
14
|
import { TeamsCrud } from "./crud/teams";
|
|
15
15
|
import { UsersCrud } from "./crud/users";
|
|
@@ -66,6 +66,7 @@ export declare class StackServerInterface extends StackClientInterface {
|
|
|
66
66
|
listServerTeams(options?: {
|
|
67
67
|
userId?: string;
|
|
68
68
|
}): Promise<TeamsCrud['Server']['Read'][]>;
|
|
69
|
+
getServerTeam(teamId: string): Promise<TeamsCrud['Server']['Read']>;
|
|
69
70
|
listServerTeamUsers(teamId: string): Promise<UsersCrud['Server']['Read'][]>;
|
|
70
71
|
createServerTeam(data: TeamsCrud['Server']['Create']): Promise<TeamsCrud['Server']['Read']>;
|
|
71
72
|
updateServerTeam(teamId: string, data: TeamsCrud['Server']['Update']): Promise<TeamsCrud['Server']['Read']>;
|
|
@@ -123,6 +123,10 @@ export class StackServerInterface extends StackClientInterface {
|
|
|
123
123
|
const result = await response.json();
|
|
124
124
|
return result.items;
|
|
125
125
|
}
|
|
126
|
+
async getServerTeam(teamId) {
|
|
127
|
+
const response = await this.sendServerRequest(`/teams/${teamId}`, {}, null);
|
|
128
|
+
return await response.json();
|
|
129
|
+
}
|
|
126
130
|
async listServerTeamUsers(teamId) {
|
|
127
131
|
const response = await this.sendServerRequest(`/users?team_id=${teamId}`, {}, null);
|
|
128
132
|
const result = await response.json();
|
|
@@ -21,8 +21,8 @@ export declare const webhookEvents: readonly [{
|
|
|
21
21
|
client_read_only_metadata?: {} | null | undefined;
|
|
22
22
|
server_metadata?: {} | null | undefined;
|
|
23
23
|
id: string;
|
|
24
|
-
created_at_millis: number;
|
|
25
24
|
display_name: string;
|
|
25
|
+
created_at_millis: number;
|
|
26
26
|
profile_image_url: string | null;
|
|
27
27
|
} | null;
|
|
28
28
|
selected_team_id: string | null;
|
|
@@ -91,8 +91,8 @@ export declare const webhookEvents: readonly [{
|
|
|
91
91
|
client_read_only_metadata?: {} | null | undefined;
|
|
92
92
|
server_metadata?: {} | null | undefined;
|
|
93
93
|
id: string;
|
|
94
|
-
created_at_millis: number;
|
|
95
94
|
display_name: string;
|
|
95
|
+
created_at_millis: number;
|
|
96
96
|
profile_image_url: string | null;
|
|
97
97
|
} | null;
|
|
98
98
|
selected_team_id: string | null;
|
|
@@ -254,4 +254,36 @@ export declare const webhookEvents: readonly [{
|
|
|
254
254
|
description: string;
|
|
255
255
|
tags: string[];
|
|
256
256
|
};
|
|
257
|
+
}, {
|
|
258
|
+
type: string;
|
|
259
|
+
schema: yup.ObjectSchema<{
|
|
260
|
+
id: string;
|
|
261
|
+
user_id: string;
|
|
262
|
+
team_id: string;
|
|
263
|
+
}, yup.AnyObject, {
|
|
264
|
+
id: undefined;
|
|
265
|
+
user_id: undefined;
|
|
266
|
+
team_id: undefined;
|
|
267
|
+
}, "">;
|
|
268
|
+
metadata: {
|
|
269
|
+
summary: string;
|
|
270
|
+
description: string;
|
|
271
|
+
tags: string[];
|
|
272
|
+
};
|
|
273
|
+
}, {
|
|
274
|
+
type: string;
|
|
275
|
+
schema: yup.ObjectSchema<{
|
|
276
|
+
id: string;
|
|
277
|
+
user_id: string;
|
|
278
|
+
team_id: string;
|
|
279
|
+
}, yup.AnyObject, {
|
|
280
|
+
id: undefined;
|
|
281
|
+
user_id: undefined;
|
|
282
|
+
team_id: undefined;
|
|
283
|
+
}, "">;
|
|
284
|
+
metadata: {
|
|
285
|
+
summary: string;
|
|
286
|
+
description: string;
|
|
287
|
+
tags: string[];
|
|
288
|
+
};
|
|
257
289
|
}];
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { teamMembershipCreatedWebhookEvent, teamMembershipDeletedWebhookEvent } from "./crud/team-memberships";
|
|
2
|
+
import { teamPermissionCreatedWebhookEvent, teamPermissionDeletedWebhookEvent } from "./crud/team-permissions";
|
|
2
3
|
import { teamCreatedWebhookEvent, teamDeletedWebhookEvent, teamUpdatedWebhookEvent } from "./crud/teams";
|
|
3
4
|
import { userCreatedWebhookEvent, userDeletedWebhookEvent, userUpdatedWebhookEvent } from "./crud/users";
|
|
4
5
|
export const webhookEvents = [
|
|
@@ -10,4 +11,6 @@ export const webhookEvents = [
|
|
|
10
11
|
teamDeletedWebhookEvent,
|
|
11
12
|
teamMembershipCreatedWebhookEvent,
|
|
12
13
|
teamMembershipDeletedWebhookEvent,
|
|
14
|
+
teamPermissionCreatedWebhookEvent,
|
|
15
|
+
teamPermissionDeletedWebhookEvent,
|
|
13
16
|
];
|
package/dist/known-errors.d.ts
CHANGED
|
@@ -66,6 +66,9 @@ export declare const KnownErrors: {
|
|
|
66
66
|
ProjectAuthenticationError: KnownErrorConstructor<KnownError & KnownErrorBrand<"PROJECT_AUTHENTICATION_ERROR">, [statusCode: number, humanReadableMessage: string, details?: Json | undefined]> & {
|
|
67
67
|
errorCode: "PROJECT_AUTHENTICATION_ERROR";
|
|
68
68
|
};
|
|
69
|
+
PermissionIdAlreadyExists: KnownErrorConstructor<KnownError & KnownErrorBrand<"PERMISSION_ID_ALREADY_EXISTS">, [permissionId: string]> & {
|
|
70
|
+
errorCode: "PERMISSION_ID_ALREADY_EXISTS";
|
|
71
|
+
};
|
|
69
72
|
InvalidProjectAuthentication: KnownErrorConstructor<KnownError & KnownErrorBrand<"PROJECT_AUTHENTICATION_ERROR"> & {
|
|
70
73
|
constructorArgs: [statusCode: number, humanReadableMessage: string, details?: Json | undefined];
|
|
71
74
|
} & KnownErrorBrand<"INVALID_PROJECT_AUTHENTICATION">, [statusCode: number, humanReadableMessage: string, details?: Json | undefined]> & {
|
|
@@ -232,7 +235,9 @@ export declare const KnownErrors: {
|
|
|
232
235
|
UserNotFound: KnownErrorConstructor<KnownError & KnownErrorBrand<"USER_NOT_FOUND">, []> & {
|
|
233
236
|
errorCode: "USER_NOT_FOUND";
|
|
234
237
|
};
|
|
235
|
-
ApiKeyNotFound: KnownErrorConstructor<KnownError & KnownErrorBrand<"
|
|
238
|
+
ApiKeyNotFound: KnownErrorConstructor<KnownError & KnownErrorBrand<"API_KEY_NOT_VALID"> & {
|
|
239
|
+
constructorArgs: [statusCode: number, humanReadableMessage: string, details?: Json | undefined];
|
|
240
|
+
} & KnownErrorBrand<"API_KEY_NOT_FOUND">, []> & {
|
|
236
241
|
errorCode: "API_KEY_NOT_FOUND";
|
|
237
242
|
};
|
|
238
243
|
ProjectNotFound: KnownErrorConstructor<KnownError & KnownErrorBrand<"PROJECT_NOT_FOUND">, [projectId: string]> & {
|
|
@@ -400,5 +405,23 @@ export declare const KnownErrors: {
|
|
|
400
405
|
InvalidPollingCodeError: KnownErrorConstructor<KnownError & KnownErrorBrand<"INVALID_POLLING_CODE">, [details?: Json | undefined]> & {
|
|
401
406
|
errorCode: "INVALID_POLLING_CODE";
|
|
402
407
|
};
|
|
408
|
+
ApiKeyNotValid: KnownErrorConstructor<KnownError & KnownErrorBrand<"API_KEY_NOT_VALID">, [statusCode: number, humanReadableMessage: string, details?: Json | undefined]> & {
|
|
409
|
+
errorCode: "API_KEY_NOT_VALID";
|
|
410
|
+
};
|
|
411
|
+
ApiKeyExpired: KnownErrorConstructor<KnownError & KnownErrorBrand<"API_KEY_NOT_VALID"> & {
|
|
412
|
+
constructorArgs: [statusCode: number, humanReadableMessage: string, details?: Json | undefined];
|
|
413
|
+
} & KnownErrorBrand<"API_KEY_EXPIRED">, []> & {
|
|
414
|
+
errorCode: "API_KEY_EXPIRED";
|
|
415
|
+
};
|
|
416
|
+
ApiKeyRevoked: KnownErrorConstructor<KnownError & KnownErrorBrand<"API_KEY_NOT_VALID"> & {
|
|
417
|
+
constructorArgs: [statusCode: number, humanReadableMessage: string, details?: Json | undefined];
|
|
418
|
+
} & KnownErrorBrand<"API_KEY_REVOKED">, []> & {
|
|
419
|
+
errorCode: "API_KEY_REVOKED";
|
|
420
|
+
};
|
|
421
|
+
WrongApiKeyType: KnownErrorConstructor<KnownError & KnownErrorBrand<"API_KEY_NOT_VALID"> & {
|
|
422
|
+
constructorArgs: [statusCode: number, humanReadableMessage: string, details?: Json | undefined];
|
|
423
|
+
} & KnownErrorBrand<"WRONG_API_KEY_TYPE">, [expectedType: string, actualType: string]> & {
|
|
424
|
+
errorCode: "WRONG_API_KEY_TYPE";
|
|
425
|
+
};
|
|
403
426
|
};
|
|
404
427
|
export {};
|
package/dist/known-errors.js
CHANGED
|
@@ -293,10 +293,6 @@ const UserNotFound = createKnownErrorConstructor(KnownError, "USER_NOT_FOUND", (
|
|
|
293
293
|
404,
|
|
294
294
|
"User not found.",
|
|
295
295
|
], () => []);
|
|
296
|
-
const ApiKeyNotFound = createKnownErrorConstructor(KnownError, "API_KEY_NOT_FOUND", () => [
|
|
297
|
-
404,
|
|
298
|
-
"API key not found.",
|
|
299
|
-
], () => []);
|
|
300
296
|
const ProjectNotFound = createKnownErrorConstructor(KnownError, "PROJECT_NOT_FOUND", (projectId) => {
|
|
301
297
|
if (typeof projectId !== "string")
|
|
302
298
|
throw new StackAssertionError("projectId of KnownErrors.ProjectNotFound must be a string");
|
|
@@ -570,6 +566,31 @@ const InvalidPollingCodeError = createKnownErrorConstructor(KnownError, "INVALID
|
|
|
570
566
|
"The polling code is invalid or does not exist.",
|
|
571
567
|
details,
|
|
572
568
|
], (json) => [json]);
|
|
569
|
+
const ApiKeyNotValid = createKnownErrorConstructor(KnownError, "API_KEY_NOT_VALID", "inherit", "inherit");
|
|
570
|
+
const ApiKeyExpired = createKnownErrorConstructor(ApiKeyNotValid, "API_KEY_EXPIRED", () => [
|
|
571
|
+
401,
|
|
572
|
+
"API key has expired.",
|
|
573
|
+
], () => []);
|
|
574
|
+
const ApiKeyRevoked = createKnownErrorConstructor(ApiKeyNotValid, "API_KEY_REVOKED", () => [
|
|
575
|
+
401,
|
|
576
|
+
"API key has been revoked.",
|
|
577
|
+
], () => []);
|
|
578
|
+
const WrongApiKeyType = createKnownErrorConstructor(ApiKeyNotValid, "WRONG_API_KEY_TYPE", (expectedType, actualType) => [
|
|
579
|
+
400,
|
|
580
|
+
`This endpoint is for ${expectedType} API keys, but a ${actualType} API key was provided.`,
|
|
581
|
+
{ expected_type: expectedType, actual_type: actualType },
|
|
582
|
+
], (json) => [json.expected_type, json.actual_type]);
|
|
583
|
+
const ApiKeyNotFound = createKnownErrorConstructor(ApiKeyNotValid, "API_KEY_NOT_FOUND", () => [
|
|
584
|
+
404,
|
|
585
|
+
"API key not found.",
|
|
586
|
+
], () => []);
|
|
587
|
+
const PermissionIdAlreadyExists = createKnownErrorConstructor(KnownError, "PERMISSION_ID_ALREADY_EXISTS", (permissionId) => [
|
|
588
|
+
400,
|
|
589
|
+
`Permission with ID "${permissionId}" already exists. Choose a different ID.`,
|
|
590
|
+
{
|
|
591
|
+
permission_id: permissionId,
|
|
592
|
+
},
|
|
593
|
+
], (json) => [json.permission_id]);
|
|
573
594
|
export const KnownErrors = {
|
|
574
595
|
CannotDeleteCurrentSession,
|
|
575
596
|
UnsupportedError,
|
|
@@ -577,6 +598,7 @@ export const KnownErrors = {
|
|
|
577
598
|
SchemaError,
|
|
578
599
|
AllOverloadsFailed,
|
|
579
600
|
ProjectAuthenticationError,
|
|
601
|
+
PermissionIdAlreadyExists,
|
|
580
602
|
InvalidProjectAuthentication,
|
|
581
603
|
ProjectKeyWithoutAccessType,
|
|
582
604
|
InvalidAccessType,
|
|
@@ -665,6 +687,10 @@ export const KnownErrors = {
|
|
|
665
687
|
OAuthProviderAccessDenied,
|
|
666
688
|
ContactChannelAlreadyUsedForAuthBySomeoneElse,
|
|
667
689
|
InvalidPollingCodeError,
|
|
690
|
+
ApiKeyNotValid,
|
|
691
|
+
ApiKeyExpired,
|
|
692
|
+
ApiKeyRevoked,
|
|
693
|
+
WrongApiKeyType,
|
|
668
694
|
};
|
|
669
695
|
// ensure that all known error codes are unique
|
|
670
696
|
const knownErrorCodes = new Set();
|
package/dist/schema-fields.d.ts
CHANGED
|
@@ -130,7 +130,7 @@ export declare const signInResponseSchema: yup.ObjectSchema<{
|
|
|
130
130
|
is_new_user: undefined;
|
|
131
131
|
user_id: undefined;
|
|
132
132
|
}, "">;
|
|
133
|
-
export declare const teamSystemPermissions: readonly ["$update_team", "$delete_team", "$read_members", "$remove_members", "$invite_members"];
|
|
133
|
+
export declare const teamSystemPermissions: readonly ["$update_team", "$delete_team", "$read_members", "$remove_members", "$invite_members", "$manage_api_keys"];
|
|
134
134
|
export declare const permissionDefinitionIdSchema: yup.StringSchema<string | undefined, yup.AnyObject, undefined, "">;
|
|
135
135
|
export declare const customPermissionDefinitionIdSchema: yup.StringSchema<string | undefined, yup.AnyObject, undefined, "">;
|
|
136
136
|
export declare const teamPermissionDescriptionSchema: yup.StringSchema<string | undefined, yup.AnyObject, undefined, "">;
|
package/dist/schema-fields.js
CHANGED
|
@@ -147,7 +147,7 @@ export function yupUnion(...args) {
|
|
|
147
147
|
const errors = [];
|
|
148
148
|
for (const schema of args) {
|
|
149
149
|
try {
|
|
150
|
-
await schema
|
|
150
|
+
await yupValidate(schema, value, context.options);
|
|
151
151
|
return true;
|
|
152
152
|
}
|
|
153
153
|
catch (e) {
|
|
@@ -326,6 +326,7 @@ export const teamSystemPermissions = [
|
|
|
326
326
|
'$read_members',
|
|
327
327
|
'$remove_members',
|
|
328
328
|
'$invite_members',
|
|
329
|
+
'$manage_api_keys',
|
|
329
330
|
];
|
|
330
331
|
export const permissionDefinitionIdSchema = yupString()
|
|
331
332
|
.matches(/^\$?[a-z0-9_:]+$/, 'Only lowercase letters, numbers, ":", "_" and optional "$" at the beginning are allowed')
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* An api key has the following format:
|
|
3
|
+
* <prefix_without_underscores>_<secret_part_45_chars><id_part_32_chars><type_user_or_team_4_chars><scanner_and_marker_10_chars><checksum_8_chars>
|
|
4
|
+
*
|
|
5
|
+
* The scanner and marker is a base32 character that is used to determine if the api key is a public or private key
|
|
6
|
+
* and if it is a cloud or self-hosted key.
|
|
7
|
+
*
|
|
8
|
+
* The checksum is a crc32 checksum of the api key encoded in hex.
|
|
9
|
+
*
|
|
10
|
+
*/
|
|
11
|
+
type ProjectApiKey = {
|
|
12
|
+
id: string;
|
|
13
|
+
prefix: string;
|
|
14
|
+
isPublic: boolean;
|
|
15
|
+
isCloudVersion: boolean;
|
|
16
|
+
secret: string;
|
|
17
|
+
checksum: string;
|
|
18
|
+
type: "user" | "team";
|
|
19
|
+
};
|
|
20
|
+
export declare function isApiKey(secret: string): boolean;
|
|
21
|
+
export declare function createProjectApiKey(options: Pick<ProjectApiKey, "id" | "isPublic" | "isCloudVersion" | "type">): string;
|
|
22
|
+
export declare function parseProjectApiKey(secret: string): ProjectApiKey;
|
|
23
|
+
export {};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { getBase32CharacterFromIndex } from "@stackframe/stack-shared/dist/utils/bytes";
|
|
2
|
+
import { generateSecureRandomString } from "@stackframe/stack-shared/dist/utils/crypto";
|
|
3
|
+
import { StackAssertionError } from "@stackframe/stack-shared/dist/utils/errors";
|
|
4
|
+
import crc32 from 'crc/crc32';
|
|
5
|
+
const STACK_AUTH_MARKER = "574ck4u7h";
|
|
6
|
+
// API key part lengths
|
|
7
|
+
const API_KEY_LENGTHS = {
|
|
8
|
+
SECRET_PART: 45,
|
|
9
|
+
ID_PART: 32,
|
|
10
|
+
TYPE_PART: 4,
|
|
11
|
+
SCANNER_AND_MARKER: 10,
|
|
12
|
+
CHECKSUM: 8,
|
|
13
|
+
};
|
|
14
|
+
function createChecksumSync(checksummablePart) {
|
|
15
|
+
const data = new TextEncoder().encode(checksummablePart);
|
|
16
|
+
const calculated_checksum = crc32(data);
|
|
17
|
+
return calculated_checksum.toString(16).padStart(8, "0");
|
|
18
|
+
}
|
|
19
|
+
function createApiKeyParts(options) {
|
|
20
|
+
const { id, isPublic, isCloudVersion, type } = options;
|
|
21
|
+
const prefix = isPublic ? "pk" : "sk";
|
|
22
|
+
const scannerFlag = (isCloudVersion ? 0 : 1) + (isPublic ? 2 : 0) + ( /* version */0);
|
|
23
|
+
const secretPart = generateSecureRandomString();
|
|
24
|
+
const idPart = id.replace(/-/g, "");
|
|
25
|
+
const scannerAndMarker = getBase32CharacterFromIndex(scannerFlag).toLowerCase() + STACK_AUTH_MARKER;
|
|
26
|
+
const checksummablePart = `${prefix}_${secretPart}${idPart}${type}${scannerAndMarker}`;
|
|
27
|
+
return { checksummablePart, idPart, prefix, scannerAndMarker, type };
|
|
28
|
+
}
|
|
29
|
+
function parseApiKeyParts(secret) {
|
|
30
|
+
const regex = new RegExp(`^([^_]+)_` + // prefix
|
|
31
|
+
`(.{${API_KEY_LENGTHS.SECRET_PART}})` + // secretPart
|
|
32
|
+
`(.{${API_KEY_LENGTHS.ID_PART}})` + // idPart
|
|
33
|
+
`(.{${API_KEY_LENGTHS.TYPE_PART}})` + // type
|
|
34
|
+
`(.{${API_KEY_LENGTHS.SCANNER_AND_MARKER}})` + // scannerAndMarker
|
|
35
|
+
`(.{${API_KEY_LENGTHS.CHECKSUM}})$` // checksum
|
|
36
|
+
);
|
|
37
|
+
const match = secret.match(regex);
|
|
38
|
+
if (!match) {
|
|
39
|
+
throw new StackAssertionError("Invalid API key format");
|
|
40
|
+
}
|
|
41
|
+
const [, prefix, secretPart, idPart, type, scannerAndMarker, checksum] = match;
|
|
42
|
+
const scannerFlag = scannerAndMarker.replace(STACK_AUTH_MARKER, "");
|
|
43
|
+
const isCloudVersion = parseInt(scannerFlag, 32) % 2 === 0;
|
|
44
|
+
const isPublic = (parseInt(scannerFlag, 32) & 2) !== 0;
|
|
45
|
+
const checksummablePart = `${prefix}_${secretPart}${idPart}${type}${scannerAndMarker}`;
|
|
46
|
+
const restored_id = idPart.replace(/(.{8})(.{4})(.{4})(.{4})(.{12})/, "$1-$2-$3-$4-$5");
|
|
47
|
+
if (!["user", "team"].includes(type)) {
|
|
48
|
+
throw new StackAssertionError("Invalid type");
|
|
49
|
+
}
|
|
50
|
+
return { checksummablePart, checksum, id: restored_id, isCloudVersion, isPublic, prefix, type: type };
|
|
51
|
+
}
|
|
52
|
+
export function isApiKey(secret) {
|
|
53
|
+
return secret.includes("_") && secret.includes(STACK_AUTH_MARKER);
|
|
54
|
+
}
|
|
55
|
+
export function createProjectApiKey(options) {
|
|
56
|
+
const { checksummablePart } = createApiKeyParts(options);
|
|
57
|
+
const checksum = createChecksumSync(checksummablePart);
|
|
58
|
+
return `${checksummablePart}${checksum}`;
|
|
59
|
+
}
|
|
60
|
+
export function parseProjectApiKey(secret) {
|
|
61
|
+
const { checksummablePart, checksum, id, isCloudVersion, isPublic, prefix, type } = parseApiKeyParts(secret);
|
|
62
|
+
const calculated_checksum = createChecksumSync(checksummablePart);
|
|
63
|
+
if (calculated_checksum !== checksum) {
|
|
64
|
+
throw new StackAssertionError("Checksum mismatch");
|
|
65
|
+
}
|
|
66
|
+
return {
|
|
67
|
+
id,
|
|
68
|
+
prefix,
|
|
69
|
+
isPublic,
|
|
70
|
+
isCloudVersion,
|
|
71
|
+
secret,
|
|
72
|
+
checksum,
|
|
73
|
+
type,
|
|
74
|
+
};
|
|
75
|
+
}
|
package/dist/utils/bytes.d.ts
CHANGED
|
@@ -1,3 +1,6 @@
|
|
|
1
|
+
export declare function toHexString(input: Uint8Array): string;
|
|
2
|
+
export declare function getBase32CharacterFromIndex(index: number): string;
|
|
3
|
+
export declare function getBase32IndexFromCharacter(character: string): number;
|
|
1
4
|
export declare function encodeBase32(input: Uint8Array): string;
|
|
2
5
|
export declare function decodeBase32(input: string): Uint8Array;
|
|
3
6
|
export declare function encodeBase64(input: Uint8Array): string;
|
package/dist/utils/bytes.js
CHANGED
|
@@ -5,6 +5,43 @@ const crockfordReplacements = new Map([
|
|
|
5
5
|
["i", "1"],
|
|
6
6
|
["l", "1"],
|
|
7
7
|
]);
|
|
8
|
+
export function toHexString(input) {
|
|
9
|
+
return Array.from(input).map(b => b.toString(16).padStart(2, "0")).join("");
|
|
10
|
+
}
|
|
11
|
+
import.meta.vitest?.test("toHexString", ({ expect }) => {
|
|
12
|
+
expect(toHexString(new Uint8Array([]))).toBe("");
|
|
13
|
+
expect(toHexString(new Uint8Array([0]))).toBe("00");
|
|
14
|
+
expect(toHexString(new Uint8Array([15]))).toBe("0f");
|
|
15
|
+
expect(toHexString(new Uint8Array([16]))).toBe("10");
|
|
16
|
+
expect(toHexString(new Uint8Array([255]))).toBe("ff");
|
|
17
|
+
expect(toHexString(new Uint8Array([1, 2, 3]))).toBe("010203");
|
|
18
|
+
});
|
|
19
|
+
export function getBase32CharacterFromIndex(index) {
|
|
20
|
+
if (index < 0 || index >= crockfordAlphabet.length) {
|
|
21
|
+
throw new StackAssertionError(`Invalid base32 index: ${index}`);
|
|
22
|
+
}
|
|
23
|
+
return crockfordAlphabet[index];
|
|
24
|
+
}
|
|
25
|
+
import.meta.vitest?.test("getBase32CharacterFromIndex", ({ expect }) => {
|
|
26
|
+
expect(getBase32CharacterFromIndex(0)).toBe("0");
|
|
27
|
+
expect(getBase32CharacterFromIndex(15)).toBe("F");
|
|
28
|
+
expect(() => getBase32CharacterFromIndex(32)).toThrow();
|
|
29
|
+
});
|
|
30
|
+
export function getBase32IndexFromCharacter(character) {
|
|
31
|
+
if (character.length !== 1) {
|
|
32
|
+
throw new StackAssertionError(`Invalid base32 character: ${character}`);
|
|
33
|
+
}
|
|
34
|
+
const index = crockfordAlphabet.indexOf(character.toUpperCase());
|
|
35
|
+
if (index === -1) {
|
|
36
|
+
throw new StackAssertionError(`Invalid base32 character: ${character}`);
|
|
37
|
+
}
|
|
38
|
+
return index;
|
|
39
|
+
}
|
|
40
|
+
import.meta.vitest?.test("getBase32IndexFromCharacter", ({ expect }) => {
|
|
41
|
+
expect(getBase32IndexFromCharacter("0")).toBe(0);
|
|
42
|
+
expect(getBase32IndexFromCharacter("F")).toBe(15);
|
|
43
|
+
expect(() => getBase32IndexFromCharacter("_")).toThrow();
|
|
44
|
+
});
|
|
8
45
|
export function encodeBase32(input) {
|
|
9
46
|
let bits = 0;
|
|
10
47
|
let value = 0;
|
|
@@ -13,12 +50,12 @@ export function encodeBase32(input) {
|
|
|
13
50
|
value = (value << 8) | input[i];
|
|
14
51
|
bits += 8;
|
|
15
52
|
while (bits >= 5) {
|
|
16
|
-
output +=
|
|
53
|
+
output += getBase32CharacterFromIndex((value >>> (bits - 5)) & 31);
|
|
17
54
|
bits -= 5;
|
|
18
55
|
}
|
|
19
56
|
}
|
|
20
57
|
if (bits > 0) {
|
|
21
|
-
output +=
|
|
58
|
+
output += getBase32CharacterFromIndex((value << (5 - bits)) & 31);
|
|
22
59
|
}
|
|
23
60
|
// sanity check
|
|
24
61
|
if (!isBase32(output)) {
|
|
@@ -26,6 +63,14 @@ export function encodeBase32(input) {
|
|
|
26
63
|
}
|
|
27
64
|
return output;
|
|
28
65
|
}
|
|
66
|
+
import.meta.vitest?.test("encodeBase32", ({ expect }) => {
|
|
67
|
+
expect(encodeBase32(new Uint8Array([]))).toBe("");
|
|
68
|
+
expect(encodeBase32(new Uint8Array([1]))).toBe("04");
|
|
69
|
+
expect(encodeBase32(new Uint8Array([15]))).toBe("1W");
|
|
70
|
+
expect(encodeBase32(new Uint8Array([16]))).toBe("20");
|
|
71
|
+
expect(encodeBase32(new Uint8Array([255]))).toBe("ZW");
|
|
72
|
+
expect(encodeBase32(new Uint8Array([255, 255]))).toBe("ZZZG");
|
|
73
|
+
});
|
|
29
74
|
export function decodeBase32(input) {
|
|
30
75
|
if (!isBase32(input)) {
|
|
31
76
|
throw new StackAssertionError("Invalid base32 string");
|
|
@@ -41,10 +86,7 @@ export function decodeBase32(input) {
|
|
|
41
86
|
if (crockfordReplacements.has(char)) {
|
|
42
87
|
char = crockfordReplacements.get(char);
|
|
43
88
|
}
|
|
44
|
-
const index =
|
|
45
|
-
if (index === -1) {
|
|
46
|
-
throw new Error(`Invalid character: ${char}`);
|
|
47
|
-
}
|
|
89
|
+
const index = getBase32IndexFromCharacter(char);
|
|
48
90
|
value = (value << 5) | index;
|
|
49
91
|
bits += 5;
|
|
50
92
|
if (bits >= 8) {
|
|
@@ -54,6 +96,13 @@ export function decodeBase32(input) {
|
|
|
54
96
|
}
|
|
55
97
|
return output;
|
|
56
98
|
}
|
|
99
|
+
import.meta.vitest?.test("decodeBase32", ({ expect }) => {
|
|
100
|
+
expect(decodeBase32("")).toEqual(new Uint8Array([]));
|
|
101
|
+
expect(decodeBase32("00")).toEqual(new Uint8Array([0]));
|
|
102
|
+
expect(decodeBase32("1W")).toEqual(new Uint8Array([15]));
|
|
103
|
+
expect(decodeBase32("20")).toEqual(new Uint8Array([16]));
|
|
104
|
+
expect(decodeBase32("ZW")).toEqual(new Uint8Array([255]));
|
|
105
|
+
});
|
|
57
106
|
export function encodeBase64(input) {
|
|
58
107
|
const res = btoa(String.fromCharCode(...input));
|
|
59
108
|
// Skip sanity check for test cases
|