@spfn/auth 0.2.0-beta.39 → 0.2.0-beta.40

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
@@ -1116,9 +1116,13 @@ Update authenticated user's username. Validates uniqueness before updating.
1116
1116
  provider: 'email' | 'phone' | 'google';
1117
1117
  email?: string;
1118
1118
  phone?: string;
1119
+ metadata?: Record<string, unknown>; // 가입 시 전달된 커스텀 메타데이터
1119
1120
  }
1120
1121
  ```
1121
1122
 
1123
+ `metadata`는 클라이언트가 register/OAuth 요청 body에 포함한 값이 그대로 전달됩니다.
1124
+ 레퍼럴 코드, UTM 파라미터 등 앱 고유 데이터를 이벤트 구독자에게 전달할 때 사용합니다.
1125
+
1122
1126
  ---
1123
1127
 
1124
1128
  ### Subscribing to Events
@@ -1132,12 +1136,32 @@ authLoginEvent.subscribe(async (payload) => {
1132
1136
  await analytics.trackLogin(payload.userId);
1133
1137
  });
1134
1138
 
1135
- // 회원가입 이벤트 구독
1139
+ // 회원가입 이벤트 구독 (metadata 활용)
1136
1140
  authRegisterEvent.subscribe(async (payload) => {
1137
1141
  console.log('New user registered:', payload.userId);
1138
1142
  if (payload.email) {
1139
1143
  await emailService.sendWelcome(payload.email);
1140
1144
  }
1145
+
1146
+ // 레퍼럴 코드 처리
1147
+ const refCode = payload.metadata?.refCode as string;
1148
+ if (refCode) {
1149
+ await referralService.link(payload.userId, refCode);
1150
+ }
1151
+ });
1152
+ ```
1153
+
1154
+ 클라이언트에서 metadata를 전달하는 방법:
1155
+
1156
+ ```typescript
1157
+ // 이메일/전화 가입
1158
+ authApi.register.call({
1159
+ body: { email, password, metadata: { refCode: 'CODE', utm_source: 'google' } }
1160
+ });
1161
+
1162
+ // OAuth 가입
1163
+ authApi.oauthStart.call({
1164
+ body: { provider: 'google', returnUrl: '/dashboard', metadata: { refCode: 'CODE' } }
1141
1165
  });
1142
1166
  ```
1143
1167
 
@@ -149,6 +149,7 @@ interface RegisterParams {
149
149
  keyId: string;
150
150
  fingerprint: string;
151
151
  algorithm?: KeyAlgorithmType;
152
+ metadata?: Record<string, unknown>;
152
153
  }
