@spfn/auth 0.3.0-beta.8 → 0.3.0-beta.9

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.
@@ -586,10 +586,14 @@ declare const KEY_FINGERPRINT_PREFIX_LENGTH = 8;
586
586
  *
587
587
  * @throws KeyIdAlreadyRegisteredError keyId가 이미 쓰인 값일 때 (자기 폐기 키 재사용 · 남의 키)
588
588
  * @throws InvalidKeyFingerprintError fingerprint가 publicKey와 맞지 않을 때
589
+ * @throws KeyAlgorithmMismatchError 키의 SPKI 타입이 선언된 algorithm과 다를 때
589
590
  */
590
591
  declare function registerPublicKeyService(params: RegisterPublicKeyParams): Promise<void>;
591
592
  /**
592
593
  * Rotate user's public key (revoke old, register new)
594
+ *
595
+ * @throws InvalidKeyFingerprintError fingerprint가 newPublicKey와 맞지 않을 때
596
+ * @throws KeyAlgorithmMismatchError 새 키의 SPKI 타입이 선언된 algorithm과 다를 때
593
597
  */
594
598
  declare function rotateKeyService(params: RotateKeyParams): Promise<RotateKeyResult>;
595
599
  /**
package/dist/server.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { A as AuthInitOptions, o as OAuthProvider, l as VerificationPurpose, j as PermissionCategory, p as AuthContext } from './machine-principals-Bd5hp76H.js';
2
- export { q as ApproveDeviceAuthParams, r as AuthProfileOutcome, s as AuthProfileVerifier, t as ChangePasswordParams, u as CompleteSignupParams, v as ConfirmSignupLinkParams, C as ConfirmSignupLinkResult, w as DenyDeviceAuthParams, x as DeviceAuthApprovedResult, y as DeviceAuthInfoParams, D as DeviceAuthInfoResult, z as DeviceAuthPendingResult, B as DeviceAuthPollResponseSchema, E as DeviceNameSchema, F as EmailSchema, G as FingerprintSchema, I as IssueOneTimeTokenResult, H as KEY_FINGERPRINT_PREFIX_LENGTH, J as KeyIdSchema, K as KeySummary, M as LoginParams, L as LoginResult, N as LogoutParams, Q as MachinePrincipal, T as MachineVerifierRegistration, W as NativeVerifyOptions, X as NormalizedIdentity, Y as OAuthCallbackParams, Z as OAuthCallbackResult, _ as OAuthCodeExchangeOptions, $ as OAuthNativeParams, f as OAuthNativeResult, a0 as OAuthStartParams, O as OAuthStartResult, a1 as OAuthTokens, a2 as PasswordSchema, a3 as PhoneSchema, a4 as PlatformSchema, a5 as PollDeviceAuthParams, a6 as PollDeviceAuthResult, a7 as PublicKeySchema, a8 as RegisterParams, a9 as RegisterPublicKeyParams, a as RegisterResult, aa as RequestSignupLinkParams, b as RequestSignupLinkResult, ab as RevokeAllKeysParams, e as RevokeAllKeysResult, ac as RevokeKeyParams, ad as RotateKeyParams, d as RotateKeyResult, ae as SendVerificationCodeParams, S as SendVerificationCodeResult, af as StartDeviceAuthParams, c as StartDeviceAuthResult, ag as TargetTypeSchema, ah as UnlinkNotification, ai as UnlinkNotifyRejection, aj as UnlinkNotifyRequest, ak as UnlinkNotifyResult, al as UserCodeSchema, V as VERIFICATION_PURPOSES, k as VERIFICATION_TARGET_TYPES, am as VerificationPurposeSchema, n as VerificationTargetType, an as VerifyCodeParams, ao as VerifyCodeResult, ap as approveDeviceAuthService, m as authRouter, aq as authenticate, ar as buildOAuthErrorUrl, as as changePasswordService, at as completeSignupService, au as confirmSignupLinkService, av as denyDeviceAuthService, aw as getDeviceAuthInfoService, ax as getEnabledOAuthProviders, ay as getGoogleAccessToken, az as getMachinePrincipal, aA as getOAuthProvider, aB as getRegisteredProviders, aC as isOAuthProviderEnabled, aD as isSafeReturnPath, aE as issueOneTimeTokenService, aF as listKeysService, aG as loginService, aH as logoutService, aI as machineAuth, aJ as oauthCallbackService, aK as oauthNativeService, aL as oauthStartService, aM as oauthUnlinkNotifyService, aN as optionalAuth, aO as pollDeviceAuthService, aP as registerAuthProfile, aQ as registerMachineVerifier, aR as registerOAuthProvider, aS as registerPublicKeyService, aT as registerService, aU as requestSignupLinkService, aV as requireEnabledProvider, aW as requireMachineScope, aX as resolveAuthenticatedUser, aY as revokeAllKeysService, aZ as revokeKeyService, a_ as rotateKeyService, a$ as runAuthProfile, b0 as selectAuthProfile, b1 as sendVerificationCodeService, b2 as startDeviceAuthService, b3 as verifyCodeService, b4 as verifyOneTimeTokenService } from './machine-principals-Bd5hp76H.js';
1
+ import { A as AuthInitOptions, o as OAuthProvider, l as VerificationPurpose, j as PermissionCategory, p as AuthContext } from './machine-principals-cC9TQZIt.js';
2
+ export { q as ApproveDeviceAuthParams, r as AuthProfileOutcome, s as AuthProfileVerifier, t as ChangePasswordParams, u as CompleteSignupParams, v as ConfirmSignupLinkParams, C as ConfirmSignupLinkResult, w as DenyDeviceAuthParams, x as DeviceAuthApprovedResult, y as DeviceAuthInfoParams, D as DeviceAuthInfoResult, z as DeviceAuthPendingResult, B as DeviceAuthPollResponseSchema, E as DeviceNameSchema, F as EmailSchema, G as FingerprintSchema, I as IssueOneTimeTokenResult, H as KEY_FINGERPRINT_PREFIX_LENGTH, J as KeyIdSchema, K as KeySummary, M as LoginParams, L as LoginResult, N as LogoutParams, Q as MachinePrincipal, T as MachineVerifierRegistration, W as NativeVerifyOptions, X as NormalizedIdentity, Y as OAuthCallbackParams, Z as OAuthCallbackResult, _ as OAuthCodeExchangeOptions, $ as OAuthNativeParams, f as OAuthNativeResult, a0 as OAuthStartParams, O as OAuthStartResult, a1 as OAuthTokens, a2 as PasswordSchema, a3 as PhoneSchema, a4 as PlatformSchema, a5 as PollDeviceAuthParams, a6 as PollDeviceAuthResult, a7 as PublicKeySchema, a8 as RegisterParams, a9 as RegisterPublicKeyParams, a as RegisterResult, aa as RequestSignupLinkParams, b as RequestSignupLinkResult, ab as RevokeAllKeysParams, e as RevokeAllKeysResult, ac as RevokeKeyParams, ad as RotateKeyParams, d as RotateKeyResult, ae as SendVerificationCodeParams, S as SendVerificationCodeResult, af as StartDeviceAuthParams, c as StartDeviceAuthResult, ag as TargetTypeSchema, ah as UnlinkNotification, ai as UnlinkNotifyRejection, aj as UnlinkNotifyRequest, ak as UnlinkNotifyResult, al as UserCodeSchema, V as VERIFICATION_PURPOSES, k as VERIFICATION_TARGET_TYPES, am as VerificationPurposeSchema, n as VerificationTargetType, an as VerifyCodeParams, ao as VerifyCodeResult, ap as approveDeviceAuthService, m as authRouter, aq as authenticate, ar as buildOAuthErrorUrl, as as changePasswordService, at as completeSignupService, au as confirmSignupLinkService, av as denyDeviceAuthService, aw as getDeviceAuthInfoService, ax as getEnabledOAuthProviders, ay as getGoogleAccessToken, az as getMachinePrincipal, aA as getOAuthProvider, aB as getRegisteredProviders, aC as isOAuthProviderEnabled, aD as isSafeReturnPath, aE as issueOneTimeTokenService, aF as listKeysService, aG as loginService, aH as logoutService, aI as machineAuth, aJ as oauthCallbackService, aK as oauthNativeService, aL as oauthStartService, aM as oauthUnlinkNotifyService, aN as optionalAuth, aO as pollDeviceAuthService, aP as registerAuthProfile, aQ as registerMachineVerifier, aR as registerOAuthProvider, aS as registerPublicKeyService, aT as registerService, aU as requestSignupLinkService, aV as requireEnabledProvider, aW as requireMachineScope, aX as resolveAuthenticatedUser, aY as revokeAllKeysService, aZ as revokeKeyService, a_ as rotateKeyService, a$ as runAuthProfile, b0 as selectAuthProfile, b1 as sendVerificationCodeService, b2 as startDeviceAuthService, b3 as verifyCodeService, b4 as verifyOneTimeTokenService } from './machine-principals-cC9TQZIt.js';
3
3
  import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
4
4
  import { K as KeyAlgorithmType, d as InvitationStatus, j as SocialProvider, c as AccountDeletionRequestedBy, i as PurgeStrategy } from './types-DYyhze28.js';
5
5
  export { A as ACCOUNT_DELETION_REQUESTED_BY, a as ACCOUNT_DELETION_REQUEST_STATUSES, b as AccountDeletionRequestStatus, I as INVITATION_STATUSES, e as KEY_ALGORITHM, f as KEY_DEVICE_NAME_MAX_LENGTH, g as KEY_PLATFORM, h as KeyPlatformType, P as PURGE_STRATEGIES, S as SOCIAL_PROVIDERS, U as USER_STATUSES, k as UserStatus } from './types-DYyhze28.js';
@@ -6681,6 +6681,23 @@ declare function decodeToken(token: string): TokenPayload | null;
6681
6681
  * ```
6682
6682
  */
