@stackframe/stack-shared 2.8.2 → 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 +6 -0
- package/dist/interface/adminInterface.d.ts +7 -7
- package/dist/interface/adminInterface.js +4 -4
- package/dist/interface/clientInterface.d.ts +42 -0
- package/dist/interface/clientInterface.js +66 -2
- 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 +2 -2
- package/dist/known-errors.d.ts +21 -1
- package/dist/known-errors.js +22 -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/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/types.d.ts +17 -0
- package/package.json +2 -1
|
@@ -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;
|
package/dist/known-errors.d.ts
CHANGED
|
@@ -235,7 +235,9 @@ export declare const KnownErrors: {
|
|
|
235
235
|
UserNotFound: KnownErrorConstructor<KnownError & KnownErrorBrand<"USER_NOT_FOUND">, []> & {
|
|
236
236
|
errorCode: "USER_NOT_FOUND";
|
|
237
237
|
};
|
|
238
|
-
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">, []> & {
|
|
239
241
|
errorCode: "API_KEY_NOT_FOUND";
|
|
240
242
|
};
|
|
241
243
|
ProjectNotFound: KnownErrorConstructor<KnownError & KnownErrorBrand<"PROJECT_NOT_FOUND">, [projectId: string]> & {
|
|
@@ -403,5 +405,23 @@ export declare const KnownErrors: {
|
|
|
403
405
|
InvalidPollingCodeError: KnownErrorConstructor<KnownError & KnownErrorBrand<"INVALID_POLLING_CODE">, [details?: Json | undefined]> & {
|
|
404
406
|
errorCode: "INVALID_POLLING_CODE";
|
|
405
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
|
+
};
|
|
406
426
|
};
|
|
407
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,24 @@ 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
|
+
], () => []);
|
|
573
587
|
const PermissionIdAlreadyExists = createKnownErrorConstructor(KnownError, "PERMISSION_ID_ALREADY_EXISTS", (permissionId) => [
|
|
574
588
|
400,
|
|
575
589
|
`Permission with ID "${permissionId}" already exists. Choose a different ID.`,
|
|
@@ -673,6 +687,10 @@ export const KnownErrors = {
|
|
|
673
687
|
OAuthProviderAccessDenied,
|
|
674
688
|
ContactChannelAlreadyUsedForAuthBySomeoneElse,
|
|
675
689
|
InvalidPollingCodeError,
|
|
690
|
+
ApiKeyNotValid,
|
|
691
|
+
ApiKeyExpired,
|
|
692
|
+
ApiKeyRevoked,
|
|
693
|
+
WrongApiKeyType,
|
|
676
694
|
};
|
|
677
695
|
// ensure that all known error codes are unique
|
|
678
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
|
package/dist/utils/hashes.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export declare function sha512(input: Uint8Array | string): Promise<
|
|
1
|
+
export declare function sha512(input: Uint8Array | string): Promise<Uint8Array>;
|
|
2
2
|
export declare function hashPassword(password: string): Promise<string>;
|
|
3
3
|
export declare function comparePassword(password: string, hash: string): Promise<boolean>;
|
|
4
4
|
export declare function isPasswordHashValid(hash: string): Promise<boolean>;
|
package/dist/utils/hashes.js
CHANGED
|
@@ -2,9 +2,7 @@ import bcrypt from 'bcrypt';
|
|
|
2
2
|
import { StackAssertionError } from './errors';
|
|
3
3
|
export async function sha512(input) {
|
|
4
4
|
const bytes = typeof input === "string" ? new TextEncoder().encode(input) : input;
|
|
5
|
-
return await crypto.subtle.digest("SHA-512", bytes)
|
|
6
|
-
return Array.prototype.map.call(new Uint8Array(buf), x => (('00' + x.toString(16)).slice(-2))).join('');
|
|
7
|
-
});
|
|
5
|
+
return new Uint8Array(await crypto.subtle.digest("SHA-512", bytes));
|
|
8
6
|
}
|
|
9
7
|
export async function hashPassword(password) {
|
|
10
8
|
const passwordBytes = new TextEncoder().encode(password);
|
package/dist/utils/objects.d.ts
CHANGED
|
@@ -26,6 +26,14 @@ export type FilterUndefined<T> = {
|
|
|
26
26
|
* TypeScript's `Partial<XYZ>` type allows `undefined` values.
|
|
27
27
|
*/
|
|
28
28
|
export declare function filterUndefined<T extends {}>(obj: T): FilterUndefined<T>;
|
|
29
|
+
export type FilterUndefinedOrNull<T> = FilterUndefined<{
|
|
30
|
+
[k in keyof T]: null extends T[k] ? NonNullable<T[k]> | undefined : T[k];
|
|
31
|
+
}>;
|
|
32
|
+
/**
|
|
33
|
+
* Returns a new object with all undefined and null values removed. Useful when spreading optional parameters on an object, as
|
|
34
|
+
* TypeScript's `Partial<XYZ>` type allows `undefined` values.
|
|
35
|
+
*/
|
|
36
|
+
export declare function filterUndefinedOrNull<T extends {}>(obj: T): FilterUndefinedOrNull<T>;
|
|
29
37
|
export declare function pick<T extends {}, K extends keyof T>(obj: T, keys: K[]): Pick<T, K>;
|
|
30
38
|
export declare function omit<T extends {}, K extends keyof T>(obj: T, keys: K[]): Omit<T, K>;
|
|
31
39
|
export declare function split<T extends {}, K extends keyof T>(obj: T, keys: K[]): [Pick<T, K>, Omit<T, K>];
|
package/dist/utils/objects.js
CHANGED
|
@@ -215,6 +215,17 @@ import.meta.vitest?.test("filterUndefined", ({ expect }) => {
|
|
|
215
215
|
expect(filterUndefined({ a: null, b: undefined })).toEqual({ a: null });
|
|
216
216
|
expect(filterUndefined({ a: 0, b: "", c: false, d: undefined })).toEqual({ a: 0, b: "", c: false });
|
|
217
217
|
});
|
|
218
|
+
/**
|
|
219
|
+
* Returns a new object with all undefined and null values removed. Useful when spreading optional parameters on an object, as
|
|
220
|
+
* TypeScript's `Partial<XYZ>` type allows `undefined` values.
|
|
221
|
+
*/
|
|
222
|
+
export function filterUndefinedOrNull(obj) {
|
|
223
|
+
return Object.fromEntries(Object.entries(obj).filter(([, v]) => v !== undefined && v !== null));
|
|
224
|
+
}
|
|
225
|
+
import.meta.vitest?.test("filterUndefinedOrNull", ({ expect }) => {
|
|
226
|
+
expect(filterUndefinedOrNull({})).toEqual({});
|
|
227
|
+
expect(filterUndefinedOrNull({ a: 1, b: 2 })).toEqual({ a: 1, b: 2 });
|
|
228
|
+
});
|
|
218
229
|
export function pick(obj, keys) {
|
|
219
230
|
return Object.fromEntries(Object.entries(obj).filter(([k]) => keys.includes(k)));
|
|
220
231
|
}
|