@spfn/auth 0.3.0-beta.16 → 0.3.0-beta.17

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -949,6 +949,16 @@ Both convenience URL APIs seal `metadata` into the encrypted OAuth state. On a n
949
949
  signup, the callback passes it to `beforeRegister` and `authRegisterEvent`; existing-account
950
950
  logins do not run the registration hook.
951
951
 
952
+ `returnUrl` must be a path inside your app — absolute URLs, `//host`, `..`, a backslash, and a
953
+ tab/CR/LF (which a URL parser strips, turning `/<tab>/host` into `//host`) are refused, so a
954
+ real login cannot become an open redirect. The start seams answer an unsafe value with a 400
955
+ `ValidationError`; the seams that already hold a logged-in user replace the destination instead
956
+ of failing the login — `OAuthCallback` navigates to `/` and `createOAuthCallbackHandler`
957
+ redirects to its `defaultRedirectUrl` (`/` unless you pass one). The rule is exported as
958
+ `isSafeReturnPath` from `@spfn/auth/server`, `@spfn/auth/nextjs/server`, and
959
+ `@spfn/auth/nextjs/client` for apps that validate a destination before calling
960
+ `getGoogleOAuthUrl`.
961
+
952
962
  Built-in OAuth routes: `POST /_auth/oauth/google/url`, `GET /_auth/oauth/google` (redirect),
953
963
  `GET /_auth/oauth/google/callback`, `POST /_auth/oauth/finalize`, `GET /_auth/oauth/providers`,
954
964
  plus the provider-generic `POST /_auth/oauth/start`. `getGoogleAccessToken(userId)` returns a
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as _spfn_core_nextjs from '@spfn/core/nextjs';
2
- import { P as PermissionConfig, R as RoleConfig, m as mainAuthRouter, S as SendVerificationCodeResult, a as RegisterResult, b as RequestSignupLinkResult, C as ConfirmSignupLinkResult, c as RequestPasswordResetResult, d as ConfirmPasswordResetResult, L as LoginResult, e as StartDeviceAuthResult, D as DeviceAuthInfoResult, F as FinishPasskeyEnrollmentResult, f as PasskeySummary, g as RotateKeyResult, K as KeySummary, h as RevokeAllKeysResult, I as IssueOneTimeTokenResult, O as OAuthStartResult, i as OAuthNativeResult, U as UserProfile, j as ProfileInfo } from './machine-principals-Dn-7BqYZ.js';
3
- export { A as AuthInitOptions, k as AuthSession, l as PERMISSION_CATEGORIES, n as PermissionCategory, V as VERIFICATION_PURPOSES, o as VERIFICATION_TARGET_TYPES, p as VerificationPurpose, q as VerificationTargetType } from './machine-principals-Dn-7BqYZ.js';
2
+ import { P as PermissionConfig, R as RoleConfig, m as mainAuthRouter, S as SendVerificationCodeResult, a as RegisterResult, b as RequestSignupLinkResult, C as ConfirmSignupLinkResult, c as RequestPasswordResetResult, d as ConfirmPasswordResetResult, L as LoginResult, e as StartDeviceAuthResult, D as DeviceAuthInfoResult, F as FinishPasskeyEnrollmentResult, f as PasskeySummary, g as RotateKeyResult, K as KeySummary, h as RevokeAllKeysResult, I as IssueOneTimeTokenResult, O as OAuthStartResult, i as OAuthNativeResult, U as UserProfile, j as ProfileInfo } from './machine-principals-nrpFSvvB.js';
3
+ export { A as AuthInitOptions, k as AuthSession, l as PERMISSION_CATEGORIES, n as PermissionCategory, V as VERIFICATION_PURPOSES, o as VERIFICATION_TARGET_TYPES, p as VerificationPurpose, q as VerificationTargetType } from './machine-principals-nrpFSvvB.js';
4
4
  import * as _simplewebauthn_server from '@simplewebauthn/server';
5
5
  import * as _spfn_core_route from '@spfn/core/route';
6
6
  import { HttpMethod } from '@spfn/core/route';
