@spfn/auth 0.2.0-beta.25 → 0.2.0-beta.26

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
@@ -602,6 +602,7 @@ import {
602
602
  ```typescript
603
603
  import {
604
604
  authenticate,
605
+ optionalAuth,
605
606
  requirePermissions,
606
607
  requireAnyPermission,
607
608
  requireRole,
@@ -624,6 +625,18 @@ app.bind(
624
625
  // User has either content:read OR admin:access
625
626
  }
626
627
  );
628
+
629
+ // Usage - optional auth (public route with optional user context)
630
+ // Auto-skips global 'auth' middleware — no .skip(['auth']) needed
631
+ export const getProducts = route.get('/products')
632
+ .use([optionalAuth])
633
+ .handler(async (c) => {
634
+ const auth = getOptionalAuth(c); // AuthContext | undefined
635
+ if (auth) {
636
+ return getPersonalizedProducts(auth.userId);
637
+ }
638
+ return getPublicProducts();
639
+ });
627
640
  ```
628
641
 
629
642
  **Helpers:**
@@ -631,6 +644,7 @@ app.bind(
631
644
  import {
632
645
  // Context
633
646
  getAuth,
647
+ getOptionalAuth,
634
648
  getUser,
635
649
  getUserId,
636
650
  getKeyId,
@@ -2412,6 +2426,6 @@ MIT License - See LICENSE file for details.
2412
2426
 
2413
2427
  ---
2414
2428
 
2415
- **Last Updated:** 2026-01-29
2416
- **Document Version:** 2.5.0 (Technical Documentation)
2429
+ **Last Updated:** 2026-02-23
2430
+ **Document Version:** 2.6.0 (Technical Documentation)
2417
2431
  **Package Version:** 0.2.0-beta.15
@@ -541,7 +541,7 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
541
541
  id: number;
542
542
  name: string;
543
543
  displayName: string;
544
- category: "custom" | "user" | "auth" | "rbac" | "system" | undefined;
544
+ category: "auth" | "custom" | "user" | "rbac" | "system" | undefined;
545
545
  }[];
546
546
  userId: number;
547
547
  email: string | null;
@@ -837,5 +837,33 @@ declare module 'hono' {
837
837
  * ```
838
838
  */
839
839
  declare const authenticate: _spfn_core_route.NamedMiddleware<"auth">;
840
+ /**
841
+ * Optional authentication middleware
842
+ *
843
+ * Same as `authenticate` but does NOT reject unauthenticated requests.
844
+ * - No token → continues without auth context
845
+ * - Invalid token → continues without auth context
846
+ * - Valid token → sets auth context normally
847
+ *
848
+ * Auto-skips the global 'auth' middleware when used at route level.
849
+ *
850
+ * @example
851
+ * ```typescript
852
+ * // No need for .skip(['auth']) — handled automatically
853
+ * export const getProducts = route.get('/products')
854
+ * .use([optionalAuth])
855
+ * .handler(async (c) => {
856
+ * const auth = getOptionalAuth(c); // AuthContext | undefined
857
+ *
858
+ * if (auth)
859
+ * {
860
+ * return getPersonalizedProducts(auth.userId);
861
+ * }
862
+ *
863
+ * return getPublicProducts();
864
+ * });
865
+ * ```
866
+ */
867
+ declare const optionalAuth: _spfn_core_route.NamedMiddleware<"optionalAuth">;
840
868
 
841
- export { getEnabledOAuthProviders as $, type AuthSession as A, type ChangePasswordParams as B, type CheckAccountExistsResult as C, sendVerificationCodeService as D, verifyCodeService as E, type SendVerificationCodeParams as F, type VerifyCodeParams as G, type VerifyCodeResult as H, INVITATION_STATUSES as I, registerPublicKeyService as J, KEY_ALGORITHM as K, type LoginResult as L, rotateKeyService as M, revokeKeyService as N, type OAuthStartResult as O, type PermissionConfig as P, type RegisterPublicKeyParams as Q, type RoleConfig as R, type SendVerificationCodeResult as S, type RotateKeyParams as T, type UserProfile as U, type VerificationTargetType as V, type RevokeKeyParams as W, oauthStartService as X, oauthCallbackService as Y, buildOAuthErrorUrl as Z, isOAuthProviderEnabled as _, type RegisterResult as a, getGoogleAccessToken as a0, type OAuthStartParams as a1, type OAuthCallbackParams as a2, type OAuthCallbackResult as a3, authenticate as a4, EmailSchema as a5, PhoneSchema as a6, PasswordSchema as a7, TargetTypeSchema as a8, VerificationPurposeSchema as a9, type RotateKeyResult as b, type ProfileInfo as c, USER_STATUSES as d, SOCIAL_PROVIDERS as e, type VerificationPurpose as f, VERIFICATION_TARGET_TYPES as g, VERIFICATION_PURPOSES as h, PERMISSION_CATEGORIES as i, type PermissionCategory as j, type AuthInitOptions as k, type KeyAlgorithmType as l, mainAuthRouter as m, type InvitationStatus as n, type UserStatus as o, type SocialProvider as p, type AuthContext as q, checkAccountExistsService as r, registerService as s, loginService as t, logoutService as u, changePasswordService as v, type CheckAccountExistsParams as w, type RegisterParams as x, type LoginParams as y, type LogoutParams as z };
869
+ export { getEnabledOAuthProviders as $, type AuthSession as A, type ChangePasswordParams as B, type CheckAccountExistsResult as C, sendVerificationCodeService as D, verifyCodeService as E, type SendVerificationCodeParams as F, type VerifyCodeParams as G, type VerifyCodeResult as H, INVITATION_STATUSES as I, registerPublicKeyService as J, KEY_ALGORITHM as K, type LoginResult as L, rotateKeyService as M, revokeKeyService as N, type OAuthStartResult as O, type PermissionConfig as P, type RegisterPublicKeyParams as Q, type RoleConfig as R, type SendVerificationCodeResult as S, type RotateKeyParams as T, type UserProfile as U, type VerificationTargetType as V, type RevokeKeyParams as W, oauthStartService as X, oauthCallbackService as Y, buildOAuthErrorUrl as Z, isOAuthProviderEnabled as _, type RegisterResult as a, getGoogleAccessToken as a0, type OAuthStartParams as a1, type OAuthCallbackParams as a2, type OAuthCallbackResult as a3, authenticate as a4, optionalAuth as a5, EmailSchema as a6, PhoneSchema as a7, PasswordSchema as a8, TargetTypeSchema as a9, VerificationPurposeSchema as aa, type RotateKeyResult as b, type ProfileInfo as c, USER_STATUSES as d, SOCIAL_PROVIDERS as e, type VerificationPurpose as f, VERIFICATION_TARGET_TYPES as g, VERIFICATION_PURPOSES as h, PERMISSION_CATEGORIES as i, type PermissionCategory as j, type AuthInitOptions as k, type KeyAlgorithmType as l, mainAuthRouter as m, type InvitationStatus as n, type UserStatus as o, type SocialProvider as p, type AuthContext as q, checkAccountExistsService as r, registerService as s, loginService as t, logoutService as u, changePasswordService as v, type CheckAccountExistsParams as w, type RegisterParams as x, type LoginParams as y, type LogoutParams as z };
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import * as _spfn_core_nextjs from '@spfn/core/nextjs';
2
- import { R as RoleConfig, P as PermissionConfig, C as CheckAccountExistsResult, S as SendVerificationCodeResult, a as RegisterResult, L as LoginResult, b as RotateKeyResult, O as OAuthStartResult, U as UserProfile, c as ProfileInfo, m as mainAuthRouter } from './authenticate-DAAEfzxa.js';
3
- export { k as AuthInitOptions, A as AuthSession, I as INVITATION_STATUSES, n as InvitationStatus, K as KEY_ALGORITHM, l as KeyAlgorithmType, i as PERMISSION_CATEGORIES, j as PermissionCategory, e as SOCIAL_PROVIDERS, p as SocialProvider, d as USER_STATUSES, o as UserStatus, h as VERIFICATION_PURPOSES, g as VERIFICATION_TARGET_TYPES, f as VerificationPurpose, V as VerificationTargetType } from './authenticate-DAAEfzxa.js';
2
+ import { R as RoleConfig, P as PermissionConfig, C as CheckAccountExistsResult, S as SendVerificationCodeResult, a as RegisterResult, L as LoginResult, b as RotateKeyResult, O as OAuthStartResult, U as UserProfile, c as ProfileInfo, m as mainAuthRouter } from './authenticate-kCg_KD-V.js';
3
+ export { k as AuthInitOptions, A as AuthSession, I as INVITATION_STATUSES, n as InvitationStatus, K as KEY_ALGORITHM, l as KeyAlgorithmType, i as PERMISSION_CATEGORIES, j as PermissionCategory, e as SOCIAL_PROVIDERS, p as SocialProvider, d as USER_STATUSES, o as UserStatus, h as VERIFICATION_PURPOSES, g as VERIFICATION_TARGET_TYPES, f as VerificationPurpose, V as VerificationTargetType } from './authenticate-kCg_KD-V.js';
4
4
  import * as _spfn_core_route from '@spfn/core/route';
5
5
  import { HttpMethod } from '@spfn/core/route';
6
6
  import * as _sinclair_typebox from '@sinclair/typebox';
@@ -168,7 +168,7 @@ declare const authApi: _spfn_core_nextjs.Client<_spfn_core_route.Router<{
168
168
  id: number;
169
169
  name: string;
170
170
  displayName: string;
171
- category: "custom" | "user" | "auth" | "rbac" | "system" | undefined;
171
+ category: "auth" | "custom" | "user" | "rbac" | "system" | undefined;
172
172
  }[];
173
173
  userId: number;
174
174
  email: string | null;
package/dist/server.d.ts CHANGED
@@ -1,5 +1,5 @@
1
- import { k as AuthInitOptions, l as KeyAlgorithmType, n as InvitationStatus, f as VerificationPurpose, j as PermissionCategory, p as SocialProvider, q as AuthContext } from './authenticate-DAAEfzxa.js';
2
- export { B as ChangePasswordParams, w as CheckAccountExistsParams, C as CheckAccountExistsResult, a5 as EmailSchema, I as INVITATION_STATUSES, K as KEY_ALGORITHM, y as LoginParams, L as LoginResult, z as LogoutParams, a2 as OAuthCallbackParams, a3 as OAuthCallbackResult, a1 as OAuthStartParams, O as OAuthStartResult, a7 as PasswordSchema, a6 as PhoneSchema, x as RegisterParams, Q as RegisterPublicKeyParams, a as RegisterResult, W as RevokeKeyParams, T as RotateKeyParams, b as RotateKeyResult, e as SOCIAL_PROVIDERS, F as SendVerificationCodeParams, S as SendVerificationCodeResult, a8 as TargetTypeSchema, d as USER_STATUSES, o as UserStatus, h as VERIFICATION_PURPOSES, g as VERIFICATION_TARGET_TYPES, a9 as VerificationPurposeSchema, V as VerificationTargetType, G as VerifyCodeParams, H as VerifyCodeResult, m as authRouter, a4 as authenticate, Z as buildOAuthErrorUrl, v as changePasswordService, r as checkAccountExistsService, $ as getEnabledOAuthProviders, a0 as getGoogleAccessToken, _ as isOAuthProviderEnabled, t as loginService, u as logoutService, Y as oauthCallbackService, X as oauthStartService, J as registerPublicKeyService, s as registerService, N as revokeKeyService, M as rotateKeyService, D as sendVerificationCodeService, E as verifyCodeService } from './authenticate-DAAEfzxa.js';
1
+ import { k as AuthInitOptions, l as KeyAlgorithmType, n as InvitationStatus, f as VerificationPurpose, j as PermissionCategory, p as SocialProvider, q as AuthContext } from './authenticate-kCg_KD-V.js';
2
+ export { B as ChangePasswordParams, w as CheckAccountExistsParams, C as CheckAccountExistsResult, a6 as EmailSchema, I as INVITATION_STATUSES, K as KEY_ALGORITHM, y as LoginParams, L as LoginResult, z as LogoutParams, a2 as OAuthCallbackParams, a3 as OAuthCallbackResult, a1 as OAuthStartParams, O as OAuthStartResult, a8 as PasswordSchema, a7 as PhoneSchema, x as RegisterParams, Q as RegisterPublicKeyParams, a as RegisterResult, W as RevokeKeyParams, T as RotateKeyParams, b as RotateKeyResult, e as SOCIAL_PROVIDERS, F as SendVerificationCodeParams, S as SendVerificationCodeResult, a9 as TargetTypeSchema, d as USER_STATUSES, o as UserStatus, h as VERIFICATION_PURPOSES, g as VERIFICATION_TARGET_TYPES, aa as VerificationPurposeSchema, V as VerificationTargetType, G as VerifyCodeParams, H as VerifyCodeResult, m as authRouter, a4 as authenticate, Z as buildOAuthErrorUrl, v as changePasswordService, r as checkAccountExistsService, $ as getEnabledOAuthProviders, a0 as getGoogleAccessToken, _ as isOAuthProviderEnabled, t as loginService, u as logoutService, Y as oauthCallbackService, X as oauthStartService, a5 as optionalAuth, J as registerPublicKeyService, s as registerService, N as revokeKeyService, M as rotateKeyService, D as sendVerificationCodeService, E as verifyCodeService } from './authenticate-kCg_KD-V.js';
3
3
  import * as drizzle_orm_pg_core from 'drizzle-orm/pg-core';
4
4
  import { UserProfile as UserProfile$1, ProfileInfo } from '@spfn/auth';
5
5
  import { BaseRepository } from '@spfn/core/db';
@@ -1256,7 +1256,7 @@ declare function getAuthSessionService(userId: string | number | bigint): Promis
1256
1256
  id: number;
1257
1257
  name: string;
1258
1258
  displayName: string;
1259
- category: "custom" | "user" | "auth" | "rbac" | "system" | undefined;
1259
+ category: "auth" | "custom" | "user" | "rbac" | "system" | undefined;
1260
1260
  }[];
1261
1261
  userId: number;
1262
1262
  email: string | null;
@@ -2385,7 +2385,7 @@ declare const permissions: drizzle_orm_pg_core.PgTableWithColumns<{
2385
2385
  tableName: "permissions";
2386
2386
  dataType: "string";
2387
2387
  columnType: "PgText";
2388
- data: "custom" | "user" | "auth" | "rbac" | "system";
2388
+ data: "auth" | "custom" | "user" | "rbac" | "system";
2389
2389
  driverParam: string;
2390
2390
  notNull: false;
2391
2391
  hasDefault: false;
@@ -2861,13 +2861,13 @@ declare class UsersRepository extends BaseRepository {
2861
2861
  create(data: NewUser): Promise<{
2862
2862
  email: string | null;
2863
2863
  phone: string | null;
2864
- status: "active" | "inactive" | "suspended";
2865
2864
  id: number;
2866
- createdAt: Date;
2867
- updatedAt: Date;
2868
2865
  passwordHash: string | null;
2869
2866
  passwordChangeRequired: boolean;
2870
2867
  roleId: number;
2868
+ createdAt: Date;
2869
+ updatedAt: Date;
2870
+ status: "active" | "inactive" | "suspended";
2871
2871
  emailVerifiedAt: Date | null;
2872
2872
  phoneVerifiedAt: Date | null;
2873
2873
  lastLoginAt: Date | null;
@@ -2933,13 +2933,13 @@ declare class UsersRepository extends BaseRepository {
2933
2933
  deleteById(id: number): Promise<{
2934
2934
  email: string | null;
2935
2935
  phone: string | null;
2936
- status: "active" | "inactive" | "suspended";
2937
2936
  id: number;
2938
- createdAt: Date;
2939
- updatedAt: Date;
2940
2937
  passwordHash: string | null;
2941
2938
  passwordChangeRequired: boolean;
2942
2939
  roleId: number;
2940
+ createdAt: Date;
2941
+ updatedAt: Date;
2942
+ status: "active" | "inactive" | "suspended";
2943
2943
  emailVerifiedAt: Date | null;
2944
2944
  phoneVerifiedAt: Date | null;
2945
2945
  lastLoginAt: Date | null;
@@ -2962,7 +2962,7 @@ declare class UsersRepository extends BaseRepository {
2962
2962
  id: number;
2963
2963
  name: string;
2964
2964
  displayName: string;
2965
- category: "custom" | "user" | "auth" | "rbac" | "system" | undefined;
2965
+ category: "auth" | "custom" | "user" | "rbac" | "system" | undefined;
2966
2966
  }[];
2967
2967
  }>;
2968
2968
  /**
@@ -3072,16 +3072,16 @@ declare class KeysRepository extends BaseRepository {
3072
3072
  * Write primary 사용
3073
3073
  */
3074
3074
  create(data: NewUserPublicKey): Promise<{
3075
- userId: number;
3075
+ publicKey: string;
3076
3076
  keyId: string;
3077
+ fingerprint: string;
3078
+ algorithm: "ES256" | "RS256";
3079
+ userId: number;
3077
3080
  id: number;
3078
3081
  isActive: boolean;
3079
3082
  createdAt: Date;
3080
- publicKey: string;
3081
- algorithm: "ES256" | "RS256";
3082
- fingerprint: string;
3083
- lastUsedAt: Date | null;
3084
3083
  expiresAt: Date | null;
3084
+ lastUsedAt: Date | null;
3085
3085
  revokedAt: Date | null;
3086
3086
  revokedReason: string | null;
3087
3087
  }>;
@@ -3108,16 +3108,16 @@ declare class KeysRepository extends BaseRepository {
3108
3108
  * Write primary 사용
3109
3109
  */
3110
3110
  deleteByKeyIdAndUserId(keyId: string, userId: number): Promise<{
3111
- userId: number;
3111
+ publicKey: string;
3112
3112
  keyId: string;
3113
+ fingerprint: string;
3114
+ algorithm: "ES256" | "RS256";
3115
+ userId: number;
3113
3116
  id: number;
3114
3117
  isActive: boolean;
3115
3118
  createdAt: Date;
3116
- publicKey: string;
3117
- algorithm: "ES256" | "RS256";
3118
- fingerprint: string;
3119
- lastUsedAt: Date | null;
3120
3119
  expiresAt: Date | null;
3120
+ lastUsedAt: Date | null;
3121
3121
  revokedAt: Date | null;
3122
3122
  revokedReason: string | null;
3123
3123
  }>;
@@ -3232,14 +3232,14 @@ declare class VerificationCodesRepository extends BaseRepository {
3232
3232
  * Write primary 사용
3233
3233
  */
3234
3234
  create(data: NewVerificationCode): Promise<{
3235
+ target: string;
3236
+ targetType: "email" | "phone";
3237
+ purpose: "registration" | "login" | "password_reset" | "email_change" | "phone_change";
3238
+ code: string;
3235
3239
  id: number;
3236
3240
  createdAt: Date;
3237
3241
  updatedAt: Date;
3238
3242
  expiresAt: Date;
3239
- target: string;
3240
- targetType: "email" | "phone";
3241
- code: string;
3242
- purpose: "registration" | "login" | "password_reset" | "email_change" | "phone_change";
3243
3243
  usedAt: Date | null;
3244
3244
  attempts: number;
3245
3245
  }>;
@@ -3428,7 +3428,7 @@ declare class PermissionsRepository extends BaseRepository {
3428
3428
  name: string;
3429
3429
  displayName: string;
3430
3430
  description: string | null;
3431
- category: "custom" | "user" | "auth" | "rbac" | "system" | null;
3431
+ category: "auth" | "custom" | "user" | "rbac" | "system" | null;
3432
3432
  isBuiltin: boolean;
3433
3433
  isSystem: boolean;
3434
3434
  isActive: boolean;
@@ -3444,7 +3444,7 @@ declare class PermissionsRepository extends BaseRepository {
3444
3444
  name: string;
3445
3445
  displayName: string;
3446
3446
  description: string | null;
3447
- category: "custom" | "user" | "auth" | "rbac" | "system" | null;
3447
+ category: "auth" | "custom" | "user" | "rbac" | "system" | null;
3448
3448
  isBuiltin: boolean;
3449
3449
  isSystem: boolean;
3450
3450
  isActive: boolean;
@@ -3484,7 +3484,7 @@ declare class PermissionsRepository extends BaseRepository {
3484
3484
  name: string;
3485
3485
  displayName: string;
3486
3486
  description: string | null;
3487
- category: "custom" | "user" | "auth" | "rbac" | "system" | null;
3487
+ category: "auth" | "custom" | "user" | "rbac" | "system" | null;
3488
3488
  isBuiltin: boolean;
3489
3489
  isSystem: boolean;
3490
3490
  isActive: boolean;
@@ -3503,8 +3503,8 @@ declare class PermissionsRepository extends BaseRepository {
3503
3503
  isActive: boolean;
3504
3504
  createdAt: Date;
3505
3505
  updatedAt: Date;
3506
+ category: "auth" | "custom" | "user" | "rbac" | "system" | null;
3506
3507
  metadata: Record<string, any> | null;
3507
- category: "custom" | "user" | "auth" | "rbac" | "system" | null;
3508
3508
  }>;
3509
3509
  }
3510
3510
  declare const permissionsRepository: PermissionsRepository;
@@ -3549,9 +3549,9 @@ declare class RolePermissionsRepository extends BaseRepository {
3549
3549
  */
3550
3550
  createMany(data: NewRolePermission[]): Promise<{
3551
3551
  id: number;
3552
+ roleId: number;
3552
3553
  createdAt: Date;
3553
3554
  updatedAt: Date;
3554
- roleId: number;
3555
3555
  permissionId: number;
3556
3556
  }[]>;
3557
3557
  /**
@@ -3567,9 +3567,9 @@ declare class RolePermissionsRepository extends BaseRepository {
3567
3567
  */
3568
3568
  setPermissionsForRole(roleId: number, permissionIds: number[]): Promise<{
3569
3569
  id: number;
3570
+ roleId: number;
3570
3571
  createdAt: Date;
3571
3572
  updatedAt: Date;
3572
- roleId: number;
3573
3573
  permissionId: number;
3574
3574
  }[]>;
3575
3575
  }
@@ -3634,10 +3634,10 @@ declare class UserPermissionsRepository extends BaseRepository {
3634
3634
  id: number;
3635
3635
  createdAt: Date;
3636
3636
  updatedAt: Date;
3637
- expiresAt: Date | null;
3638
3637
  permissionId: number;
3639
- granted: boolean;
3638
+ expiresAt: Date | null;
3640
3639
  reason: string | null;
3640
+ granted: boolean;
3641
3641
  }>;
3642
3642
  /**
3643
3643
  * 사용자 권한 오버라이드 업데이트
@@ -3660,10 +3660,10 @@ declare class UserPermissionsRepository extends BaseRepository {
3660
3660
  id: number;
3661
3661
  createdAt: Date;
3662
3662
  updatedAt: Date;
3663
- expiresAt: Date | null;
3664
3663
  permissionId: number;
3665
- granted: boolean;
3664
+ expiresAt: Date | null;
3666
3665
  reason: string | null;
3666
+ granted: boolean;
3667
3667
  }>;
3668
3668
  /**
3669
3669
  * 사용자의 모든 권한 오버라이드 삭제
@@ -3742,6 +3742,7 @@ declare class UserProfilesRepository extends BaseRepository {
3742
3742
  displayName: string;
3743
3743
  createdAt: Date;
3744
3744
  updatedAt: Date;
3745
+ metadata: Record<string, any> | null;
3745
3746
  firstName: string | null;
3746
3747
  lastName: string | null;
3747
3748
  avatarUrl: string | null;
@@ -3754,7 +3755,6 @@ declare class UserProfilesRepository extends BaseRepository {
3754
3755
  location: string | null;
3755
3756
  company: string | null;
3756
3757
  jobTitle: string | null;
3757
- metadata: Record<string, any> | null;
3758
3758
  }>;
3759
3759
  /**
3760
3760
  * 프로필 업데이트 (by ID)
@@ -3811,6 +3811,7 @@ declare class UserProfilesRepository extends BaseRepository {
3811
3811
  displayName: string;
3812
3812
  createdAt: Date;
3813
3813
  updatedAt: Date;
3814
+ metadata: Record<string, any> | null;
3814
3815
  firstName: string | null;
3815
3816
  lastName: string | null;
3816
3817
  avatarUrl: string | null;
@@ -3823,7 +3824,6 @@ declare class UserProfilesRepository extends BaseRepository {
3823
3824
  location: string | null;
3824
3825
  company: string | null;
3825
3826
  jobTitle: string | null;
3826
- metadata: Record<string, any> | null;
3827
3827
  }>;
3828
3828
  /**
3829
3829
  * 프로필 삭제 (by User ID)
@@ -3834,6 +3834,7 @@ declare class UserProfilesRepository extends BaseRepository {
3834
3834
  displayName: string;
3835
3835
  createdAt: Date;
3836
3836
  updatedAt: Date;
3837
+ metadata: Record<string, any> | null;
3837
3838
  firstName: string | null;
3838
3839
  lastName: string | null;
3839
3840
  avatarUrl: string | null;
@@ -3846,7 +3847,6 @@ declare class UserProfilesRepository extends BaseRepository {
3846
3847
  location: string | null;
3847
3848
  company: string | null;
3848
3849
  jobTitle: string | null;
3849
- metadata: Record<string, any> | null;
3850
3850
  }>;
3851
3851
  /**
3852
3852
  * 프로필 Upsert (by User ID)
@@ -3860,6 +3860,7 @@ declare class UserProfilesRepository extends BaseRepository {
3860
3860
  displayName: string;
3861
3861
  createdAt: Date;
3862
3862
  updatedAt: Date;
3863
+ metadata: Record<string, any> | null;
3863
3864
  firstName: string | null;
3864
3865
  lastName: string | null;
3865
3866
  avatarUrl: string | null;
@@ -3872,7 +3873,6 @@ declare class UserProfilesRepository extends BaseRepository {
3872
3873
  location: string | null;
3873
3874
  company: string | null;
3874
3875
  jobTitle: string | null;
3875
- metadata: Record<string, any> | null;
3876
3876
  }>;
3877
3877
  /**
3878
3878
  * User ID로 프로필 데이터 조회 (formatted)
@@ -4000,15 +4000,15 @@ declare class InvitationsRepository extends BaseRepository {
4000
4000
  */
4001
4001
  create(data: NewInvitation): Promise<{
4002
4002
  email: string;
4003
- status: "pending" | "accepted" | "expired" | "cancelled";
4004
4003
  id: number;
4004
+ roleId: number;
4005
4005
  createdAt: Date;
4006
4006
  updatedAt: Date;
4007
- roleId: number;
4007
+ status: "pending" | "accepted" | "expired" | "cancelled";
4008
4008
  metadata: Record<string, any> | null;
4009
- expiresAt: Date;
4010
4009
  token: string;
4011
4010
  invitedBy: number;
4011
+ expiresAt: Date;
4012
4012
  acceptedAt: Date | null;
4013
4013
  cancelledAt: Date | null;
4014
4014
  }>;
@@ -4034,15 +4034,15 @@ declare class InvitationsRepository extends BaseRepository {
4034
4034
  */
4035
4035
  deleteById(id: number): Promise<{
4036
4036
  email: string;
4037
- status: "pending" | "accepted" | "expired" | "cancelled";
4038
4037
  id: number;
4038
+ roleId: number;
4039
4039
  createdAt: Date;
4040
4040
  updatedAt: Date;
4041
- roleId: number;
4041
+ status: "pending" | "accepted" | "expired" | "cancelled";
4042
4042
  metadata: Record<string, any> | null;
4043
- expiresAt: Date;
4044
4043
  token: string;
4045
4044
  invitedBy: number;
4045
+ expiresAt: Date;
4046
4046
  acceptedAt: Date | null;
4047
4047
  cancelledAt: Date | null;
4048
4048
  }>;
@@ -4667,6 +4667,32 @@ declare const roleGuard: _spfn_core_route.NamedMiddlewareFactory<"roleGuard", [o
4667
4667
  declare function getAuth(c: Context | {
4668
4668
  raw: Context;
4669
4669
  }): AuthContext;
4670
+ /**
4671
+ * Get optional auth context from route context
4672
+ *
4673
+ * Returns AuthContext if authenticated, undefined otherwise.
4674
+ * Use with `optionalAuth` middleware for routes that serve both
4675
+ * authenticated and unauthenticated users.
4676
+ *
4677
+ * @example
4678
+ * ```typescript
4679
+ * export const getProducts = route.get('/products')
4680
+ * .use([optionalAuth])
4681
+ * .handler(async (c) => {
4682
+ * const auth = getOptionalAuth(c);
4683
+ *
4684
+ * if (auth)
4685
+ * {
4686
+ * return getPersonalizedProducts(auth.userId);
4687
+ * }
4688
+ *
4689
+ * return getPublicProducts();
4690
+ * });
4691
+ * ```
4692
+ */
4693
+ declare function getOptionalAuth(c: Context | {
4694
+ raw: Context;
4695
+ }): AuthContext | undefined;
4670
4696
  /**
4671
4697
  * Get authenticated user from route context
4672
4698
  *
@@ -4683,13 +4709,13 @@ declare function getUser(c: Context | {
4683
4709
  }): {
4684
4710
  email: string | null;
4685
4711
  phone: string | null;
4686
- status: "active" | "inactive" | "suspended";
4687
4712
  id: number;
4688
- createdAt: Date;
4689
- updatedAt: Date;
4690
4713
  passwordHash: string | null;
4691
4714
  passwordChangeRequired: boolean;
4692
4715
  roleId: number;
4716
+ createdAt: Date;
4717
+ updatedAt: Date;
4718
+ status: "active" | "inactive" | "suspended";
4693
4719
  emailVerifiedAt: Date | null;
4694
4720
  phoneVerifiedAt: Date | null;
4695
4721
  lastLoginAt: Date | null;
@@ -5178,4 +5204,4 @@ declare const authRegisterEvent: _spfn_core_event.EventDef<{
5178
5204
  type AuthLoginPayload = typeof authLoginEvent._payload;
5179
5205
  type AuthRegisterPayload = typeof authRegisterEvent._payload;
5180
5206
 
5181
- export { type AuthConfig, AuthContext, type AuthLoginPayload, AuthProviderSchema, type AuthRegisterPayload, COOKIE_NAMES, type CreateOAuthStateParams, type GoogleTokenResponse, type GoogleUserInfo, type Invitation, InvitationStatus, InvitationsRepository, KeyAlgorithmType, type KeyPair, KeysRepository, type NewInvitation, type NewPermission, type NewPermissionEntity, type NewRole, type NewRoleEntity, type NewRolePermission, type NewUser, type NewUserPermission, type NewUserProfile, type NewUserPublicKey, type NewUserSocialAccount, type NewVerificationCode, type OAuthState, type Permission, type PermissionEntity, PermissionsRepository, type Role, type RoleEntity, type RoleGuardOptions, type RolePermission, RolePermissionsRepository, RolesRepository, type SessionData, type SessionPayload, SocialAccountsRepository, SocialProvider, type TokenPayload, type UpdateProfileParams, type User, type UserPermission, UserPermissionsRepository, type UserProfile, UserProfilesRepository, type UserPublicKey, type UserSocialAccount, UsersRepository, type VerificationCode, VerificationCodesRepository, VerificationPurpose, acceptInvitation, addPermissionToRole, authLogger, authLoginEvent, authRegisterEvent, authSchema, cancelInvitation, configureAuth, createAuthLifecycle, createInvitation, createOAuthState, createRole, decodeToken, deleteInvitation, deleteRole, exchangeCodeForTokens, expireOldInvitations, generateClientToken, generateKeyPair, generateKeyPairES256, generateKeyPairRS256, generateToken, getAllRoles, getAuth, getAuthConfig, getAuthSessionService, getGoogleAuthUrl, getGoogleOAuthConfig, getGoogleUserInfo, getInvitationByToken, getInvitationWithDetails, getKeyId, getKeySize, getRole, getRoleByName, getRolePermissions, getSessionInfo, getSessionTtl, getUser, getUserByEmailService, getUserByIdService, getUserByPhoneService, getUserId, getUserPermissions, getUserProfileService, getUserRole, hasAllPermissions, hasAnyPermission, hasAnyRole, hasPermission, hasRole, hashPassword, initializeAuth, invitationsRepository, isGoogleOAuthEnabled, keysRepository, listInvitations, parseDuration, permissions, permissionsRepository, refreshAccessToken, removePermissionFromRole, requireAnyPermission, requirePermissions, requireRole, resendInvitation, roleGuard, rolePermissions, rolePermissionsRepository, roles, rolesRepository, sealSession, setRolePermissions, shouldRefreshSession, shouldRotateKey, socialAccountsRepository, unsealSession, updateLastLoginService, updateRole, updateUserProfileService, updateUserService, userInvitations, userPermissions, userPermissionsRepository, userProfiles, userProfilesRepository, userPublicKeys, userSocialAccounts, users, usersRepository, validateInvitation, validatePasswordStrength, verificationCodes, verificationCodesRepository, verifyClientToken, verifyKeyFingerprint, verifyOAuthState, verifyPassword, verifyToken };
5207
+ export { type AuthConfig, AuthContext, type AuthLoginPayload, AuthProviderSchema, type AuthRegisterPayload, COOKIE_NAMES, type CreateOAuthStateParams, type GoogleTokenResponse, type GoogleUserInfo, type Invitation, InvitationStatus, InvitationsRepository, KeyAlgorithmType, type KeyPair, KeysRepository, type NewInvitation, type NewPermission, type NewPermissionEntity, type NewRole, type NewRoleEntity, type NewRolePermission, type NewUser, type NewUserPermission, type NewUserProfile, type NewUserPublicKey, type NewUserSocialAccount, type NewVerificationCode, type OAuthState, type Permission, type PermissionEntity, PermissionsRepository, type Role, type RoleEntity, type RoleGuardOptions, type RolePermission, RolePermissionsRepository, RolesRepository, type SessionData, type SessionPayload, SocialAccountsRepository, SocialProvider, type TokenPayload, type UpdateProfileParams, type User, type UserPermission, UserPermissionsRepository, type UserProfile, UserProfilesRepository, type UserPublicKey, type UserSocialAccount, UsersRepository, type VerificationCode, VerificationCodesRepository, VerificationPurpose, acceptInvitation, addPermissionToRole, authLogger, authLoginEvent, authRegisterEvent, authSchema, cancelInvitation, configureAuth, createAuthLifecycle, createInvitation, createOAuthState, createRole, decodeToken, deleteInvitation, deleteRole, exchangeCodeForTokens, expireOldInvitations, generateClientToken, generateKeyPair, generateKeyPairES256, generateKeyPairRS256, generateToken, getAllRoles, getAuth, getAuthConfig, getAuthSessionService, getGoogleAuthUrl, getGoogleOAuthConfig, getGoogleUserInfo, getInvitationByToken, getInvitationWithDetails, getKeyId, getKeySize, getOptionalAuth, getRole, getRoleByName, getRolePermissions, getSessionInfo, getSessionTtl, getUser, getUserByEmailService, getUserByIdService, getUserByPhoneService, getUserId, getUserPermissions, getUserProfileService, getUserRole, hasAllPermissions, hasAnyPermission, hasAnyRole, hasPermission, hasRole, hashPassword, initializeAuth, invitationsRepository, isGoogleOAuthEnabled, keysRepository, listInvitations, parseDuration, permissions, permissionsRepository, refreshAccessToken, removePermissionFromRole, requireAnyPermission, requirePermissions, requireRole, resendInvitation, roleGuard, rolePermissions, rolePermissionsRepository, roles, rolesRepository, sealSession, setRolePermissions, shouldRefreshSession, shouldRotateKey, socialAccountsRepository, unsealSession, updateLastLoginService, updateRole, updateUserProfileService, updateUserService, userInvitations, userPermissions, userPermissionsRepository, userProfiles, userProfilesRepository, userPublicKeys, userSocialAccounts, users, usersRepository, validateInvitation, validatePasswordStrength, verificationCodes, verificationCodesRepository, verifyClientToken, verifyKeyFingerprint, verifyOAuthState, verifyPassword, verifyToken };
package/dist/server.js CHANGED
@@ -6782,6 +6782,12 @@ function getAuth(c) {
6782
6782
  }
6783
6783
  return c.get("auth");
6784
6784
  }
6785
+ function getOptionalAuth(c) {
6786
+ if ("raw" in c && c.raw) {
6787
+ return c.raw.get("auth");
6788
+ }
6789
+ return c.get("auth");
6790
+ }
6785
6791
  function getUser(c) {
6786
6792
  return getAuth(c).user;
6787
6793
  }
@@ -8297,6 +8303,51 @@ var authenticate = defineMiddleware("auth", async (c, next) => {
8297
8303
  });
8298
8304
  await next();
8299
8305
  });
8306
+ var optionalAuth = defineMiddleware("optionalAuth", async (c, next) => {
8307
+ const authHeader = c.req.header("Authorization");
8308
+ if (!authHeader || !authHeader.startsWith("Bearer ")) {
8309
+ await next();
8310
+ return;
8311
+ }
8312
+ const token = authHeader.substring(7);
8313
+ try {
8314
+ const decoded = decodeToken2(token);
8315
+ if (!decoded || !decoded.keyId) {
8316
+ await next();
8317
+ return;
8318
+ }
8319
+ const keyId = decoded.keyId;
8320
+ const keyRecord = await keysRepository2.findActiveByKeyId(keyId);
8321
+ if (!keyRecord) {
8322
+ await next();
8323
+ return;
8324
+ }
8325
+ if (keyRecord.expiresAt && /* @__PURE__ */ new Date() > keyRecord.expiresAt) {
8326
+ await next();
8327
+ return;
8328
+ }
8329
+ verifyClientToken2(
8330
+ token,
8331
+ keyRecord.publicKey,
8332
+ keyRecord.algorithm
8333
+ );
8334
+ const result = await usersRepository2.findByIdWithRole(keyRecord.userId);
8335
+ if (!result || result.user.status !== "active") {
8336
+ await next();
8337
+ return;
8338
+ }
8339
+ const { user, role } = result;
8340
+ keysRepository2.updateLastUsedById(keyRecord.id).catch((err) => authLogger2.middleware.error("Failed to update lastUsedAt", err));
8341
+ c.set("auth", {
8342
+ user,
8343
+ userId: String(user.id),
8344
+ keyId,
8345
+ role: role?.name ?? null
8346
+ });
8347
+ } catch {
8348
+ }
8349
+ await next();
8350
+ }, { skips: ["auth"] });
8300
8351
 
8301
8352
  // src/server/middleware/require-permission.ts
8302
8353
  import { defineMiddleware as defineMiddleware2 } from "@spfn/core/route";
@@ -9284,6 +9335,7 @@ export {
9284
9335
  getInvitationWithDetails,
9285
9336
  getKeyId,
9286
9337
  getKeySize,
9338
+ getOptionalAuth,
9287
9339
  getRole,
9288
9340
  getRoleByName,
9289
9341
  getRolePermissions,
@@ -9313,6 +9365,7 @@ export {
9313
9365
  logoutService,
9314
9366
  oauthCallbackService,
9315
9367
  oauthStartService,
9368
+ optionalAuth,
9316
9369
  parseDuration,
9317
9370
  permissions,
9318
9371
  permissionsRepository,