6683
6683
  declare function verifyKeyFingerprint(publicKeyB64: string, expectedFingerprint: string): boolean;
6684
+ /**
6685
+ * Refuses a public key whose SPKI type is not the one its declared algorithm
6686
+ * needs, before the key is stored.
6687
+ *
6688
+ * The algorithm column is what proof verification later reads, and nothing
6689
+ * re-derives it from the key material — so an EC key parked as RS256 is
6690
+ * accepted at enrollment and fails on every request afterwards, with the device
6691
+ * already believing it is enrolled. Refusing at registration is what keeps that
6692
+ * mismatch from ever being written.
6693
+ *
6694
+ * Bytes that are no SPKI public key at all are refused the same way: the parser
6695
+ * throws on them, and an unreadable key is the same defect as a mismatched one.
6696
+ * Neither is a server fault, so both answer 400 rather than falling out as a 500.
6697
+ *
6698
+ * @throws KeyAlgorithmMismatchError when the key is not the algorithm's type.
6699
+ */
6700
+ declare function assertKeyMatchesAlgorithm(publicKeyB64: string, algorithm: KeyAlgorithmType): void;
6684
6701
 
6685
6702
  /**
6686
6703
  * @spfn/auth - Permission Middleware
@@ -7980,4 +7997,4 @@ type AuthDeletionCancelledPayload = typeof authDeletionCancelledEvent._payload;
7980
7997
  type AuthDeletionCompletedPayload = typeof authDeletionCompletedEvent._payload;
7981
7998
  type OAuthUnlinkedPayload = typeof oauthUnlinkedEvent._payload;
7982
7999
 
7983
- export { type AccountDeletionPurgeUser, type AccountDeletionRequest, AccountDeletionRequestedBy, AccountDeletionRequestsRepository, type AuthConfig, AuthContext, type AuthCsrfConfig, type AuthDeletionCancelledPayload, type AuthDeletionCompletedPayload, type AuthDeletionConfig, type AuthDeletionRequestedPayload, type AuthDeviceAuthConfig, type AuthLifecycleConfig, type AuthLifecycleOptions, type AuthLoginPayload, type AuthMetadataEntity, AuthMetadataRepository, AuthProviderSchema, type AuthRegisterPayload, type BeforeRegisterContext, COOKIE_NAMES, CSRF_HEADER, type CancelAccountDeletionParams, type CancelAccountDeletionResult, type CreateOAuthStateParams, type CsrfMode, DEFAULT_DELETION_ALLOW_SELF_IMMEDIATE, DEFAULT_DELETION_GRACE_PERIOD_DAYS, DEFAULT_DELETION_PURGE_CRON, DEFAULT_DELETION_PURGE_STRATEGY, DEFAULT_DELETION_SEND_NOTIFICATIONS, DEFAULT_DEVICE_AUTH_INTERVAL_MS, DEFAULT_DEVICE_AUTH_TTL_MS, DEVICE_AUTH_STATUSES, type DecryptedOAuthToken, type DeviceAuthStatus, type DeviceAuthorization, DeviceAuthorizationsRepository, type EmailNormalizationResult, EnvironmentKeyringTokenCipher, type GoogleTokenResponse, type GoogleUserInfo, type Invitation, type InvitationAcceptedPayload, type InvitationCreatedPayload, InvitationStatus, InvitationsRepository, type IssuedOpsToken, KeyAlgorithmType, KeysRepository, type NewAccountDeletionRequest, type NewAuthMetadataEntity, type NewDeviceAuthorization, type NewInvitation, type NewOpsToken, type NewPermission, type NewPermissionEntity, type NewRole, type NewRoleEntity, type NewRolePermission, type NewSignupLinkToken, type NewUser, type NewUserPermission, type NewUserProfile, type NewUserPublicKey, type NewUserSocialAccount, type NewVerificationCode, OAuthProvider, type OAuthState, type OAuthTokenCipher, type OAuthTokenContext, type OAuthTokenType, type OAuthUnlinkedPayload, type OpsToken, OpsTokensRepository, type PendingDeletionInfo, type Permission, type PermissionEntity, PermissionsRepository, PurgeStrategy, type PurgeUserResult, type RegisterChannel, type RequestAccountDeletionParams, type RequestAccountDeletionResult, type Role, type RoleEntity, type RoleGuardOptions, type RolePermission, RolePermissionsRepository, RolesRepository, type SessionPayload, type SignupLinkToken, SignupLinkTokensRepository, SocialAccountsRepository, SocialProvider, type SweepDuePurgesResult, type TokenPayload, USER_CODE_ALPHABET, USER_CODE_LENGTH, type UpdateProfileParams, type User, type UserPermission, UserPermissionsRepository, type UserProfile, UserProfilesRepository, type UserPublicKey, type UserSocialAccount, UsersRepository, type VerificationCode, VerificationCodesRepository, VerificationPurpose, type VerifiedOpsToken, type VerifyIdTokenParams, acceptInvitation, accountDeletionRequests, accountDeletionRequestsRepository, addPermissionToRole, appleProvider, assertCanAssignRole, authDeletionCancelledEvent, authDeletionCompletedEvent, authDeletionRequestedEvent, authJobRouter, authLogger, authLoginEvent, authMetadata, authMetadataRepository, authRegisterEvent, authSchema, cancelAccountDeletionService, cancelInvitation, checkUsernameAvailableService, configureAuth, configureDeletion, configureDeviceAuth, configureOAuthTokenCipher, createAuthDeletionJobRouter, createAuthDeletionPurgeJob, createAuthLifecycle, createInvitation, createOAuthState, createRole, decodeToken, decryptToken, deleteInvitation, deleteRole, deriveCsrfToken, deviceAuthorizations, deviceAuthorizationsRepository, encryptToken, exchangeCodeForTokens, expireOldInvitations, formatUserCode, generateDeviceCode, generateOAuthNonce, generateToken, generateUserCode, getAllRoles, getAuth, getAuthConfig, getAuthSessionService, getCsrfExemptPaths, getCsrfMode, getDeletionConfig, getDeviceAuthConfig, getDummyPasswordHash, getGoogleAuthUrl, getGoogleOAuthConfig, getGoogleUserInfo, getInvitationByToken, getInvitationWithDetails, getKeyId, getLocale, getOneTimeTokenManager, getOpsToken, getOptionalAuth, getPendingDeletionInfo, getRole, getRoleByName, getRolePermissions, getSessionTtl, getUser, getUserByEmailService, getUserByIdService, getUserByPhoneService, getUserId, getUserPermissions, getUserProfileService, getUserRole, githubProvider, googleProvider, hasAllPermissions, hasAnyPermission, hasAnyRole, hasPermission, hasRole, hashDeviceCode, hashPassword, initOneTimeTokenManager, initializeAuth, invitationAcceptedEvent, invitationCreatedEvent, invitationsRepository, isEncrypted, isGoogleOAuthEnabled, issueOpsTokenService, kakaoProvider, keysRepository, listInvitations, listOpsTokensService, matchOAuthCsrfCookies, matchesCsrfToken, naverProvider, normalizeEmail, normalizeOptionalEmail, normalizeStoredEmails, normalizeUserCode, oauthUnlinkedEvent, oneTimeTokenAuth, opsTokenAuth, opsTokens, opsTokensRepository, parseDuration, permissions, permissionsRepository, purgeUserService, refreshAccessToken, removePermissionFromRole, requestAccountDeletionService, requireAnyPermission, requireOpsScope, requirePermissions, requireRole, resendInvitation, revokeOpsTokenService, roleGuard, rolePermissions, rolePermissionsRepository, roles, rolesRepository, runBeforeRegister, setRolePermissions, signupLinkTokens, signupLinkTokensRepository, socialAccountsRepository, sweepDuePurges, updateLastLoginService, updateLocaleService, updateRole, updateUserProfileService, updateUserService, updateUsernameService, userInvitations, userPermissions, userPermissionsRepository, userProfiles, userProfilesRepository, userPublicKeys, userSocialAccounts, users, usersRepository, validateInvitation, validatePasswordStrength, verificationCodes, verificationCodesRepository, verifyClientToken, verifyIdToken, verifyKeyFingerprint, verifyOAuthState, verifyOpsTokenService, verifyPassword, verifyToken };
8000
+ export { type AccountDeletionPurgeUser, type AccountDeletionRequest, AccountDeletionRequestedBy, AccountDeletionRequestsRepository, type AuthConfig, AuthContext, type AuthCsrfConfig, type AuthDeletionCancelledPayload, type AuthDeletionCompletedPayload, type AuthDeletionConfig, type AuthDeletionRequestedPayload, type AuthDeviceAuthConfig, type AuthLifecycleConfig, type AuthLifecycleOptions, type AuthLoginPayload, type AuthMetadataEntity, AuthMetadataRepository, AuthProviderSchema, type AuthRegisterPayload, type BeforeRegisterContext, COOKIE_NAMES, CSRF_HEADER, type CancelAccountDeletionParams, type CancelAccountDeletionResult, type CreateOAuthStateParams, type CsrfMode, DEFAULT_DELETION_ALLOW_SELF_IMMEDIATE, DEFAULT_DELETION_GRACE_PERIOD_DAYS, DEFAULT_DELETION_PURGE_CRON, DEFAULT_DELETION_PURGE_STRATEGY, DEFAULT_DELETION_SEND_NOTIFICATIONS, DEFAULT_DEVICE_AUTH_INTERVAL_MS, DEFAULT_DEVICE_AUTH_TTL_MS, DEVICE_AUTH_STATUSES, type DecryptedOAuthToken, type DeviceAuthStatus, type DeviceAuthorization, DeviceAuthorizationsRepository, type EmailNormalizationResult, EnvironmentKeyringTokenCipher, type GoogleTokenResponse, type GoogleUserInfo, type Invitation, type InvitationAcceptedPayload, type InvitationCreatedPayload, InvitationStatus, InvitationsRepository, type IssuedOpsToken, KeyAlgorithmType, KeysRepository, type NewAccountDeletionRequest, type NewAuthMetadataEntity, type NewDeviceAuthorization, type NewInvitation, type NewOpsToken, type NewPermission, type NewPermissionEntity, type NewRole, type NewRoleEntity, type NewRolePermission, type NewSignupLinkToken, type NewUser, type NewUserPermission, type NewUserProfile, type NewUserPublicKey, type NewUserSocialAccount, type NewVerificationCode, OAuthProvider, type OAuthState, type OAuthTokenCipher, type OAuthTokenContext, type OAuthTokenType, type OAuthUnlinkedPayload, type OpsToken, OpsTokensRepository, type PendingDeletionInfo, type Permission, type PermissionEntity, PermissionsRepository, PurgeStrategy, type PurgeUserResult, type RegisterChannel, type RequestAccountDeletionParams, type RequestAccountDeletionResult, type Role, type RoleEntity, type RoleGuardOptions, type RolePermission, RolePermissionsRepository, RolesRepository, type SessionPayload, type SignupLinkToken, SignupLinkTokensRepository, SocialAccountsRepository, SocialProvider, type SweepDuePurgesResult, type TokenPayload, USER_CODE_ALPHABET, USER_CODE_LENGTH, type UpdateProfileParams, type User, type UserPermission, UserPermissionsRepository, type UserProfile, UserProfilesRepository, type UserPublicKey, type UserSocialAccount, UsersRepository, type VerificationCode, VerificationCodesRepository, VerificationPurpose, type VerifiedOpsToken, type VerifyIdTokenParams, acceptInvitation, accountDeletionRequests, accountDeletionRequestsRepository, addPermissionToRole, appleProvider, assertCanAssignRole, assertKeyMatchesAlgorithm, authDeletionCancelledEvent, authDeletionCompletedEvent, authDeletionRequestedEvent, authJobRouter, authLogger, authLoginEvent, authMetadata, authMetadataRepository, authRegisterEvent, authSchema, cancelAccountDeletionService, cancelInvitation, checkUsernameAvailableService, configureAuth, configureDeletion, configureDeviceAuth, configureOAuthTokenCipher, createAuthDeletionJobRouter, createAuthDeletionPurgeJob, createAuthLifecycle, createInvitation, createOAuthState, createRole, decodeToken, decryptToken, deleteInvitation, deleteRole, deriveCsrfToken, deviceAuthorizations, deviceAuthorizationsRepository, encryptToken, exchangeCodeForTokens, expireOldInvitations, formatUserCode, generateDeviceCode, generateOAuthNonce, generateToken, generateUserCode, getAllRoles, getAuth, getAuthConfig, getAuthSessionService, getCsrfExemptPaths, getCsrfMode, getDeletionConfig, getDeviceAuthConfig, getDummyPasswordHash, getGoogleAuthUrl, getGoogleOAuthConfig, getGoogleUserInfo, getInvitationByToken, getInvitationWithDetails, getKeyId, getLocale, getOneTimeTokenManager, getOpsToken, getOptionalAuth, getPendingDeletionInfo, getRole, getRoleByName, getRolePermissions, getSessionTtl, getUser, getUserByEmailService, getUserByIdService, getUserByPhoneService, getUserId, getUserPermissions, getUserProfileService, getUserRole, githubProvider, googleProvider, hasAllPermissions, hasAnyPermission, hasAnyRole, hasPermission, hasRole, hashDeviceCode, hashPassword, initOneTimeTokenManager, initializeAuth, invitationAcceptedEvent, invitationCreatedEvent, invitationsRepository, isEncrypted, isGoogleOAuthEnabled, issueOpsTokenService, kakaoProvider, keysRepository, listInvitations, listOpsTokensService, matchOAuthCsrfCookies, matchesCsrfToken, naverProvider, normalizeEmail, normalizeOptionalEmail, normalizeStoredEmails, normalizeUserCode, oauthUnlinkedEvent, oneTimeTokenAuth, opsTokenAuth, opsTokens, opsTokensRepository, parseDuration, permissions, permissionsRepository, purgeUserService, refreshAccessToken, removePermissionFromRole, requestAccountDeletionService, requireAnyPermission, requireOpsScope, requirePermissions, requireRole, resendInvitation, revokeOpsTokenService, roleGuard, rolePermissions, rolePermissionsRepository, roles, rolesRepository, runBeforeRegister, setRolePermissions, signupLinkTokens, signupLinkTokensRepository, socialAccountsRepository, sweepDuePurges, updateLastLoginService, updateLocaleService, updateRole, updateUserProfileService, updateUserService, updateUsernameService, userInvitations, userPermissions, userPermissionsRepository, userProfiles, userProfilesRepository, userPublicKeys, userSocialAccounts, users, usersRepository, validateInvitation, validatePasswordStrength, verificationCodes, verificationCodesRepository, verifyClientToken, verifyIdToken, verifyKeyFingerprint, verifyOAuthState, verifyOpsTokenService, verifyPassword, verifyToken };
package/dist/server.js CHANGED
@@ -7568,7 +7568,7 @@ var init_schema5 = __esm({
7568
7568
  },
7569
7569
  SPFN_AUTH_GOOGLE_REDIRECT_URI: {
7570
7570
  ...envString({
7571
- description: "Google OAuth callback URL. Defaults to {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}/_auth/oauth/google/callback \u2014 the callback must return to the web app origin that set the oauth_csrf cookie (the app rewrites /_auth/:path* to the API). Set this explicitly only when the callback should hit a different host (e.g. the API host for the direct oauthStart flow).",
7571
+ description: "Google OAuth callback URL. Defaults to {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}/_auth/oauth/google/callback. The override must stay on the web app origin at this exact path \u2014 the CSRF cookie for the callback is host-only and the app rewrites /_auth/:path* to the API, so a callback that lands anywhere else is refused for CSRF. Checked at boot: a value off the web app origin or off the callback path refuses to start. The one case for an override elsewhere is the direct POST /_auth/oauth/start flow on a split deployment (no Next.js interceptor, so its CSRF cookie is on the API host), which also needs SPFN_AUTH_OAUTH_CALLBACK_ORIGIN_CHECK=off.",
7572
7572
  required: false,
7573
7573
  examples: [
7574
7574
  "https://app.example.com/_auth/oauth/google/callback",
@@ -7611,7 +7611,7 @@ var init_schema5 = __esm({
7611
7611
  },
7612
7612
  SPFN_AUTH_KAKAO_REDIRECT_URI: {
7613
7613
  ...envString({
7614
- description: "Kakao OAuth callback URL. Defaults to {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}/_auth/oauth/kakao/callback.",
7614
+ description: "Kakao OAuth callback URL. Defaults to {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}/_auth/oauth/kakao/callback. The override must stay on the web app origin at this exact path \u2014 the CSRF cookie for the callback is host-only and the app rewrites /_auth/:path* to the API, so a callback that lands anywhere else is refused for CSRF. Checked at boot: a value off the web app origin or off the callback path refuses to start. The one case for an override elsewhere is the direct POST /_auth/oauth/start flow on a split deployment (no Next.js interceptor, so its CSRF cookie is on the API host), which also needs SPFN_AUTH_OAUTH_CALLBACK_ORIGIN_CHECK=off.",
7615
7615
  required: false,
7616
7616
  examples: ["https://app.example.com/_auth/oauth/kakao/callback"]
7617
7617
  })
@@ -7636,7 +7636,7 @@ var init_schema5 = __esm({
7636
7636
  },
7637
7637
  SPFN_AUTH_NAVER_REDIRECT_URI: {
7638
7638
  ...envString({
7639
- description: "Naver OAuth callback URL. Defaults to {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}/_auth/oauth/naver/callback.",
7639
+ description: "Naver OAuth callback URL. Defaults to {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}/_auth/oauth/naver/callback. The override must stay on the web app origin at this exact path \u2014 the CSRF cookie for the callback is host-only and the app rewrites /_auth/:path* to the API, so a callback that lands anywhere else is refused for CSRF. Checked at boot: a value off the web app origin or off the callback path refuses to start. The one case for an override elsewhere is the direct POST /_auth/oauth/start flow on a split deployment (no Next.js interceptor, so its CSRF cookie is on the API host), which also needs SPFN_AUTH_OAUTH_CALLBACK_ORIGIN_CHECK=off.",
7640
7640
  required: false,
7641
7641
  examples: ["https://app.example.com/_auth/oauth/naver/callback"]
7642
7642
  })
@@ -7668,11 +7668,18 @@ var init_schema5 = __esm({
7668
7668
  },
7669
7669
  SPFN_AUTH_GITHUB_REDIRECT_URI: {
7670
7670
  ...envString({
7671
- description: "GitHub OAuth callback URL. Defaults to {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}/_auth/oauth/github/callback.",
7671
+ description: "GitHub OAuth callback URL. Defaults to {NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}/_auth/oauth/github/callback. The override must stay on the web app origin at this exact path \u2014 the CSRF cookie for the callback is host-only and the app rewrites /_auth/:path* to the API, so a callback that lands anywhere else is refused for CSRF. Checked at boot: a value off the web app origin or off the callback path refuses to start. The one case for an override elsewhere is the direct POST /_auth/oauth/start flow on a split deployment (no Next.js interceptor, so its CSRF cookie is on the API host), which also needs SPFN_AUTH_OAUTH_CALLBACK_ORIGIN_CHECK=off.",
7672
7672
  required: false,
7673
7673
  examples: ["https://app.example.com/_auth/oauth/github/callback"]
7674
7674
  })
7675
7675
  },
7676
+ SPFN_AUTH_OAUTH_CALLBACK_ORIGIN_CHECK: {
7677
+ ...envString({
7678
+ description: 'Boot-time check of the four SPFN_AUTH_<PROVIDER>_REDIRECT_URI overrides: each one that is set must sit on the web app origin ({NEXT_PUBLIC_SPFN_APP_URL || SPFN_APP_URL}) at /_auth/oauth/<provider>/callback, because the callback CSRF cookie is host-only. "off" is the only value that disables the check \u2014 unset, "on" and anything else all run it.',
7679
+ required: false,
7680
+ examples: ["off"]
7681
+ })
7682
+ },
7676
7683
  // ============================================================================
7677
7684
  // Native Social Login (mobile/web id_token verification)
7678
7685
  //
@@ -8474,6 +8481,7 @@ function validatePasswordStrength(password) {
8474
8481
  import jwt from "jsonwebtoken";
8475
8482
  import crypto2 from "crypto";
8476
8483
  import { env as env2 } from "@spfn/auth/config";
8484
+ import { KeyAlgorithmMismatchError } from "@spfn/auth/errors";
8477
8485
  var PUBLIC_KEY_CACHE_MAX = 1e3;
8478
8486
  var publicKeyCache = /* @__PURE__ */ new Map();