@@ -300,15 +300,6 @@ declare function verifyCodeService(params: VerifyCodeParams): Promise<{
300
300
  * is a second entry point to the same account creation, not a replacement.
301
301
  */
302
302
 
303
- /**
304
- * Whether a return path can be handed back to the browser.
305
- *
306
- * Only a path within the app is allowed. The rejected shapes are the ones that
307
- * turn a return path into an open redirect: an absolute URL, a protocol-relative
308
- * `//host` that a browser reads as another origin, a backslash that some
309
- * browsers normalize into a slash, and any `..` traversal.
310
- */
311
- declare function isSafeReturnPath(returnPath: string): boolean;
312
303
  interface RequestSignupLinkParams {
313
304
  email: string;
314
305
  returnPath?: string;
@@ -2575,4 +2566,4 @@ declare const machineAuth: _spfn_core_route.NamedMiddleware<"machineAuth">;
2575
2566
  */
2576
2567
  declare const requireMachineScope: _spfn_core_route.NamedMiddlewareFactory<"machineScope", string[]>;
2577
2568
 
2578
- export { KeyIdSchema as $, type AuthInitOptions as A, type ConfirmPasswordResetParams as B, type ConfirmSignupLinkResult as C, type DeviceAuthInfoResult as D, type ConfirmSignupLinkParams as E, type FinishPasskeyEnrollmentResult as F, type DenyDeviceAuthParams as G, type DeviceAuthApprovedResult as H, type IssueOneTimeTokenResult as I, type DeviceAuthInfoParams as J, type KeySummary as K, type LoginResult as L, type DeviceAuthPendingResult as M, type NewPasskey as N, type OAuthStartResult as O, type PermissionConfig as P, DeviceAuthPollResponseSchema as Q, type RoleConfig as R, type SendVerificationCodeResult as S, DeviceNameSchema as T, type UserProfile as U, VERIFICATION_PURPOSES as V, EmailSchema as W, FingerprintSchema as X, type FinishPasskeyEnrollmentParams as Y, type FinishPasskeyLoginParams as Z, KEY_FINGERPRINT_PREFIX_LENGTH as _, type RegisterResult as a, isSafeReturnPath as a$, type LoginParams as a0, type LogoutParams as a1, type MachinePrincipal as a2, type MachineVerifierRegistration as a3, type NativeVerifyOptions as a4, type NormalizedIdentity as a5, type OAuthCallbackParams as a6, type OAuthCallbackResult as a7, type OAuthCodeExchangeOptions as a8, type OAuthNativeParams as a9, UnlinkNotifyRejection as aA, type UnlinkNotifyRequest as aB, type UnlinkNotifyResult as aC, UserCodeSchema as aD, VerificationPurposeSchema as aE, type VerifyCodeParams as aF, type VerifyCodeResult as aG, approveDeviceAuthService as aH, assertNotLastRecoveryCredential as aI, assertRecentAuthentication as aJ, authenticate as aK, buildOAuthErrorUrl as aL, changePasswordService as aM, completePasswordResetService as aN, completeSignupService as aO, confirmPasswordResetService as aP, confirmSignupLinkService as aQ, denyDeviceAuthService as aR, finishPasskeyEnrollmentService as aS, finishPasskeyLoginService as aT, getDeviceAuthInfoService as aU, getEnabledOAuthProviders as aV, getGoogleAccessToken as aW, getMachinePrincipal as aX, getOAuthProvider as aY, getRegisteredProviders as aZ, isOAuthProviderEnabled as a_, type OAuthStartParams as aa, type OAuthTokens as ab, PASSKEY_DEVICE_TYPES as ac, PASSKEY_LABEL_MAX_LENGTH as ad, type PasskeyDeviceType as ae, PasswordSchema as af, PhoneSchema as ag, PlatformSchema as ah, type PollDeviceAuthParams as ai, type PollDeviceAuthResult as aj, PublicKeySchema as ak, type RecentAuthenticationParams as al, type RegisterParams as am, type RegisterPublicKeyParams as an, type RenamePasskeyParams as ao, type RequestPasswordResetParams as ap, type RequestSignupLinkParams as aq, type RevokeAllKeysParams as ar, type RevokeKeyParams as as, type RevokePasskeyParams as at, type RotateKeyParams as au, type SendVerificationCodeParams as av, type StartDeviceAuthParams as aw, type StartPasskeyEnrollmentParams as ax, TargetTypeSchema as ay, type UnlinkNotification as az, type RequestSignupLinkResult as b, issueOneTimeTokenService as b0, listKeysService as b1, listPasskeysService as b2, loginService as b3, logoutService as b4, machineAuth as b5, oauthCallbackService as b6, oauthNativeService as b7, oauthStartService as b8, oauthUnlinkNotifyService as b9, optionalAuth as ba, passkeys as bb, pollDeviceAuthService as bc, registerAuthProfile as bd, registerMachineVerifier as be, registerOAuthProvider as bf, registerPublicKeyService as bg, registerService as bh, renamePasskeyService as bi, requestPasswordResetService as bj, requestSignupLinkService as bk, requireEnabledProvider as bl, requireMachineScope as bm, resolveAuthenticatedUser as bn, revokeAllKeysService as bo, revokeKeyService as bp, revokePasskeyService as bq, rotateKeyService as br, runAuthProfile as bs, selectAuthProfile as bt, sendVerificationCodeService as bu, startDeviceAuthService as bv, startPasskeyEnrollmentService as bw, startPasskeyLoginService as bx, verifyCodeService as by, verifyOneTimeTokenService as bz, type RequestPasswordResetResult as c, type ConfirmPasswordResetResult as d, type StartDeviceAuthResult as e, type PasskeySummary as f, type RotateKeyResult as g, type RevokeAllKeysResult as h, type OAuthNativeResult as i, type ProfileInfo as j, type AuthSession as k, PERMISSION_CATEGORIES as l, mainAuthRouter as m, type PermissionCategory as n, VERIFICATION_TARGET_TYPES as o, type VerificationPurpose as p, type VerificationTargetType as q, type OAuthProvider as r, type Passkey as s, type AuthContext as t, type ApproveDeviceAuthParams as u, type AuthProfileOutcome as v, type AuthProfileVerifier as w, type ChangePasswordParams as x, type CompletePasswordResetParams as y, type CompleteSignupParams as z };
2569
+ export { KeyIdSchema as $, type AuthInitOptions as A, type ConfirmPasswordResetParams as B, type ConfirmSignupLinkResult as C, type DeviceAuthInfoResult as D, type ConfirmSignupLinkParams as E, type FinishPasskeyEnrollmentResult as F, type DenyDeviceAuthParams as G, type DeviceAuthApprovedResult as H, type IssueOneTimeTokenResult as I, type DeviceAuthInfoParams as J, type KeySummary as K, type LoginResult as L, type DeviceAuthPendingResult as M, type NewPasskey as N, type OAuthStartResult as O, type PermissionConfig as P, DeviceAuthPollResponseSchema as Q, type RoleConfig as R, type SendVerificationCodeResult as S, DeviceNameSchema as T, type UserProfile as U, VERIFICATION_PURPOSES as V, EmailSchema as W, FingerprintSchema as X, type FinishPasskeyEnrollmentParams as Y, type FinishPasskeyLoginParams as Z, KEY_FINGERPRINT_PREFIX_LENGTH as _, type RegisterResult as a, issueOneTimeTokenService as a$, type LoginParams as a0, type LogoutParams as a1, type MachinePrincipal as a2, type MachineVerifierRegistration as a3, type NativeVerifyOptions as a4, type NormalizedIdentity as a5, type OAuthCallbackParams as a6, type OAuthCallbackResult as a7, type OAuthCodeExchangeOptions as a8, type OAuthNativeParams as a9, UnlinkNotifyRejection as aA, type UnlinkNotifyRequest as aB, type UnlinkNotifyResult as aC, UserCodeSchema as aD, VerificationPurposeSchema as aE, type VerifyCodeParams as aF, type VerifyCodeResult as aG, approveDeviceAuthService as aH, assertNotLastRecoveryCredential as aI, assertRecentAuthentication as aJ, authenticate as aK, buildOAuthErrorUrl as aL, changePasswordService as aM, completePasswordResetService as aN, completeSignupService as aO, confirmPasswordResetService as aP, confirmSignupLinkService as aQ, denyDeviceAuthService as aR, finishPasskeyEnrollmentService as aS, finishPasskeyLoginService as aT, getDeviceAuthInfoService as aU, getEnabledOAuthProviders as aV, getGoogleAccessToken as aW, getMachinePrincipal as aX, getOAuthProvider as aY, getRegisteredProviders as aZ, isOAuthProviderEnabled as a_, type OAuthStartParams as aa, type OAuthTokens as ab, PASSKEY_DEVICE_TYPES as ac, PASSKEY_LABEL_MAX_LENGTH as ad, type PasskeyDeviceType as ae, PasswordSchema as af, PhoneSchema as ag, PlatformSchema as ah, type PollDeviceAuthParams as ai, type PollDeviceAuthResult as aj, PublicKeySchema as ak, type RecentAuthenticationParams as al, type RegisterParams as am, type RegisterPublicKeyParams as an, type RenamePasskeyParams as ao, type RequestPasswordResetParams as ap, type RequestSignupLinkParams as aq, type RevokeAllKeysParams as ar, type RevokeKeyParams as as, type RevokePasskeyParams as at, type RotateKeyParams as au, type SendVerificationCodeParams as av, type StartDeviceAuthParams as aw, type StartPasskeyEnrollmentParams as ax, TargetTypeSchema as ay, type UnlinkNotification as az, type RequestSignupLinkResult as b, listKeysService as b0, listPasskeysService as b1, loginService as b2, logoutService as b3, machineAuth as b4, oauthCallbackService as b5, oauthNativeService as b6, oauthStartService as b7, oauthUnlinkNotifyService as b8, optionalAuth as b9, passkeys as ba, pollDeviceAuthService as bb, registerAuthProfile as bc, registerMachineVerifier as bd, registerOAuthProvider as be, registerPublicKeyService as bf, registerService as bg, renamePasskeyService as bh, requestPasswordResetService as bi, requestSignupLinkService as bj, requireEnabledProvider as bk, requireMachineScope as bl, resolveAuthenticatedUser as bm, revokeAllKeysService as bn, revokeKeyService as bo, revokePasskeyService as bp, rotateKeyService as bq, runAuthProfile as br, selectAuthProfile as bs, sendVerificationCodeService as bt, startDeviceAuthService as bu, startPasskeyEnrollmentService as bv, startPasskeyLoginService as bw, verifyCodeService as bx, verifyOneTimeTokenService as by, type RequestPasswordResetResult as c, type ConfirmPasswordResetResult as d, type StartDeviceAuthResult as e, type PasskeySummary as f, type RotateKeyResult as g, type RevokeAllKeysResult as h, type OAuthNativeResult as i, type ProfileInfo as j, type AuthSession as k, PERMISSION_CATEGORIES as l, mainAuthRouter as m, type PermissionCategory as n, VERIFICATION_TARGET_TYPES as o, type VerificationPurpose as p, type VerificationTargetType as q, type OAuthProvider as r, type Passkey as s, type AuthContext as t, type ApproveDeviceAuthParams as u, type AuthProfileOutcome as v, type AuthProfileVerifier as w, type ChangePasswordParams as x, type CompletePasswordResetParams as y, type CompleteSignupParams as z };
@@ -846,6 +846,21 @@ async function createOAuthState(params) {
846
846
  return encodeURIComponent(jwe);
847
847
  }
848
848
 
849
+ // src/lib/return-path.ts
850
+ var URL_STRIPPED_CHARACTER = /[\t\n\r]/;
851
+ function isSafeReturnPath(returnPath) {
852
+ if (!returnPath.startsWith("/")) {
853
+ return false;
854
+ }
855
+ if (returnPath.startsWith("//") || returnPath.includes("\\")) {
856
+ return false;
857
+ }
858
+ if (returnPath.includes("..") || URL_STRIPPED_CHARACTER.test(returnPath)) {
859
+ return false;
860
+ }
861
+ return !/^\/[^/?#]*:/.test(returnPath);
862
+ }
863
+
849
864
  // src/nextjs/session-helpers.ts
850
865
  import * as jose3 from "jose";
851
866
  import { cookies } from "next/headers.js";
@@ -872,6 +887,20 @@ async function unsealPendingSession(jwt2) {
872
887
  }
873
888
 
874
889
  // src/nextjs/interceptors/oauth.ts
890
+ var UNSAFE_RETURN_URL_MESSAGE = "returnUrl must be a relative path within the app";
891
+ function refuseUnsafeReturnUrl() {
892
+ return {
893
+ status: 400,
894
+ body: {
895
+ __type: "ValidationError",
896
+ message: UNSAFE_RETURN_URL_MESSAGE,
897
+ error: {
898
+ code: "ValidationError",
899
+ message: UNSAFE_RETURN_URL_MESSAGE
900
+ }
901
+ }
902
+ };
903
+ }
875
904
  var oauthUrlInterceptor = {
876
905
  pathPattern: /^\/_auth\/oauth\/\w+\/url$/,
877
906
  method: "POST",
@@ -879,6 +908,13 @@ var oauthUrlInterceptor = {
879
908
  const provider = ctx.path.split("/")[3];
880
909
  const returnUrl = ctx.body?.returnUrl || "/";
881
910
  const metadata = ctx.body?.metadata;
911
+ if (typeof returnUrl !== "string" || !isSafeReturnPath(returnUrl)) {
912
+ authLogger.interceptor.oauth?.warn?.("OAuth start refused: returnUrl is not a path within the app", {
913
+ provider
914
+ });
915
+ ctx.abort = refuseUnsafeReturnUrl();
916
+ return;
917
+ }
882
918
  const keyPair = generateKeyPair("ES256");
883
919
  const csrfNonce = generateOAuthNonce();
884
920
  const state = await createOAuthState({