153
154
  interface RegisterResult {
154
155
  userId: string;
@@ -393,6 +394,7 @@ interface OAuthStartParams {
393
394
  keyId: string;
394
395
  fingerprint: string;
395
396
  algorithm: KeyAlgorithmType;
397
+ metadata?: Record<string, unknown>;
396
398
  }
397
399
  interface OAuthStartResult {
398
400
  authUrl: string;
@@ -492,6 +494,7 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
492
494
  phone: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
493
495
  verificationToken: _sinclair_typebox.TString;
494
496
  password: _sinclair_typebox.TString;
497
+ metadata: _sinclair_typebox.TOptional<_sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TUnknown>>;
495
498
  }>;
496
499
  }, {
497
500
  body: _sinclair_typebox.TObject<{
@@ -570,6 +573,7 @@ declare const mainAuthRouter: _spfn_core_route.Router<{
570
573
  keyId: _sinclair_typebox.TString;
571
574
  fingerprint: _sinclair_typebox.TString;
572
575
  algorithm: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"ES256" | "RS256">[]>;
576
+ metadata: _sinclair_typebox.TOptional<_sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TUnknown>>;
573
577
  }>;
574
578
  }, {}, OAuthStartResult>;
575
579
  oauthProviders: _spfn_core_route.RouteDef<{}, {}, {
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-vcXIhj1J.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-vcXIhj1J.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-BbugF32w.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-BbugF32w.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';
@@ -118,6 +118,7 @@ declare const authApi: _spfn_core_nextjs.Client<_spfn_core_route.Router<{
118
118
  phone: _sinclair_typebox.TOptional<_sinclair_typebox.TString>;
119
119
  verificationToken: _sinclair_typebox.TString;
120
120
  password: _sinclair_typebox.TString;
121
+ metadata: _sinclair_typebox.TOptional<_sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TUnknown>>;
121
122
  }>;
122
123
  }, {
123
124
  body: _sinclair_typebox.TObject<{
@@ -196,6 +197,7 @@ declare const authApi: _spfn_core_nextjs.Client<_spfn_core_route.Router<{
196
197
  keyId: _sinclair_typebox.TString;
197
198
  fingerprint: _sinclair_typebox.TString;
198
199
  algorithm: _sinclair_typebox.TUnion<_sinclair_typebox.TLiteral<"ES256" | "RS256">[]>;
200
+ metadata: _sinclair_typebox.TOptional<_sinclair_typebox.TRecord<_sinclair_typebox.TString, _sinclair_typebox.TUnknown>>;
199
201
  }>;
200
202
  }, {}, OAuthStartResult>;
201
203
  oauthProviders: _spfn_core_route.RouteDef<{}, {}, {
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-vcXIhj1J.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-vcXIhj1J.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-BbugF32w.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-BbugF32w.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';
@@ -3585,6 +3585,7 @@ declare class PermissionsRepository extends BaseRepository {
3585
3585
  */
3586
3586
  deleteById(id: number): Promise<{
3587
3587
  description: string | null;
3588
+ metadata: Record<string, any> | null;
3588
3589
  id: number;
3589
3590
  name: string;
3590
3591
  displayName: string;
@@ -3594,7 +3595,6 @@ declare class PermissionsRepository extends BaseRepository {
3594
3595
  createdAt: Date;
3595
3596
  updatedAt: Date;
3596
3597
  category: "auth" | "custom" | "user" | "rbac" | "system" | null;
3597
- metadata: Record<string, any> | null;
3598
3598
  }>;
3599
3599
  }
3600
3600
  declare const permissionsRepository: PermissionsRepository;
@@ -3831,12 +3831,12 @@ declare class UserProfilesRepository extends BaseRepository {
3831
3831
  * 프로필 생성
3832
3832
  */
3833
3833
  create(data: NewUserProfile): Promise<{
3834
+ metadata: Record<string, any> | null;
3834
3835
  userId: number;
3835
3836
  id: number;
3836
3837
  displayName: string;
3837
3838
  createdAt: Date;
3838
3839
  updatedAt: Date;
3839
- metadata: Record<string, any> | null;
3840
3840
  firstName: string | null;
3841
3841
  lastName: string | null;
3842
3842
  avatarUrl: string | null;
@@ -3900,12 +3900,12 @@ declare class UserProfilesRepository extends BaseRepository {
3900
3900
  * 프로필 삭제 (by ID)
3901
3901
  */
3902
3902
  deleteById(id: number): Promise<{
3903
+ metadata: Record<string, any> | null;
3903
3904
  userId: number;
3904
3905
  id: number;
3905
3906
  displayName: string;
3906
3907
  createdAt: Date;
3907
3908
  updatedAt: Date;
3908
- metadata: Record<string, any> | null;
3909
3909
  firstName: string | null;
3910
3910
  lastName: string | null;
3911
3911
  avatarUrl: string | null;
@@ -3923,12 +3923,12 @@ declare class UserProfilesRepository extends BaseRepository {
3923
3923
  * 프로필 삭제 (by User ID)
3924
3924
  */
3925
3925
  deleteByUserId(userId: number): Promise<{
3926
+ metadata: Record<string, any> | null;
3926
3927
  userId: number;
3927
3928
  id: number;
3928
3929
  displayName: string;
3929
3930
  createdAt: Date;
3930
3931
  updatedAt: Date;
3931
- metadata: Record<string, any> | null;
3932
3932
  firstName: string | null;
3933
3933
  lastName: string | null;
3934
3934
  avatarUrl: string | null;
@@ -3949,12 +3949,12 @@ declare class UserProfilesRepository extends BaseRepository {
3949
3949
  * 새로 생성 시 displayName은 필수 (없으면 'User'로 설정)
3950
3950
  */
3951
3951
  upsertByUserId(userId: number, data: Partial<Omit<NewUserProfile, 'userId'>>): Promise<{
3952
+ metadata: Record<string, any> | null;
3952
3953
  userId: number;
3953
3954
  id: number;
3954
3955
  displayName: string;
3955
3956
  createdAt: Date;
3956
3957
  updatedAt: Date;
3957
- metadata: Record<string, any> | null;
3958
3958
  firstName: string | null;
3959
3959
  lastName: string | null;
3960
3960
  avatarUrl: string | null;
@@ -4094,12 +4094,12 @@ declare class InvitationsRepository extends BaseRepository {
4094
4094
  */
4095
4095
  create(data: NewInvitation): Promise<{
4096
4096
  email: string;
4097
+ metadata: Record<string, any> | null;
4097
4098
  id: number;
4098
4099
  roleId: number;
4099
4100
  createdAt: Date;
4100
4101
  updatedAt: Date;
4101
4102
  status: "pending" | "accepted" | "expired" | "cancelled";
4102
- metadata: Record<string, any> | null;
4103
4103
  token: string;
4104
4104
  invitedBy: number;
4105
4105
  expiresAt: Date;
@@ -4128,12 +4128,12 @@ declare class InvitationsRepository extends BaseRepository {
4128
4128
  */
4129
4129
  deleteById(id: number): Promise<{
4130
4130
  email: string;
4131
+ metadata: Record<string, any> | null;
4131
4132
  id: number;
4132
4133
  roleId: number;
4133
4134
  createdAt: Date;
4134
4135
  updatedAt: Date;
4135
4136
  status: "pending" | "accepted" | "expired" | "cancelled";
4136
- metadata: Record<string, any> | null;
4137
4137
  token: string;
4138
4138
  invitedBy: number;
4139
4139
  expiresAt: Date;
@@ -5137,6 +5137,7 @@ interface OAuthState {
5137
5137
  keyId: string;
5138
5138
  fingerprint: string;
5139
5139
  algorithm: KeyAlgorithmType;
5140
+ metadata?: Record<string, unknown>;
5140
5141
  }
5141
5142
  interface CreateOAuthStateParams {
5142
5143
  provider: string;
@@ -5145,6 +5146,7 @@ interface CreateOAuthStateParams {
5145
5146
  keyId: string;
5146
5147
  fingerprint: string;
5147
5148
  algorithm: KeyAlgorithmType;
5149
+ metadata?: Record<string, unknown>;
5148
5150
  }
5149
5151
  /**
5150
5152
  * OAuth state 생성 및 암호화
@@ -5306,6 +5308,9 @@ declare const authLoginEvent: _spfn_core_event.EventDef<{
5306
5308
  declare const authRegisterEvent: _spfn_core_event.EventDef<{
5307
5309
  email?: string | undefined;
5308
5310
  phone?: string | undefined;
5311
+ metadata?: {
5312
+ [x: string]: unknown;
5313
+ } | undefined;
5309
5314
  userId: string;
5310
5315
  provider: "email" | "phone" | "google";
5311
5316
  }>;
package/dist/server.js CHANGED
@@ -7149,7 +7149,8 @@ var authRegisterEvent = defineEvent(
7149
7149
  userId: Type.String(),
7150
7150
  provider: AuthProviderSchema,
7151
7151
  email: Type.Optional(Type.String()),
7152
- phone: Type.Optional(Type.String())
7152
+ phone: Type.Optional(Type.String()),
7153
+ metadata: Type.Optional(Type.Record(Type.String(), Type.Unknown()))
7153
7154
  })
7154
7155
  );
7155
7156
 
@@ -7177,7 +7178,7 @@ async function checkAccountExistsService(params) {
7177
7178
  };
7178
7179
  }
7179
7180
  async function registerService(params) {
7180
- const { email, phone, verificationToken, password, publicKey, keyId, fingerprint, algorithm } = params;
7181
+ const { email, phone, verificationToken, password, publicKey, keyId, fingerprint, algorithm, metadata } = params;
7181
7182
  const tokenPayload = validateVerificationToken(verificationToken);
7182
7183
  if (!tokenPayload) {
7183
7184
  throw new InvalidVerificationTokenError();
@@ -7228,7 +7229,8 @@ async function registerService(params) {
7228
7229
  userId: result.userId,
7229
7230
  provider: email ? "email" : "phone",
7230
7231
  email: result.email,
7231
- phone: result.phone
7232
+ phone: result.phone,
7233
+ metadata
7232
7234
  });
7233
7235
  return result;
7234
7236
  }
@@ -7940,7 +7942,8 @@ async function createOAuthState(params) {
7940
7942
  publicKey: params.publicKey,
7941
7943
  keyId: params.keyId,
7942
7944
  fingerprint: params.fingerprint,
7943
- algorithm: params.algorithm
7945
+ algorithm: params.algorithm,
7946
+ metadata: params.metadata
7944
7947
  };
7945
7948
  const jwe = await new jose.EncryptJWT({ state }).setProtectedHeader({ alg: "dir", enc: "A256GCM" }).setIssuedAt().setExpirationTime("10m").encrypt(key);
7946
7949
  return encodeURIComponent(jwe);
@@ -7954,7 +7957,7 @@ async function verifyOAuthState(encryptedState) {
7954
7957
 
7955
7958
  // src/server/services/oauth.service.ts
7956
7959
  async function oauthStartService(params) {
7957
- const { provider, returnUrl, publicKey, keyId, fingerprint, algorithm } = params;
7960
+ const { provider, returnUrl, publicKey, keyId, fingerprint, algorithm, metadata } = params;
7958
7961
  if (provider === "google") {
7959
7962
  if (!isGoogleOAuthEnabled()) {
7960
7963
  throw new ValidationError3({
@@ -7967,7 +7970,8 @@ async function oauthStartService(params) {
7967
7970
  publicKey,
7968
7971
  keyId,
7969
7972
  fingerprint,
7970
- algorithm
7973
+ algorithm,
7974
+ metadata
7971
7975
  });
7972
7976
  const authUrl = getGoogleAuthUrl(state);
7973
7977
  return { authUrl };
@@ -8034,7 +8038,8 @@ async function handleGoogleCallback(code, stateData) {
8034
8038
  userId: String(userId),
8035
8039
  provider: "google",
8036
8040
  email: user?.email || void 0,
8037
- phone: user?.phone || void 0
8041
+ phone: user?.phone || void 0,
8042
+ metadata: stateData.metadata
8038
8043
  };
8039
8044
  if (isNewUser) {
8040
8045
  await authRegisterEvent.emit(eventPayload);
@@ -8203,7 +8208,10 @@ var register = route.post("/_auth/register").input({
8203
8208
  verificationToken: Type.String({
8204
8209
  description: "Verification token obtained from /verify-code endpoint"
8205
8210
  }),
8206
- password: PasswordSchema
8211
+ password: PasswordSchema,
8212
+ metadata: Type.Optional(Type.Record(Type.String(), Type.Unknown(), {
8213
+ description: "Custom metadata passed to authRegisterEvent (e.g. referral code, UTM params)"
8214
+ }))
8207
8215
  }, {
8208
8216
  minProperties: 3,
8209
8217
  // email/phone + verificationToken + password
@@ -8924,7 +8932,10 @@ var oauthStart = route4.post("/_auth/oauth/start").input({
8924
8932
  }),
8925
8933
  algorithm: Type.Union(KEY_ALGORITHM.map((a) => Type.Literal(a)), {
8926
8934
  description: "Key algorithm (ES256 or RS256)"
8927
- })
8935
+ }),
8936
+ metadata: Type.Optional(Type.Record(Type.String(), Type.Unknown(), {
8937
+ description: "Custom metadata passed to authRegisterEvent (e.g. referral code, UTM params)"
8938
+ }))
8928
8939
  })
8929
8940
  }).skip(["auth"]).handler(async (c) => {
8930
8941
  const { body } = await c.data();