8479
8487
  function getPublicKeyObject(publicKeyB64) {
@@ -8554,6 +8562,39 @@ function verifyKeyFingerprint(publicKeyB64, expectedFingerprint) {
8554
8562
  return false;
8555
8563
  }
8556
8564
  }
8565
+ function readPublicKey(publicKeyB64) {
8566
+ try {
8567
+ return getPublicKeyObject(publicKeyB64);
8568
+ } catch {
8569
+ return null;
8570
+ }
8571
+ }
8572
+ function describeKeyType(key) {
8573
+ if (!key) {
8574
+ return "not a readable SPKI public key";
8575
+ }
8576
+ if (key.asymmetricKeyType === "ec") {
8577
+ return `a ${key.asymmetricKeyDetails?.namedCurve ?? "unknown-curve"} EC key`;
8578
+ }
8579
+ return `a ${key.asymmetricKeyType ?? "unrecognised"} key`;
8580
+ }
8581
+ function keyMatchesAlgorithm(key, algorithm) {
8582
+ if (!key) {
8583
+ return false;
8584
+ }
8585
+ if (algorithm === "ES256") {
8586
+ return key.asymmetricKeyType === "ec" && key.asymmetricKeyDetails?.namedCurve === "prime256v1";
8587
+ }
8588
+ return key.asymmetricKeyType === "rsa";
8589
+ }
8590
+ function assertKeyMatchesAlgorithm(publicKeyB64, algorithm) {
8591
+ const key = readPublicKey(publicKeyB64);
8592
+ if (!keyMatchesAlgorithm(key, algorithm)) {
8593
+ throw new KeyAlgorithmMismatchError({
8594
+ message: `Public key is ${describeKeyType(key)} but the declared algorithm is ${algorithm}`
8595
+ });
8596
+ }
8597
+ }
8557
8598
 
