@productcraft/auth 0.7.1 → 0.8.1

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/dist/index.d.cts CHANGED
@@ -156,7 +156,7 @@ declare class AuthHttpError<TData = unknown> extends Error {
156
156
  */
157
157
  type AuditLogEntryDto = {
158
158
  /**
159
- * @description Canonical action string (e.g. `app.member.removed`, `role.created`).
159
+ * @description Canonical action string (e.g. `app.member.removed`, `role.created`). Account deletions: `auth.account.deleted` (one row per account, written in the deleting transaction; `resource` is `account`, `resource_id` the deleted account id, `metadata` `{ via, username, email_domain, created_at, verified }` where `via` is `platform`, `platform_bulk`, `consumer_admin` or `self`, plus `selection` (`{ mode, filter? }`) on bulk rows) and `auth.account.expired_unverified` (the unverified-account expiry job).
160
160
  * @type string
161
161
  */
162
162
  action: string;
@@ -981,6 +981,48 @@ type EndUserInviteListResponseDto = {
981
981
  * Generated by Kubb (https://kubb.dev/).
982
982
  * Do not edit manually.
983
983
  */
984
+ declare const botProtectionResponseDtoProviderEnum: {
985
+ readonly turnstile: "turnstile";
986
+ readonly hcaptcha: "hcaptcha";
987
+ };
988
+ type BotProtectionResponseDtoProviderEnumKey = (typeof botProtectionResponseDtoProviderEnum)[keyof typeof botProtectionResponseDtoProviderEnum];
989
+ type BotProtectionResponseDto = {
990
+ /**
991
+ * @description Whether sign-up requires a verified token.
992
+ * @type boolean
993
+ */
994
+ enabled: boolean;
995
+ /**
996
+ * @description Hostnames a solved challenge must come from. Empty = any.
997
+ * @type array
998
+ */
999
+ expected_hostnames: string[];
1000
+ /**
1001
+ * @type string
1002
+ */
1003
+ provider: BotProtectionResponseDtoProviderEnumKey;
1004
+ /**
1005
+ * @description Last four characters of the stored secret, so you can tell which one it is. null when none is stored.
1006
+ * @type string
1007
+ */
1008
+ secret_last4: string | null;
1009
+ /**
1010
+ * @description Whether a provider secret is stored. The secret itself is never returned.
1011
+ * @type boolean
1012
+ */
1013
+ secret_set: boolean;
1014
+ /**
1015
+ * @description Public widget site key.
1016
+ * @type string
1017
+ */
1018
+ site_key: string;
1019
+ };
1020
+
1021
+ /**
1022
+ * Generated by Kubb (https://kubb.dev/).
1023
+ * Do not edit manually.
1024
+ */
1025
+
984
1026
  declare const authConfigResponseDtoMfaEnum: {
985
1027
  readonly optional: "optional";
986
1028
  readonly required: "required";
@@ -1007,6 +1049,10 @@ type AuthConfigResponseDto = {
1007
1049
  * @type array
1008
1050
  */
1009
1051
  allowed_redirect_origins: string[];
1052
+ /**
1053
+ * @description Sign-up bot protection; null when not configured.
1054
+ */
1055
+ bot_protection: BotProtectionResponseDto | null;
1010
1056
  /**
1011
1057
  * @type boolean
1012
1058
  */
@@ -1071,6 +1117,18 @@ type AuthConfigResponseDto = {
1071
1117
  * @type boolean
1072
1118
  */
1073
1119
  signup_enabled: boolean;
1120
+ /**
1121
+ * @description When true, sign-up requires a Platform API Key from this app\'s workspace (anonymous sign-ups get 401).
1122
+ * @type boolean
1123
+ */
1124
+ signup_requires_pak: boolean;
1125
+ /**
1126
+ * @description Days an account may keep an unverified primary email before it is deleted. null = never.
1127
+ * @minLength 1
1128
+ * @maxLength 90
1129
+ * @type number
1130
+ */
1131
+ unverified_account_ttl_days: number | null;
1074
1132
  };
1075
1133
 
1076
1134
  /**
@@ -1133,6 +1191,46 @@ type UpdateAppStatusDto = {
1133
1191
  * Generated by Kubb (https://kubb.dev/).
1134
1192
  * Do not edit manually.
1135
1193
  */
1194
+ declare const updateBotProtectionDtoProviderEnum: {
1195
+ readonly turnstile: "turnstile";
1196
+ readonly hcaptcha: "hcaptcha";
1197
+ };
1198
+ type UpdateBotProtectionDtoProviderEnumKey = (typeof updateBotProtectionDtoProviderEnum)[keyof typeof updateBotProtectionDtoProviderEnum];
1199
+ type UpdateBotProtectionDto = {
1200
+ /**
1201
+ * @description When true, every sign-up on this app must carry a `bot_protection_token` the provider accepts. Needs a stored secret. Takes effect within seconds.
1202
+ * @type boolean | undefined
1203
+ */
1204
+ enabled?: boolean;
1205
+ /**
1206
+ * @description Hostnames the solved challenge must come from, as the provider reports them (exact match, no scheme, port or path). Empty (default) accepts any hostname the site key allows. Send null or [] to clear.
1207
+ * @type array
1208
+ */
1209
+ expected_hostnames?: string[] | null;
1210
+ /**
1211
+ * @description Which CAPTCHA the app\'s sign-up form renders: Cloudflare Turnstile or hCaptcha. Required the first time the block is configured. Changing it without sending a new `secret` clears the stored secret, which belonged to the other provider.
1212
+ * @type string | undefined
1213
+ */
1214
+ provider?: UpdateBotProtectionDtoProviderEnumKey;
1215
+ /**
1216
+ * @description The provider secret Auth verifies tokens with. WRITE-ONLY: no response ever returns it (GET shows `secret_set` and the last four characters). Send a string to store or replace it, null to clear it, or leave it out to keep the stored one. Stored encrypted.
1217
+ * @maxLength 512
1218
+ * @type string
1219
+ */
1220
+ secret?: string | null;
1221
+ /**
1222
+ * @description The public site key your sign-up page renders the widget with. Not a secret. Required the first time the block is configured.
1223
+ * @maxLength 256
1224
+ * @type string | undefined
1225
+ */
1226
+ site_key?: string;
1227
+ };
1228
+
1229
+ /**
1230
+ * Generated by Kubb (https://kubb.dev/).
1231
+ * Do not edit manually.
1232
+ */
1233
+
1136
1234
  declare const updateAuthConfigDtoMfaEnum: {
1137
1235
  readonly optional: "optional";
1138
1236
  readonly required: "required";
@@ -1163,6 +1261,10 @@ type UpdateAuthConfigDto = {
1163
1261
  * @type array | undefined
1164
1262
  */
1165
1263
  allowed_redirect_origins?: string[];
1264
+ /**
1265
+ * @description Sign-up bot protection (Cloudflare Turnstile or hCaptcha). Fields you send are merged onto the stored block; send null to remove the block and its secret. While `enabled`, sign-up requires a `bot_protection_token` the provider accepts, checked before anything else, and fails closed (503) when the provider cannot be reached.
1266
+ */
1267
+ bot_protection?: UpdateBotProtectionDto | null;
1166
1268
  /**
1167
1269
  * @description When true, Consumer-API routes annotated with `@RequireAppPermission(perm)` enforce the requested perm against the EndUser\'s role-resolved permission set. When false (default), permission checks are bypassed — any valid token can call any annotated route. Flip on once you\'ve authored roles + permissions for this app and want them to actually mean something.
1168
1270
  * @type boolean | undefined
@@ -1253,6 +1355,18 @@ type UpdateAuthConfigDto = {
1253
1355
  * @type boolean | undefined
1254
1356
  */
1255
1357
  signup_enabled?: boolean;
1358
+ /**
1359
+ * @description When true, sign-up only works from your backend: every `POST /{app_slug}/v1/auth/signup` must carry a Platform API Key (`Authorization: Bearer pcft_live_…`) from this app\'s workspace whose policy allows `auth.signup.create` on this app. Anonymous sign-ups get 401. Use it when your own backend gates sign-ups (CAPTCHA, invite or payment checks). Independent of `bot_protection`; an app can use both. Turning it on for an app that has no workspace is refused with 409.
1360
+ * @type boolean | undefined
1361
+ */
1362
+ signup_requires_pak?: boolean;
1363
+ /**
1364
+ * @description Delete accounts whose primary email is still unverified this many days after sign-up. Only self-sign-ups that never signed in after sign-up, hold no two-step factor, no verified contact and no social login qualify. Deletion is permanent, frees the email address and username, is audited as `auth.account.expired_unverified` and fires `user.deleted` with `reason: \"unverified_expired\"`. Send null to never delete. New apps start at 7; apps created before this setting existed start at null.
1365
+ * @minLength 1
1366
+ * @maxLength 90
1367
+ * @type number
1368
+ */
1369
+ unverified_account_ttl_days?: number | null;
1256
1370
  };
1257
1371
 
1258
1372
  /**
@@ -1306,6 +1420,13 @@ type UpdateEndUserDto = {
1306
1420
  * @type string | undefined
1307
1421
  */
1308
1422
  email?: string;
1423
+ /**
1424
+ * @description Keys to merge into the end-user\'s metadata (shallow; a key set to null is removed; at most 8 KB stored). Set `app_active: true` to mark someone who uses your app: the unverified-account expiry never deletes an account carrying it.
1425
+ * @type object | undefined
1426
+ */
1427
+ metadata?: {
1428
+ [key: string]: any;
1429
+ };
1309
1430
  };
1310
1431
 
1311
1432
  /**
@@ -2142,6 +2263,12 @@ type ConsumerAuthControllerSignin200 = ConsumerTokenResponseDto | ConsumerMfaCha
2142
2263
  * Do not edit manually.
2143
2264
  */
2144
2265
  type ConsumerSignupDto = {
2266
+ /**
2267
+ * @description The response token from the app\'s bot-protection widget (Cloudflare Turnstile or hCaptcha). Required when the app has bot protection switched on (`auth_config.bot_protection.enabled`); ignored otherwise. Auth verifies it with the provider before anything else about the sign-up is looked at. Tokens are single-use and expire after a few minutes, so render a fresh widget for every attempt. A backend that signs users up with a Platform API Key forwards the token its browser received.
2268
+ * @maxLength 2048
2269
+ * @type string | undefined
2270
+ */
2271
+ bot_protection_token?: string;
2145
2272
  /**
2146
2273
  * @description Optional display name shown back to the user.
2147
2274
  * @type string | undefined
@@ -2823,9 +2950,18 @@ interface VerifyOptions {
2823
2950
  /** Maximum token age — passed straight to jose. */
2824
2951
  maxTokenAge?: string | number;
2825
2952
  }
2953
+ /**
2954
+ * True when the claims mark a refresh token: `token_use: "refresh"`
2955
+ * (current shape) or `type: "refresh"` (older Auth tokens). Refresh
2956
+ * tokens must never authenticate a request.
2957
+ */
2958
+ declare function isRefreshToken(claims: Record<string, unknown> | null | undefined): boolean;
2826
2959
  /**
2827
2960
  * Verify an Auth-issued JWT against a JWKS resolver.
2828
2961
  *
2962
+ * Refresh tokens are refused with `JwtRefreshTokenError` even when the
2963
+ * signature, issuer and audience are valid.
2964
+ *
2829
2965
  * @param token The compact-form JWT (header.payload.signature)
2830
2966
  * @param getKey A jose-compatible key resolver — pass
2831
2967
  * `scope.jwks.getKey` from a `ConsumerScope`
@@ -3236,6 +3372,16 @@ declare class JwtNotYetValidError extends JwtVerifyError {
3236
3372
  cause?: unknown;
3237
3373
  });
3238
3374
  }
3375
+ /**
3376
+ * The token is a refresh token (`token_use: "refresh"` or
3377
+ * `type: "refresh"`). Refresh tokens are only valid at the refresh
3378
+ * endpoint and must never authenticate an API request.
3379
+ */
3380
+ declare class JwtRefreshTokenError extends JwtVerifyError {
3381
+ constructor(message?: string, options?: {
3382
+ cause?: unknown;
3383
+ });
3384
+ }
3239
3385
  /** `iss` claim doesn't match the expected issuer. */
3240
3386
  declare class JwtIssuerMismatchError extends JwtVerifyError {
3241
3387
  constructor(message: string, options?: {
@@ -3286,7 +3432,15 @@ interface AuthConfig extends PCClientConfig {
3286
3432
  jwksTtlMs?: number;
3287
3433
  }
3288
3434
  declare class Auth {
3435
+ /** Admin client: explicit `auth`, else `PRODUCTCRAFT_API_KEY`. */
3289
3436
  private readonly client;
3437
+ /**
3438
+ * Consumer client: explicit `auth` only. Consumer routes act for an
3439
+ * end user, so a workspace key from the environment must never leak
3440
+ * onto them. Pass `auth` explicitly for PAK-only consumer routes
3441
+ * (`signup_requires_pak`, tenants, code mint, factors).
3442
+ */
3443
+ private readonly consumerClient;
3290
3444
  private readonly baseUrl;
3291
3445
  private readonly fetch;
3292
3446
  private readonly jwtConfig;
@@ -3294,15 +3448,20 @@ declare class Auth {
3294
3448
  /**
3295
3449
  * Returns an `AppScope` bound to the given appId. All operations under
3296
3450
  * `/v1/apps/{appId}/...` are exposed as resource namespaces on the
3297
- * returned object.
3451
+ * returned object. Uses the explicit `auth`, else
3452
+ * `PRODUCTCRAFT_API_KEY` from the environment.
3298
3453
  */
3299
3454
  app(appId: string): AppScope;
3300
3455
  /**
3301
3456
  * Returns a `ConsumerScope` bound to the given appSlug. Exposes the
3302
3457
  * `/{appSlug}/v1/...` surface (sign-in, sign-up, me, verify, oauth)
3303
3458
  * and the JWT-verify helper for tokens issued by this app.
3459
+ *
3460
+ * `appSlug` defaults to `PRODUCTCRAFT_AUTH_APP` from the environment.
3461
+ * No credential is ever read from the environment for consumer
3462
+ * routes; only an explicit `auth` is sent.
3304
3463
  */
3305
- consumer(appSlug: string): ConsumerScope;
3464
+ consumer(appSlug?: string): ConsumerScope;
3306
3465
  readonly apps: {
3307
3466
  /** List apps the caller's workspace can see. */
3308
3467
  list: (params?: Partial<AppControllerListMyAppsQueryParams>) => Promise<AppListResponseDto>;
@@ -3319,4 +3478,4 @@ declare class Auth {
3319
3478
  };
3320
3479
  }
3321
3480
 
3322
- export { AUTH_LEGACY_ISSUER, AppScope, type AssignRoleDto, Auth, type AuthClaims, type AuthConfig, AuthHttpError, type AuthorizeBatchBody, type AuthorizeBody, type ClientCredentialsDto, type ConfirmEnrollmentResponseDto, type ConsumerAcceptInviteDto, type ConsumerLogoutDto, type ConsumerRefreshDto, type ConsumerRequestPasswordResetDto, type ConsumerResetPasswordDto, ConsumerScope, type ConsumerSigninDto, type ConsumerSignupDto, type ConsumerTokenResponseDto, type CreateApiKeyDto, type CreateAppDto, type CreateEndUserInviteDto, type CreateM2MClientDto, type CreatePermissionDto, type CreateRoleDto, type EncodedPasskeyCredential, type EndUserInviteResponseDto, type IdpNativeSigninDto, type IdpNativeUserHintDto, type IdpTokenResponseDto, JwksCache, JwksFetchError, JwksKeyNotFoundError, JwtAudienceMismatchError, JwtExpiredError, JwtInvalidError, JwtIssuerMismatchError, JwtNotYetValidError, JwtVerifyError, type PasskeyBrowserOptions, type PasskeyCredentialLike, type PasskeyCredentialsContainer, type PasskeyDecodedOptions, PasskeyError, type PasskeyErrorCode, type PasskeyMediation, type PasskeyPublicKeyOptions, type PublicFactorDto, type SetPermissionsDto, type SetScopesDto, type StepUpResponseDto, type UpdateAppDto, type UpdateAppStatusDto, type UpdateAuthConfigDto, type UpdateEndUserDto, type UpdateEndUserRoleDto, type UpdateEndUserStatusDto, type UpdateM2MClientDto, type UpdateMeDto, type UpdateRoleDto, type VerifyBody, type VerifyOptions, type WebauthnCredentialDto, createPasskeyCredential, decodeOptions, encodeCredential, fromB64u, getPasskeyCredential, isPasskeySupported, toB64u, verifyAuthToken };
3481
+ export { AUTH_LEGACY_ISSUER, AppScope, type AssignRoleDto, Auth, type AuthClaims, type AuthConfig, AuthHttpError, type AuthorizeBatchBody, type AuthorizeBody, type ClientCredentialsDto, type ConfirmEnrollmentResponseDto, type ConsumerAcceptInviteDto, type ConsumerLogoutDto, type ConsumerRefreshDto, type ConsumerRequestPasswordResetDto, type ConsumerResetPasswordDto, ConsumerScope, type ConsumerSigninDto, type ConsumerSignupDto, type ConsumerTokenResponseDto, type CreateApiKeyDto, type CreateAppDto, type CreateEndUserInviteDto, type CreateM2MClientDto, type CreatePermissionDto, type CreateRoleDto, type EncodedPasskeyCredential, type EndUserInviteResponseDto, type IdpNativeSigninDto, type IdpNativeUserHintDto, type IdpTokenResponseDto, JwksCache, JwksFetchError, JwksKeyNotFoundError, JwtAudienceMismatchError, JwtExpiredError, JwtInvalidError, JwtIssuerMismatchError, JwtNotYetValidError, JwtRefreshTokenError, JwtVerifyError, type PasskeyBrowserOptions, type PasskeyCredentialLike, type PasskeyCredentialsContainer, type PasskeyDecodedOptions, PasskeyError, type PasskeyErrorCode, type PasskeyMediation, type PasskeyPublicKeyOptions, type PublicFactorDto, type SetPermissionsDto, type SetScopesDto, type StepUpResponseDto, type UpdateAppDto, type UpdateAppStatusDto, type UpdateAuthConfigDto, type UpdateEndUserDto, type UpdateEndUserRoleDto, type UpdateEndUserStatusDto, type UpdateM2MClientDto, type UpdateMeDto, type UpdateRoleDto, type VerifyBody, type VerifyOptions, type WebauthnCredentialDto, createPasskeyCredential, decodeOptions, encodeCredential, fromB64u, getPasskeyCredential, isPasskeySupported, isRefreshToken, toB64u, verifyAuthToken };
package/dist/index.d.ts CHANGED
@@ -156,7 +156,7 @@ declare class AuthHttpError<TData = unknown> extends Error {
156
156
  */
157
157
  type AuditLogEntryDto = {
158
158
  /**
159
- * @description Canonical action string (e.g. `app.member.removed`, `role.created`).
159
+ * @description Canonical action string (e.g. `app.member.removed`, `role.created`). Account deletions: `auth.account.deleted` (one row per account, written in the deleting transaction; `resource` is `account`, `resource_id` the deleted account id, `metadata` `{ via, username, email_domain, created_at, verified }` where `via` is `platform`, `platform_bulk`, `consumer_admin` or `self`, plus `selection` (`{ mode, filter? }`) on bulk rows) and `auth.account.expired_unverified` (the unverified-account expiry job).
160
160
  * @type string
161
161
  */
162
162
  action: string;
@@ -981,6 +981,48 @@ type EndUserInviteListResponseDto = {
981
981
  * Generated by Kubb (https://kubb.dev/).
982
982
  * Do not edit manually.
983
983
  */
984
+ declare const botProtectionResponseDtoProviderEnum: {
985
+ readonly turnstile: "turnstile";
986
+ readonly hcaptcha: "hcaptcha";
987
+ };
988
+ type BotProtectionResponseDtoProviderEnumKey = (typeof botProtectionResponseDtoProviderEnum)[keyof typeof botProtectionResponseDtoProviderEnum];
989
+ type BotProtectionResponseDto = {
990
+ /**
991
+ * @description Whether sign-up requires a verified token.
992
+ * @type boolean
993
+ */
994
+ enabled: boolean;
995
+ /**
996
+ * @description Hostnames a solved challenge must come from. Empty = any.
997
+ * @type array
998
+ */
999
+ expected_hostnames: string[];
1000
+ /**
1001
+ * @type string
1002
+ */
1003
+ provider: BotProtectionResponseDtoProviderEnumKey;
1004
+ /**
1005
+ * @description Last four characters of the stored secret, so you can tell which one it is. null when none is stored.
1006
+ * @type string
1007
+ */
1008
+ secret_last4: string | null;
1009
+ /**
1010
+ * @description Whether a provider secret is stored. The secret itself is never returned.
1011
+ * @type boolean
1012
+ */
1013
+ secret_set: boolean;
1014
+ /**
1015
+ * @description Public widget site key.
1016
+ * @type string
1017
+ */
1018
+ site_key: string;
1019
+ };
1020
+
1021
+ /**
1022
+ * Generated by Kubb (https://kubb.dev/).
1023
+ * Do not edit manually.
1024
+ */
1025
+
984
1026
  declare const authConfigResponseDtoMfaEnum: {
985
1027
  readonly optional: "optional";
986
1028
  readonly required: "required";
@@ -1007,6 +1049,10 @@ type AuthConfigResponseDto = {
1007
1049
  * @type array
1008
1050
  */
1009
1051
  allowed_redirect_origins: string[];
1052
+ /**
1053
+ * @description Sign-up bot protection; null when not configured.
1054
+ */
1055
+ bot_protection: BotProtectionResponseDto | null;
1010
1056
  /**
1011
1057
  * @type boolean
1012
1058
  */
@@ -1071,6 +1117,18 @@ type AuthConfigResponseDto = {
1071
1117
  * @type boolean
1072
1118
  */
1073
1119
  signup_enabled: boolean;
1120
+ /**
1121
+ * @description When true, sign-up requires a Platform API Key from this app\'s workspace (anonymous sign-ups get 401).
1122
+ * @type boolean
1123
+ */
1124
+ signup_requires_pak: boolean;
1125
+ /**
1126
+ * @description Days an account may keep an unverified primary email before it is deleted. null = never.
1127
+ * @minLength 1
1128
+ * @maxLength 90
1129
+ * @type number
1130
+ */
1131
+ unverified_account_ttl_days: number | null;
1074
1132
  };
1075
1133
 
1076
1134
  /**
@@ -1133,6 +1191,46 @@ type UpdateAppStatusDto = {
1133
1191
  * Generated by Kubb (https://kubb.dev/).
1134
1192
  * Do not edit manually.
1135
1193
  */
1194
+ declare const updateBotProtectionDtoProviderEnum: {
1195
+ readonly turnstile: "turnstile";
1196
+ readonly hcaptcha: "hcaptcha";
1197
+ };
1198
+ type UpdateBotProtectionDtoProviderEnumKey = (typeof updateBotProtectionDtoProviderEnum)[keyof typeof updateBotProtectionDtoProviderEnum];
1199
+ type UpdateBotProtectionDto = {
1200
+ /**
1201
+ * @description When true, every sign-up on this app must carry a `bot_protection_token` the provider accepts. Needs a stored secret. Takes effect within seconds.
1202
+ * @type boolean | undefined
1203
+ */
1204
+ enabled?: boolean;
1205
+ /**
1206
+ * @description Hostnames the solved challenge must come from, as the provider reports them (exact match, no scheme, port or path). Empty (default) accepts any hostname the site key allows. Send null or [] to clear.
1207
+ * @type array
1208
+ */
1209
+ expected_hostnames?: string[] | null;
1210
+ /**
1211
+ * @description Which CAPTCHA the app\'s sign-up form renders: Cloudflare Turnstile or hCaptcha. Required the first time the block is configured. Changing it without sending a new `secret` clears the stored secret, which belonged to the other provider.
1212
+ * @type string | undefined
1213
+ */
1214
+ provider?: UpdateBotProtectionDtoProviderEnumKey;
1215
+ /**
1216
+ * @description The provider secret Auth verifies tokens with. WRITE-ONLY: no response ever returns it (GET shows `secret_set` and the last four characters). Send a string to store or replace it, null to clear it, or leave it out to keep the stored one. Stored encrypted.
1217
+ * @maxLength 512
1218
+ * @type string
1219
+ */
1220
+ secret?: string | null;
1221
+ /**
1222
+ * @description The public site key your sign-up page renders the widget with. Not a secret. Required the first time the block is configured.
1223
+ * @maxLength 256
1224
+ * @type string | undefined
1225
+ */
1226
+ site_key?: string;
1227
+ };
1228
+
1229
+ /**
1230
+ * Generated by Kubb (https://kubb.dev/).
1231
+ * Do not edit manually.
1232
+ */
1233
+
1136
1234
  declare const updateAuthConfigDtoMfaEnum: {
1137
1235
  readonly optional: "optional";
1138
1236
  readonly required: "required";
@@ -1163,6 +1261,10 @@ type UpdateAuthConfigDto = {
1163
1261
  * @type array | undefined
1164
1262
  */
1165
1263
  allowed_redirect_origins?: string[];
1264
+ /**
1265
+ * @description Sign-up bot protection (Cloudflare Turnstile or hCaptcha). Fields you send are merged onto the stored block; send null to remove the block and its secret. While `enabled`, sign-up requires a `bot_protection_token` the provider accepts, checked before anything else, and fails closed (503) when the provider cannot be reached.
1266
+ */
1267
+ bot_protection?: UpdateBotProtectionDto | null;
1166
1268
  /**
1167
1269
  * @description When true, Consumer-API routes annotated with `@RequireAppPermission(perm)` enforce the requested perm against the EndUser\'s role-resolved permission set. When false (default), permission checks are bypassed — any valid token can call any annotated route. Flip on once you\'ve authored roles + permissions for this app and want them to actually mean something.
1168
1270
  * @type boolean | undefined
@@ -1253,6 +1355,18 @@ type UpdateAuthConfigDto = {
1253
1355
  * @type boolean | undefined
1254
1356
  */
1255
1357
  signup_enabled?: boolean;
1358
+ /**
1359
+ * @description When true, sign-up only works from your backend: every `POST /{app_slug}/v1/auth/signup` must carry a Platform API Key (`Authorization: Bearer pcft_live_…`) from this app\'s workspace whose policy allows `auth.signup.create` on this app. Anonymous sign-ups get 401. Use it when your own backend gates sign-ups (CAPTCHA, invite or payment checks). Independent of `bot_protection`; an app can use both. Turning it on for an app that has no workspace is refused with 409.
1360
+ * @type boolean | undefined
1361
+ */
1362
+ signup_requires_pak?: boolean;
1363
+ /**
1364
+ * @description Delete accounts whose primary email is still unverified this many days after sign-up. Only self-sign-ups that never signed in after sign-up, hold no two-step factor, no verified contact and no social login qualify. Deletion is permanent, frees the email address and username, is audited as `auth.account.expired_unverified` and fires `user.deleted` with `reason: \"unverified_expired\"`. Send null to never delete. New apps start at 7; apps created before this setting existed start at null.
1365
+ * @minLength 1
1366
+ * @maxLength 90
1367
+ * @type number
1368
+ */
1369
+ unverified_account_ttl_days?: number | null;
1256
1370
  };
1257
1371
 
1258
1372
  /**
@@ -1306,6 +1420,13 @@ type UpdateEndUserDto = {
1306
1420
  * @type string | undefined
1307
1421
  */
1308
1422
  email?: string;
1423
+ /**
1424
+ * @description Keys to merge into the end-user\'s metadata (shallow; a key set to null is removed; at most 8 KB stored). Set `app_active: true` to mark someone who uses your app: the unverified-account expiry never deletes an account carrying it.
1425
+ * @type object | undefined
1426
+ */
1427
+ metadata?: {
1428
+ [key: string]: any;
1429
+ };
1309
1430
  };
1310
1431
 
1311
1432
  /**
@@ -2142,6 +2263,12 @@ type ConsumerAuthControllerSignin200 = ConsumerTokenResponseDto | ConsumerMfaCha
2142
2263
  * Do not edit manually.
2143
2264
  */
2144
2265
  type ConsumerSignupDto = {
2266
+ /**
2267
+ * @description The response token from the app\'s bot-protection widget (Cloudflare Turnstile or hCaptcha). Required when the app has bot protection switched on (`auth_config.bot_protection.enabled`); ignored otherwise. Auth verifies it with the provider before anything else about the sign-up is looked at. Tokens are single-use and expire after a few minutes, so render a fresh widget for every attempt. A backend that signs users up with a Platform API Key forwards the token its browser received.
2268
+ * @maxLength 2048
2269
+ * @type string | undefined
2270
+ */
2271
+ bot_protection_token?: string;
2145
2272
  /**
2146
2273
  * @description Optional display name shown back to the user.
2147
2274
  * @type string | undefined
@@ -2823,9 +2950,18 @@ interface VerifyOptions {
2823
2950
  /** Maximum token age — passed straight to jose. */
2824
2951
  maxTokenAge?: string | number;
2825
2952
  }
2953
+ /**
2954
+ * True when the claims mark a refresh token: `token_use: "refresh"`
2955
+ * (current shape) or `type: "refresh"` (older Auth tokens). Refresh
2956
+ * tokens must never authenticate a request.
2957
+ */
2958
+ declare function isRefreshToken(claims: Record<string, unknown> | null | undefined): boolean;
2826
2959
  /**
2827
2960
  * Verify an Auth-issued JWT against a JWKS resolver.
2828
2961
  *
2962
+ * Refresh tokens are refused with `JwtRefreshTokenError` even when the
2963
+ * signature, issuer and audience are valid.
2964
+ *
2829
2965
  * @param token The compact-form JWT (header.payload.signature)
2830
2966
  * @param getKey A jose-compatible key resolver — pass
2831
2967
  * `scope.jwks.getKey` from a `ConsumerScope`
@@ -3236,6 +3372,16 @@ declare class JwtNotYetValidError extends JwtVerifyError {
3236
3372
  cause?: unknown;
3237
3373
  });
3238
3374
  }
3375
+ /**
3376
+ * The token is a refresh token (`token_use: "refresh"` or
3377
+ * `type: "refresh"`). Refresh tokens are only valid at the refresh
3378
+ * endpoint and must never authenticate an API request.
3379
+ */
3380
+ declare class JwtRefreshTokenError extends JwtVerifyError {
3381
+ constructor(message?: string, options?: {
3382
+ cause?: unknown;
3383
+ });
3384
+ }
3239
3385
  /** `iss` claim doesn't match the expected issuer. */
3240
3386
  declare class JwtIssuerMismatchError extends JwtVerifyError {
3241
3387
  constructor(message: string, options?: {
@@ -3286,7 +3432,15 @@ interface AuthConfig extends PCClientConfig {
3286
3432
  jwksTtlMs?: number;
3287
3433
  }
3288
3434
  declare class Auth {
3435
+ /** Admin client: explicit `auth`, else `PRODUCTCRAFT_API_KEY`. */
3289
3436
  private readonly client;
3437
+ /**
3438
+ * Consumer client: explicit `auth` only. Consumer routes act for an
3439
+ * end user, so a workspace key from the environment must never leak
3440
+ * onto them. Pass `auth` explicitly for PAK-only consumer routes
3441
+ * (`signup_requires_pak`, tenants, code mint, factors).
3442
+ */
3443
+ private readonly consumerClient;
3290
3444
  private readonly baseUrl;
3291
3445
  private readonly fetch;
3292
3446
  private readonly jwtConfig;
@@ -3294,15 +3448,20 @@ declare class Auth {
3294
3448
  /**
3295
3449
  * Returns an `AppScope` bound to the given appId. All operations under
3296
3450
  * `/v1/apps/{appId}/...` are exposed as resource namespaces on the
3297
- * returned object.
3451
+ * returned object. Uses the explicit `auth`, else
3452
+ * `PRODUCTCRAFT_API_KEY` from the environment.
3298
3453
  */
3299
3454
  app(appId: string): AppScope;
3300
3455
  /**
3301
3456
  * Returns a `ConsumerScope` bound to the given appSlug. Exposes the
3302
3457
  * `/{appSlug}/v1/...` surface (sign-in, sign-up, me, verify, oauth)
3303
3458
  * and the JWT-verify helper for tokens issued by this app.
3459
+ *
3460
+ * `appSlug` defaults to `PRODUCTCRAFT_AUTH_APP` from the environment.
3461
+ * No credential is ever read from the environment for consumer
3462
+ * routes; only an explicit `auth` is sent.
3304
3463
  */
3305
- consumer(appSlug: string): ConsumerScope;
3464
+ consumer(appSlug?: string): ConsumerScope;
3306
3465
  readonly apps: {
3307
3466
  /** List apps the caller's workspace can see. */
3308
3467
  list: (params?: Partial<AppControllerListMyAppsQueryParams>) => Promise<AppListResponseDto>;
@@ -3319,4 +3478,4 @@ declare class Auth {
3319
3478
  };
3320
3479
  }
3321
3480
 
3322
- export { AUTH_LEGACY_ISSUER, AppScope, type AssignRoleDto, Auth, type AuthClaims, type AuthConfig, AuthHttpError, type AuthorizeBatchBody, type AuthorizeBody, type ClientCredentialsDto, type ConfirmEnrollmentResponseDto, type ConsumerAcceptInviteDto, type ConsumerLogoutDto, type ConsumerRefreshDto, type ConsumerRequestPasswordResetDto, type ConsumerResetPasswordDto, ConsumerScope, type ConsumerSigninDto, type ConsumerSignupDto, type ConsumerTokenResponseDto, type CreateApiKeyDto, type CreateAppDto, type CreateEndUserInviteDto, type CreateM2MClientDto, type CreatePermissionDto, type CreateRoleDto, type EncodedPasskeyCredential, type EndUserInviteResponseDto, type IdpNativeSigninDto, type IdpNativeUserHintDto, type IdpTokenResponseDto, JwksCache, JwksFetchError, JwksKeyNotFoundError, JwtAudienceMismatchError, JwtExpiredError, JwtInvalidError, JwtIssuerMismatchError, JwtNotYetValidError, JwtVerifyError, type PasskeyBrowserOptions, type PasskeyCredentialLike, type PasskeyCredentialsContainer, type PasskeyDecodedOptions, PasskeyError, type PasskeyErrorCode, type PasskeyMediation, type PasskeyPublicKeyOptions, type PublicFactorDto, type SetPermissionsDto, type SetScopesDto, type StepUpResponseDto, type UpdateAppDto, type UpdateAppStatusDto, type UpdateAuthConfigDto, type UpdateEndUserDto, type UpdateEndUserRoleDto, type UpdateEndUserStatusDto, type UpdateM2MClientDto, type UpdateMeDto, type UpdateRoleDto, type VerifyBody, type VerifyOptions, type WebauthnCredentialDto, createPasskeyCredential, decodeOptions, encodeCredential, fromB64u, getPasskeyCredential, isPasskeySupported, toB64u, verifyAuthToken };
3481
+ export { AUTH_LEGACY_ISSUER, AppScope, type AssignRoleDto, Auth, type AuthClaims, type AuthConfig, AuthHttpError, type AuthorizeBatchBody, type AuthorizeBody, type ClientCredentialsDto, type ConfirmEnrollmentResponseDto, type ConsumerAcceptInviteDto, type ConsumerLogoutDto, type ConsumerRefreshDto, type ConsumerRequestPasswordResetDto, type ConsumerResetPasswordDto, ConsumerScope, type ConsumerSigninDto, type ConsumerSignupDto, type ConsumerTokenResponseDto, type CreateApiKeyDto, type CreateAppDto, type CreateEndUserInviteDto, type CreateM2MClientDto, type CreatePermissionDto, type CreateRoleDto, type EncodedPasskeyCredential, type EndUserInviteResponseDto, type IdpNativeSigninDto, type IdpNativeUserHintDto, type IdpTokenResponseDto, JwksCache, JwksFetchError, JwksKeyNotFoundError, JwtAudienceMismatchError, JwtExpiredError, JwtInvalidError, JwtIssuerMismatchError, JwtNotYetValidError, JwtRefreshTokenError, JwtVerifyError, type PasskeyBrowserOptions, type PasskeyCredentialLike, type PasskeyCredentialsContainer, type PasskeyDecodedOptions, PasskeyError, type PasskeyErrorCode, type PasskeyMediation, type PasskeyPublicKeyOptions, type PublicFactorDto, type SetPermissionsDto, type SetScopesDto, type StepUpResponseDto, type UpdateAppDto, type UpdateAppStatusDto, type UpdateAuthConfigDto, type UpdateEndUserDto, type UpdateEndUserRoleDto, type UpdateEndUserStatusDto, type UpdateM2MClientDto, type UpdateMeDto, type UpdateRoleDto, type VerifyBody, type VerifyOptions, type WebauthnCredentialDto, createPasskeyCredential, decodeOptions, encodeCredential, fromB64u, getPasskeyCredential, isPasskeySupported, isRefreshToken, toB64u, verifyAuthToken };
package/dist/index.js CHANGED
@@ -1,4 +1,4 @@
1
- import { PC_BASE_URL } from '@productcraft/core';
1
+ import { PC_BASE_URL, resolveAuth, readEnv, PC_AUTH_APP_ENV } from '@productcraft/core';
2
2
  import { createRemoteJWKSet, errors, jwtVerify, customFetch } from 'jose';
3
3
 
4
4
  // src/index.ts
@@ -2068,6 +2068,12 @@ var JwtNotYetValidError = class extends JwtVerifyError {
2068
2068
  this.name = "JwtNotYetValidError";
2069
2069
  }
2070
2070
  };
2071
+ var JwtRefreshTokenError = class extends JwtVerifyError {
2072
+ constructor(message = "Refresh tokens cannot be used as access tokens", options) {
2073
+ super("ERR_JWT_REFRESH_TOKEN", message, options);
2074
+ this.name = "JwtRefreshTokenError";
2075
+ }
2076
+ };
2071
2077
  var JwtIssuerMismatchError = class extends JwtVerifyError {
2072
2078
  constructor(message, options) {
2073
2079
  super("ERR_JWT_ISS_MISMATCH", message, options);
@@ -2148,6 +2154,10 @@ var JwksCache = class {
2148
2154
  refresh() {
2149
2155
  }
2150
2156
  };
2157
+ function isRefreshToken(claims) {
2158
+ if (!claims) return false;
2159
+ return claims.token_use === "refresh" || claims.type === "refresh";
2160
+ }
2151
2161
  async function verifyAuthToken(token, getKey, opts = {}) {
2152
2162
  try {
2153
2163
  const { payload } = await jwtVerify(token, getKey, {
@@ -2159,6 +2169,7 @@ async function verifyAuthToken(token, getKey, opts = {}) {
2159
2169
  currentDate: opts.currentDate,
2160
2170
  maxTokenAge: opts.maxTokenAge
2161
2171
  });
2172
+ if (isRefreshToken(payload)) throw new JwtRefreshTokenError();
2162
2173
  return payload;
2163
2174
  } catch (err) {
2164
2175
  throw translateJoseError(err);
@@ -2700,7 +2711,15 @@ async function statsControllerGetMyStats({ params } = {}, config = {}) {
2700
2711
 
2701
2712
  // src/index.ts
2702
2713
  var Auth = class {
2714
+ /** Admin client: explicit `auth`, else `PRODUCTCRAFT_API_KEY`. */
2703
2715
  client;
2716
+ /**
2717
+ * Consumer client: explicit `auth` only. Consumer routes act for an
2718
+ * end user, so a workspace key from the environment must never leak
2719
+ * onto them. Pass `auth` explicitly for PAK-only consumer routes
2720
+ * (`signup_requires_pak`, tenants, code mint, factors).
2721
+ */
2722
+ consumerClient;
2704
2723
  baseUrl;
2705
2724
  fetch;
2706
2725
  jwtConfig;
@@ -2708,6 +2727,11 @@ var Auth = class {
2708
2727
  this.baseUrl = config.baseUrl ?? PC_BASE_URL.auth;
2709
2728
  this.fetch = config.fetch;
2710
2729
  this.client = makeAuthHttpClient({
2730
+ baseUrl: this.baseUrl,
2731
+ auth: resolveAuth(config.auth),
2732
+ fetch: this.fetch
2733
+ });
2734
+ this.consumerClient = makeAuthHttpClient({
2711
2735
  baseUrl: this.baseUrl,
2712
2736
  auth: config.auth,
2713
2737
  fetch: this.fetch
@@ -2719,7 +2743,8 @@ var Auth = class {
2719
2743
  /**
2720
2744
  * Returns an `AppScope` bound to the given appId. All operations under
2721
2745
  * `/v1/apps/{appId}/...` are exposed as resource namespaces on the
2722
- * returned object.
2746
+ * returned object. Uses the explicit `auth`, else
2747
+ * `PRODUCTCRAFT_API_KEY` from the environment.
2723
2748
  */
2724
2749
  app(appId) {
2725
2750
  return new AppScope(appId, this.client);
@@ -2728,11 +2753,21 @@ var Auth = class {
2728
2753
  * Returns a `ConsumerScope` bound to the given appSlug. Exposes the
2729
2754
  * `/{appSlug}/v1/...` surface (sign-in, sign-up, me, verify, oauth)
2730
2755
  * and the JWT-verify helper for tokens issued by this app.
2756
+ *
2757
+ * `appSlug` defaults to `PRODUCTCRAFT_AUTH_APP` from the environment.
2758
+ * No credential is ever read from the environment for consumer
2759
+ * routes; only an explicit `auth` is sent.
2731
2760
  */
2732
2761
  consumer(appSlug) {
2762
+ const slug = appSlug ?? readEnv(PC_AUTH_APP_ENV);
2763
+ if (!slug) {
2764
+ throw new Error(
2765
+ `@productcraft/auth: consumer() needs an app slug. Pass one, or set ${PC_AUTH_APP_ENV}.`
2766
+ );
2767
+ }
2733
2768
  return new ConsumerScope(
2734
- appSlug,
2735
- { client: this.client, baseUrl: this.baseUrl, fetch: this.fetch },
2769
+ slug,
2770
+ { client: this.consumerClient, baseUrl: this.baseUrl, fetch: this.fetch },
2736
2771
  this.jwtConfig
2737
2772
  );
2738
2773
  }
@@ -2771,6 +2806,6 @@ var Auth = class {
2771
2806
  };
2772
2807
  };
2773
2808
 
2774
- export { AUTH_LEGACY_ISSUER, AppScope, Auth, AuthHttpError, ConsumerScope, JwksCache, JwksFetchError, JwksKeyNotFoundError, JwtAudienceMismatchError, JwtExpiredError, JwtInvalidError, JwtIssuerMismatchError, JwtNotYetValidError, JwtVerifyError, PasskeyError, createPasskeyCredential, decodeOptions, encodeCredential, fromB64u, getPasskeyCredential, isPasskeySupported, toB64u, verifyAuthToken };
2809
+ export { AUTH_LEGACY_ISSUER, AppScope, Auth, AuthHttpError, ConsumerScope, JwksCache, JwksFetchError, JwksKeyNotFoundError, JwtAudienceMismatchError, JwtExpiredError, JwtInvalidError, JwtIssuerMismatchError, JwtNotYetValidError, JwtRefreshTokenError, JwtVerifyError, PasskeyError, createPasskeyCredential, decodeOptions, encodeCredential, fromB64u, getPasskeyCredential, isPasskeySupported, isRefreshToken, toB64u, verifyAuthToken };
2775
2810
  //# sourceMappingURL=index.js.map
2776
2811
  //# sourceMappingURL=index.js.map