8558
8599
  // src/server/helpers/context.ts
8559
8600
  function getAuth(c) {
@@ -8950,6 +8991,7 @@ async function registerPublicKeyService(params) {
8950
8991
  if (!isValidFingerprint) {
8951
8992
  throw new InvalidKeyFingerprintError();
8952
8993
  }
8994
+ assertKeyMatchesAlgorithm(publicKey, algorithm);
8953
8995
  await keysRepository.create({
8954
8996
  userId,
8955
8997
  keyId,
@@ -8968,6 +9010,7 @@ async function rotateKeyService(params) {
8968
9010
  if (!isValidFingerprint) {
8969
9011
  throw new InvalidKeyFingerprintError();
8970
9012
  }
9013
+ assertKeyMatchesAlgorithm(newPublicKey, algorithm);
8971
9014
  const replaced = await keysRepository.findByKeyIdAndUserId(oldKeyId, userId);
8972
9015
  await keysRepository.revokeByKeyIdAndUserId(
8973
9016
  oldKeyId,
@@ -9912,6 +9955,7 @@ async function startDeviceAuthService(params) {
9912
9955
  if (!verifyKeyFingerprint(params.publicKey, params.fingerprint)) {
9913
9956
  throw new InvalidKeyFingerprintError2();
9914
9957
  }
9958
+ assertKeyMatchesAlgorithm(params.publicKey, params.algorithm ?? DEFAULT_KEY_ALGORITHM);
9915
9959
  const { ttlMs, intervalMs } = getDeviceAuthConfig();
9916
9960
  const expiresAt = new Date(Date.now() + ttlMs);
9917
9961
  for (let attempt = 0; attempt < USER_CODE_ATTEMPTS; attempt++) {
@@ -10377,6 +10421,7 @@ async function validateInvitation(token) {
10377
10421
  }
10378
10422
  async function acceptInvitation(params) {
10379
10423
  const { token, password, publicKey, keyId, fingerprint, algorithm } = params;
10424
+ assertKeyMatchesAlgorithm(publicKey, algorithm);
10380
10425
  const validation = await validateInvitation(token);
10381
10426
  if (!validation.valid || !validation.invitation) {
10382
10427
  throw new BadRequestError({ message: validation.error || "Invalid invitation" });
@@ -12966,7 +13011,7 @@ var CORE_PREREQUISITE_OPERATIONS = [
12966
13011
 
12967
13012
  // src/server/client-proof/contract-bundle.ts
12968
13013
  init_wire_headers();
12969
- var CONTRACT_VERSION = "0.10.0";
13014
+ var CONTRACT_VERSION = "0.10.1";
12970
13015
  var CONTRACT_SUPPORTED_RANGE = ">=0.10.0 <0.11.0";
12971
13016
  function required(name, type) {
12972
13017
  return { name, type, optional: false };
@@ -15510,6 +15555,72 @@ async function ensureAdminExists() {
15510
15555
  }
15511
15556
  }
15512
15557
 
15558
+ // src/server/lib/oauth/redirect-uri-check.ts
15559
+ var PROVIDERS = ["google", "kakao", "naver", "github"];
15560
+ var OPT_OUT_VAR = "SPFN_AUTH_OAUTH_CALLBACK_ORIGIN_CHECK";
15561
+ function redirectUriVar(provider) {
15562
+ return `SPFN_AUTH_${provider.toUpperCase()}_REDIRECT_URI`;
15563
+ }
15564
+ function resolveWebAppOrigin(env16) {
15565
+ const configured2 = env16.NEXT_PUBLIC_SPFN_APP_URL || env16.SPFN_APP_URL;
15566
+ if (!configured2) {
15567
+ return null;
15568
+ }
15569
+ try {
15570
+ return new URL(configured2).origin;
15571
+ } catch {
15572
+ return null;
15573
+ }
15574
+ }
15575
+ function conformanceDetail(value, webAppOrigin, callbackPath) {
15576
+ let url;
15577
+ try {
15578
+ url = new URL(value);
15579
+ } catch {
15580
+ return " The value is not a URL.";
15581
+ }
15582
+ if (url.origin !== webAppOrigin || url.pathname !== callbackPath) {
15583
+ return "";
15584
+ }
15585
+ if (url.search !== "" || url.hash !== "") {
15586
+ return " The value carries a query string or fragment; the callback URL is the path alone.";
15587
+ }
15588
+ return null;
15589
+ }
15590
+ function providerRefusal(provider, env16, webAppOrigin) {
15591
+ const variable = redirectUriVar(provider);
15592
+ const value = env16[variable];
15593
+ if (!value) {
15594
+ return null;
15595
+ }
15596
+ const callbackPath = `/_auth/oauth/${provider}/callback`;
15597
+ const detail = conformanceDetail(value, webAppOrigin, callbackPath);
15598
+ if (detail === null) {
15599
+ return null;
15600
+ }
15601
+ return `${variable} must be on the web app origin (${webAppOrigin}) at ${callbackPath}: the callback's CSRF cookie is host-only and /_auth/* is forwarded to the API by the app's rewrite. Unset it to use the default, fix the origin, or set ${OPT_OUT_VAR}=off for a deployment that deliberately terminates the callback elsewhere.${detail}`;
15602
+ }
15603
+ function assertOAuthRedirectUris(env16 = process.env) {
15604
+ const examined = PROVIDERS.map(redirectUriVar).join(", ");
15605
+ if (env16[OPT_OUT_VAR] === "off") {
15606
+ authLogger.service.info(
15607
+ `${OPT_OUT_VAR}=off: the OAuth callback origin check is disabled. Not examined: ${examined}.`
15608
+ );
15609
+ return;
15610
+ }
15611
+ const webAppOrigin = resolveWebAppOrigin(env16);
15612
+ if (!webAppOrigin) {
15613
+ authLogger.service.info(
15614
+ `The OAuth callback origin check was skipped: neither NEXT_PUBLIC_SPFN_APP_URL nor SPFN_APP_URL resolves to a parseable URL, which the startup env validation reports on its own. Not examined: ${examined}.`
15615
+ );
15616
+ return;
15617
+ }
15618
+ const refusals = PROVIDERS.map((provider) => providerRefusal(provider, env16, webAppOrigin)).filter((refusal) => refusal !== null);
15619
+ if (refusals.length > 0) {
15620
+ throw new Error(refusals.join("\n"));
15621
+ }
15622
+ }
15623
+
15513
15624
  // src/server/lifecycle.ts
15514
15625
  function createAuthLifecycle(options = {}) {
15515
15626
  configureDeletion(options.deletion);
@@ -15519,11 +15630,13 @@ function createAuthLifecycle(options = {}) {
15519
15630
  * Initialize auth system after database is ready
15520
15631
  *
15521
15632
  * Performs:
15633
+ * 0. Refuses boot on an OAuth redirect URI override off the web app origin
15522
15634
  * 1. Ensures admin account exists (creates if missing)
15523
15635
  * 2. Initializes RBAC system with built-in + custom roles/permissions
15524
15636
  * 3. Initializes one-time token manager
15525
15637
  */
15526
15638
  afterInfrastructure: async () => {
15639
+ assertOAuthRedirectUris();
15527
15640
  await initializeAuth(options);
15528
15641
  try {
15529
15642
  await normalizeStoredEmails();
@@ -15617,6 +15730,7 @@ export {
15617
15730
  appleProvider,
15618
15731
  approveDeviceAuthService,
15619
15732
  assertCanAssignRole,
15733
+ assertKeyMatchesAlgorithm,
15620
15734
  authDeletionCancelledEvent,
15621
15735
  authDeletionCompletedEvent,
15622
15736
  authDeletionRequestedEvent,