@spfn/auth 0.2.0-beta.62 → 0.2.0-beta.65

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/server.js CHANGED
@@ -6492,6 +6492,50 @@ var init_invitations_repository = __esm({
6492
6492
  }
6493
6493
  });
6494
6494
 
6495
+ // src/server/lib/oauth/token-cipher.ts
6496
+ import crypto3 from "crypto";
6497
+ import { env as env3 } from "@spfn/auth/config";
6498
+ function getTokenKey() {
6499
+ return crypto3.createHash("sha256").update(`social-token:${env3.SPFN_AUTH_SESSION_SECRET}`).digest();
6500
+ }
6501
+ function isEncrypted(value) {
6502
+ return value.startsWith(ENC_PREFIX);
6503
+ }
6504
+ function encryptToken(plain) {
6505
+ if (isEncrypted(plain)) {
6506
+ return plain;
6507
+ }
6508
+ const iv = crypto3.randomBytes(IV_BYTES);
6509
+ const cipher = crypto3.createCipheriv("aes-256-gcm", getTokenKey(), iv);
6510
+ const ciphertext = Buffer.concat([cipher.update(plain, "utf8"), cipher.final()]);
6511
+ const tag = cipher.getAuthTag();
6512
+ return ENC_PREFIX + Buffer.concat([iv, tag, ciphertext]).toString("base64");
6513
+ }
6514
+ function decryptToken(stored) {
6515
+ if (!isEncrypted(stored)) {
6516
+ return stored;
6517
+ }
6518
+ const packed = Buffer.from(stored.slice(ENC_PREFIX.length), "base64");
6519
+ if (packed.length < IV_BYTES + TAG_BYTES) {
6520
+ throw new Error("Malformed encrypted token: payload too short");
6521
+ }
6522
+ const iv = packed.subarray(0, IV_BYTES);
6523
+ const tag = packed.subarray(IV_BYTES, IV_BYTES + TAG_BYTES);
6524
+ const ciphertext = packed.subarray(IV_BYTES + TAG_BYTES);
6525
+ const decipher = crypto3.createDecipheriv("aes-256-gcm", getTokenKey(), iv);
6526
+ decipher.setAuthTag(tag);
6527
+ return Buffer.concat([decipher.update(ciphertext), decipher.final()]).toString("utf8");
6528
+ }
6529
+ var ENC_PREFIX, IV_BYTES, TAG_BYTES;
6530
+ var init_token_cipher = __esm({
6531
+ "src/server/lib/oauth/token-cipher.ts"() {
6532
+ "use strict";
6533
+ ENC_PREFIX = "enc:v1:";
6534
+ IV_BYTES = 12;
6535
+ TAG_BYTES = 16;
6536
+ }
6537
+ });
6538
+
6495
6539
  // src/server/repositories/social-accounts.repository.ts
6496
6540
  import { eq as eq10, and as and7 } from "drizzle-orm";
6497
6541
  import { BaseRepository as BaseRepository10 } from "@spfn/core/db";
@@ -6500,7 +6544,38 @@ var init_social_accounts_repository = __esm({
6500
6544
  "src/server/repositories/social-accounts.repository.ts"() {
6501
6545
  "use strict";
6502
6546
  init_entities();
6547
+ init_token_cipher();
6503
6548
  SocialAccountsRepository = class extends BaseRepository10 {
6549
+ /**
6550
+ * 저장 row 의 토큰을 평문으로 복호화해 반환한다.
6551
+ *
6552
+ * 레거시 평문(마커 없음)이 감지되면 즉시 재암호화해 저장하는
6553
+ * self-healing 마이그레이션을 수행한다. 호출자에게는 항상 평문이 반환되어
6554
+ * 외부 API 계약(평문 토큰)이 유지된다.
6555
+ */
6556
+ async decryptAccount(account) {
6557
+ if (!account) {
6558
+ return account;
6559
+ }
6560
+ const heal = {};
6561
+ if (account.accessToken && !isEncrypted(account.accessToken)) {
6562
+ heal.accessToken = encryptToken(account.accessToken);
6563
+ }
6564
+ if (account.refreshToken && !isEncrypted(account.refreshToken)) {
6565
+ heal.refreshToken = encryptToken(account.refreshToken);
6566
+ }
6567
+ if (heal.accessToken || heal.refreshToken) {
6568
+ try {
6569
+ await this.db.update(userSocialAccounts).set(heal).where(eq10(userSocialAccounts.id, account.id));
6570
+ } catch {
6571
+ }
6572
+ }
6573
+ return {
6574
+ ...account,
6575
+ accessToken: account.accessToken ? decryptToken(account.accessToken) : account.accessToken,
6576
+ refreshToken: account.refreshToken ? decryptToken(account.refreshToken) : account.refreshToken
6577
+ };
6578
+ }
6504
6579
  /**
6505
6580
  * provider와 providerUserId로 소셜 계정 조회
6506
6581
  * Read replica 사용
@@ -6512,14 +6587,15 @@ var init_social_accounts_repository = __esm({
6512
6587
  eq10(userSocialAccounts.providerUserId, providerUserId)
6513
6588
  )
6514
6589
  ).limit(1);
6515
- return result[0] ?? null;
6590
+ return this.decryptAccount(result[0] ?? null);
6516
6591
  }
6517
6592
  /**
6518
6593
  * userId로 모든 소셜 계정 조회
6519
6594
  * Read replica 사용
6520
6595
  */
6521
6596
  async findByUserId(userId) {
6522
- return await this.readDb.select().from(userSocialAccounts).where(eq10(userSocialAccounts.userId, userId));
6597
+ const result = await this.readDb.select().from(userSocialAccounts).where(eq10(userSocialAccounts.userId, userId));
6598
+ return Promise.all(result.map((account) => this.decryptAccount(account)));
6523
6599
  }
6524
6600
  /**
6525
6601
  * userId와 provider로 소셜 계정 조회
@@ -6532,18 +6608,21 @@ var init_social_accounts_repository = __esm({
6532
6608
  eq10(userSocialAccounts.provider, provider)
6533
6609
  )
6534
6610
  ).limit(1);
6535
- return result[0] ?? null;
6611
+ return this.decryptAccount(result[0] ?? null);
6536
6612
  }
6537
6613
  /**
6538
6614
  * 소셜 계정 생성
6539
6615
  * Write primary 사용
6540
6616
  */
6541
6617
  async create(data) {
6542
- return await this._create(userSocialAccounts, {
6618
+ const created = await this._create(userSocialAccounts, {
6543
6619
  ...data,
6620
+ accessToken: data.accessToken ? encryptToken(data.accessToken) : data.accessToken,
6621
+ refreshToken: data.refreshToken ? encryptToken(data.refreshToken) : data.refreshToken,
6544
6622
  createdAt: /* @__PURE__ */ new Date(),
6545
6623
  updatedAt: /* @__PURE__ */ new Date()
6546
6624
  });
6625
+ return this.decryptAccount(created);
6547
6626
  }
6548
6627
  /**
6549
6628
  * 토큰 정보 업데이트
@@ -6552,9 +6631,11 @@ var init_social_accounts_repository = __esm({
6552
6631
  async updateTokens(id11, data) {
6553
6632
  const result = await this.db.update(userSocialAccounts).set({
6554
6633
  ...data,
6634
+ accessToken: data.accessToken ? encryptToken(data.accessToken) : data.accessToken,
6635
+ refreshToken: data.refreshToken ? encryptToken(data.refreshToken) : data.refreshToken,
6555
6636
  updatedAt: /* @__PURE__ */ new Date()
6556
6637
  }).where(eq10(userSocialAccounts.id, id11)).returning();
6557
- return result[0] ?? null;
6638
+ return this.decryptAccount(result[0] ?? null);
6558
6639
  }
6559
6640
  /**
6560
6641
  * 소셜 계정 삭제
@@ -6921,7 +7002,7 @@ import {
6921
7002
  } from "@spfn/auth/errors";
6922
7003
 
6923
7004
  // src/server/services/verification.service.ts
6924
- import { env as env3 } from "@spfn/auth/config";
7005
+ import { env as env4 } from "@spfn/auth/config";
6925
7006
  import { InvalidVerificationCodeError } from "@spfn/auth/errors";
6926
7007
  import jwt2 from "jsonwebtoken";
6927
7008
  import { sendEmail, sendSMS } from "@spfn/notification/server";
@@ -6989,7 +7070,7 @@ async function markCodeAsUsed(codeId) {
6989
7070
  await verificationCodesRepository.markAsUsed(codeId);
6990
7071
  }
6991
7072
  function createVerificationToken(payload) {
6992
- return jwt2.sign(payload, env3.SPFN_AUTH_VERIFICATION_TOKEN_SECRET, {
7073
+ return jwt2.sign(payload, env4.SPFN_AUTH_VERIFICATION_TOKEN_SECRET, {
6993
7074
  expiresIn: VERIFICATION_TOKEN_EXPIRY,
6994
7075
  issuer: "spfn-auth",
6995
7076
  audience: "spfn-client"
@@ -6997,7 +7078,7 @@ function createVerificationToken(payload) {
6997
7078
  }
6998
7079
  function validateVerificationToken(token) {
6999
7080
  try {
7000
- const decoded = jwt2.verify(token, env3.SPFN_AUTH_VERIFICATION_TOKEN_SECRET, {
7081
+ const decoded = jwt2.verify(token, env4.SPFN_AUTH_VERIFICATION_TOKEN_SECRET, {
7001
7082
  issuer: "spfn-auth",
7002
7083
  audience: "spfn-client"
7003
7084
  });
@@ -7142,7 +7223,7 @@ async function revokeKeyService(params) {
7142
7223
  init_repositories();
7143
7224
  import { ValidationError } from "@spfn/core/errors";
7144
7225
  import { ReservedUsernameError, UsernameAlreadyTakenError } from "@spfn/auth/errors";
7145
- import { env as env4 } from "@spfn/auth/config";
7226
+ import { env as env5 } from "@spfn/auth/config";
7146
7227
  async function getUserByIdService(userId) {
7147
7228
  return await usersRepository.findById(userId);
7148
7229
  }
@@ -7159,7 +7240,7 @@ async function updateUserService(userId, updates) {
7159
7240
  await usersRepository.updateById(userId, updates);
7160
7241
  }
7161
7242
  function getReservedUsernames() {
7162
- const raw = env4.SPFN_AUTH_RESERVED_USERNAMES ?? "";
7243
+ const raw = env5.SPFN_AUTH_RESERVED_USERNAMES ?? "";
7163
7244
  if (!raw) {
7164
7245
  return /* @__PURE__ */ new Set();
7165
7246
  }
@@ -7171,8 +7252,8 @@ function isReservedUsername(username) {
7171
7252
  return getReservedUsernames().has(username.toLowerCase());
7172
7253
  }
7173
7254
  function validateUsernameLength(username) {
7174
- const min = env4.SPFN_AUTH_USERNAME_MIN_LENGTH ?? 3;
7175
- const max = env4.SPFN_AUTH_USERNAME_MAX_LENGTH ?? 30;
7255
+ const min = env5.SPFN_AUTH_USERNAME_MIN_LENGTH ?? 3;
7256
+ const max = env5.SPFN_AUTH_USERNAME_MAX_LENGTH ?? 30;
7176
7257
  if (username.length < min) {
7177
7258
  throw new ValidationError({
7178
7259
  message: `Username must be at least ${min} characters`,
@@ -7428,7 +7509,7 @@ init_rbac();
7428
7509
  import { createHash } from "crypto";
7429
7510
 
7430
7511
  // src/server/lib/config.ts
7431
- import { env as env5 } from "@spfn/auth/config";
7512
+ import { env as env6 } from "@spfn/auth/config";
7432
7513
  function getCookieSuffix() {
7433
7514
  const port = process.env.PORT;
7434
7515
  return port ? `_${port}` : "";
@@ -7490,7 +7571,7 @@ function getSessionTtl(override) {
7490
7571
  if (globalConfig.sessionTtl !== void 0) {
7491
7572
  return parseDuration(globalConfig.sessionTtl);
7492
7573
  }
7493
- const envTtl = env5.SPFN_AUTH_SESSION_TTL;
7574
+ const envTtl = env6.SPFN_AUTH_SESSION_TTL;
7494
7575
  if (envTtl) {
7495
7576
  return parseDuration(envTtl);
7496
7577
  }
@@ -7706,9 +7787,9 @@ init_role_service();
7706
7787
 
7707
7788
  // src/server/services/invitation.service.ts
7708
7789
  init_repositories();
7709
- import crypto3 from "crypto";
7790
+ import crypto4 from "crypto";
7710
7791
  function generateInvitationToken() {
7711
- return crypto3.randomUUID();
7792
+ return crypto4.randomUUID();
7712
7793
  }
7713
7794
  function calculateExpiresAt(days = 7) {
7714
7795
  const expiresAt = /* @__PURE__ */ new Date();
@@ -8023,25 +8104,25 @@ async function updateUserProfileService(userId, params) {
8023
8104
 
8024
8105
  // src/server/services/oauth.service.ts
8025
8106
  init_repositories();
8026
- import { env as env8 } from "@spfn/auth/config";
8107
+ import { env as env9 } from "@spfn/auth/config";
8027
8108
  import { ValidationError as ValidationError3 } from "@spfn/core/errors";
8028
8109
 
8029
8110
  // src/server/lib/oauth/google.ts
8030
- import { env as env6 } from "@spfn/auth/config";
8111
+ import { env as env7 } from "@spfn/auth/config";
8031
8112
  var GOOGLE_AUTH_URL = "https://accounts.google.com/o/oauth2/v2/auth";
8032
8113
  var GOOGLE_TOKEN_URL = "https://oauth2.googleapis.com/token";
8033
8114
  var GOOGLE_USERINFO_URL = "https://www.googleapis.com/oauth2/v2/userinfo";
8034
8115
  function isGoogleOAuthEnabled() {
8035
- return !!(env6.SPFN_AUTH_GOOGLE_CLIENT_ID && env6.SPFN_AUTH_GOOGLE_CLIENT_SECRET);
8116
+ return !!(env7.SPFN_AUTH_GOOGLE_CLIENT_ID && env7.SPFN_AUTH_GOOGLE_CLIENT_SECRET);
8036
8117
  }
8037
8118
  function getGoogleOAuthConfig() {
8038
- const clientId = env6.SPFN_AUTH_GOOGLE_CLIENT_ID;
8039
- const clientSecret = env6.SPFN_AUTH_GOOGLE_CLIENT_SECRET;
8119
+ const clientId = env7.SPFN_AUTH_GOOGLE_CLIENT_ID;
8120
+ const clientSecret = env7.SPFN_AUTH_GOOGLE_CLIENT_SECRET;
8040
8121
  if (!clientId || !clientSecret) {
8041
8122
  throw new Error("Google OAuth is not configured. Set SPFN_AUTH_GOOGLE_CLIENT_ID and SPFN_AUTH_GOOGLE_CLIENT_SECRET.");
8042
8123
  }
8043
- const baseUrl = env6.NEXT_PUBLIC_SPFN_API_URL || env6.SPFN_API_URL;
8044
- const redirectUri = env6.SPFN_AUTH_GOOGLE_REDIRECT_URI || `${baseUrl}/_auth/oauth/google/callback`;
8124
+ const baseUrl = env7.NEXT_PUBLIC_SPFN_API_URL || env7.SPFN_API_URL;
8125
+ const redirectUri = env7.SPFN_AUTH_GOOGLE_REDIRECT_URI || `${baseUrl}/_auth/oauth/google/callback`;
8045
8126
  return {
8046
8127
  clientId,
8047
8128
  clientSecret,
@@ -8049,7 +8130,7 @@ function getGoogleOAuthConfig() {
8049
8130
  };
8050
8131
  }
8051
8132
  function getDefaultScopes() {
8052
- const envScopes = env6.SPFN_AUTH_GOOGLE_SCOPES;
8133
+ const envScopes = env7.SPFN_AUTH_GOOGLE_SCOPES;
8053
8134
  if (envScopes) {
8054
8135
  return envScopes.split(",").map((s) => s.trim()).filter(Boolean);
8055
8136
  }
@@ -8127,9 +8208,9 @@ async function refreshAccessToken(refreshToken) {
8127
8208
 
8128
8209
  // src/server/lib/oauth/state.ts
8129
8210
  import * as jose from "jose";
8130
- import { env as env7 } from "@spfn/auth/config";
8211
+ import { env as env8 } from "@spfn/auth/config";
8131
8212
  async function getStateKey() {
8132
- const secret = env7.SPFN_AUTH_SESSION_SECRET;
8213
+ const secret = env8.SPFN_AUTH_SESSION_SECRET;
8133
8214
  const encoder = new TextEncoder();
8134
8215
  const data = encoder.encode(`oauth-state:${secret}`);
8135
8216
  const hashBuffer = await crypto.subtle.digest("SHA-256", data);
@@ -8282,8 +8363,8 @@ async function oauthCallbackService(params) {
8282
8363
  algorithm: stateData.algorithm
8283
8364
  });
8284
8365
  await updateLastLoginService(userId);
8285
- const appUrl = env8.NEXT_PUBLIC_SPFN_APP_URL || env8.SPFN_APP_URL;
8286
- const callbackPath = env8.SPFN_AUTH_OAUTH_SUCCESS_URL || "/auth/callback";
8366
+ const appUrl = env9.NEXT_PUBLIC_SPFN_APP_URL || env9.SPFN_APP_URL;
8367
+ const callbackPath = env9.SPFN_AUTH_OAUTH_SUCCESS_URL || "/auth/callback";
8287
8368
  const callbackUrl = callbackPath.startsWith("http") ? callbackPath : `${appUrl}${callbackPath}`;
8288
8369
  const redirectUrl = buildRedirectUrl(callbackUrl, {
8289
8370
  userId: String(userId),
@@ -8368,7 +8449,7 @@ function buildRedirectUrl(baseUrl, params) {
8368
8449
  return `${url.pathname}${url.search}`;
8369
8450
  }
8370
8451
  function buildOAuthErrorUrl(error) {
8371
- const errorUrl = env8.SPFN_AUTH_OAUTH_ERROR_URL || "/auth/error?error={error}";
8452
+ const errorUrl = env9.SPFN_AUTH_OAUTH_ERROR_URL || "/auth/error?error={error}";
8372
8453
  return errorUrl.replace("{error}", encodeURIComponent(error));
8373
8454
  }
8374
8455
  function isOAuthProviderEnabled(provider) {
@@ -9158,7 +9239,13 @@ var userRouter = defineRouter3({
9158
9239
  init_esm();
9159
9240
  init_types();
9160
9241
  import { Transactional as Transactional2 } from "@spfn/core/db";
9242
+ import { ValidationError as ValidationError4 } from "@spfn/core/errors";
9161
9243
  import { defineRouter as defineRouter4, route as route4 } from "@spfn/core/route";
9244
+ var providerParams = Type.Object({
9245
+ provider: Type.Union(SOCIAL_PROVIDERS.map((p) => Type.Literal(p)), {
9246
+ description: "OAuth provider id (google, github, kakao, naver, superself)"
9247
+ })
9248
+ });
9162
9249
  var oauthGoogleStart = route4.get("/_auth/oauth/google").input({
9163
9250
  query: Type.Object({
9164
9251
  state: Type.String({
@@ -9255,10 +9342,12 @@ var getGoogleOAuthUrl = route4.post("/_auth/oauth/google/url").input({
9255
9342
  }).skip(["auth"]).handler(async (c) => {
9256
9343
  const { body } = await c.data();
9257
9344
  if (!isGoogleOAuthEnabled()) {
9258
- throw new Error("Google OAuth is not configured");
9345
+ throw new ValidationError4({ message: "Google OAuth is not configured" });
9259
9346
  }
9260
9347
  if (!body.state) {
9261
- throw new Error("OAuth state is required. Ensure the OAuth interceptor is configured.");
9348
+ throw new ValidationError4({
9349
+ message: "OAuth state is required. Ensure the OAuth interceptor is configured."
9350
+ });
9262
9351
  }
9263
9352
  return { authUrl: getGoogleAuthUrl(body.state) };
9264
9353
  });
@@ -9277,13 +9366,88 @@ var oauthFinalize = route4.post("/_auth/oauth/finalize").input({
9277
9366
  returnUrl: body.returnUrl || "/"
9278
9367
  };
9279
9368
  });
9369
+ var oauthProviderStart = route4.get("/_auth/oauth/:provider").input({
9370
+ params: providerParams,
9371
+ query: Type.Object({
9372
+ state: Type.String({
9373
+ description: "Encrypted OAuth state (returnUrl, publicKey, keyId, fingerprint, algorithm)"
9374
+ })
9375
+ })
9376
+ }).skip(["auth"]).handler(async (c) => {
9377
+ const { params, query } = await c.data();
9378
+ const provider = getOAuthProvider(params.provider);
9379
+ if (!provider?.isEnabled()) {
9380
+ return c.redirect(buildOAuthErrorUrl(`OAuth provider '${params.provider}' is not configured`));
9381
+ }
9382
+ return c.redirect(provider.getAuthUrl(query.state));
9383
+ });
9384
+ var oauthProviderCallback = route4.get("/_auth/oauth/:provider/callback").input({
9385
+ params: providerParams,
9386
+ query: Type.Object({
9387
+ code: Type.Optional(Type.String({
9388
+ description: "Authorization code from provider"
9389
+ })),
9390
+ state: Type.Optional(Type.String({
9391
+ description: "OAuth state parameter"
9392
+ })),
9393
+ error: Type.Optional(Type.String({
9394
+ description: "Error code from provider"
9395
+ })),
9396
+ error_description: Type.Optional(Type.String({
9397
+ description: "Error description from provider"
9398
+ }))
9399
+ })
9400
+ }).use([Transactional2()]).skip(["auth"]).handler(async (c) => {
9401
+ const { params, query } = await c.data();
9402
+ if (query.error) {
9403
+ const errorMessage = query.error_description || query.error;
9404
+ return c.redirect(buildOAuthErrorUrl(errorMessage));
9405
+ }
9406
+ if (!query.code || !query.state) {
9407
+ return c.redirect(buildOAuthErrorUrl("Missing authorization code or state"));
9408
+ }
9409
+ try {
9410
+ const result = await oauthCallbackService({
9411
+ provider: params.provider,
9412
+ code: query.code,
9413
+ state: query.state
9414
+ });
9415
+ return c.redirect(result.redirectUrl);
9416
+ } catch (err) {
9417
+ const message = err instanceof Error ? err.message : "OAuth callback failed";
9418
+ return c.redirect(buildOAuthErrorUrl(message));
9419
+ }
9420
+ });
9421
+ var getProviderOAuthUrl = route4.post("/_auth/oauth/:provider/url").input({
9422
+ params: providerParams,
9423
+ body: Type.Object({
9424
+ returnUrl: Type.Optional(Type.String({
9425
+ description: "URL to redirect after OAuth success"
9426
+ })),
9427
+ state: Type.Optional(Type.String({
9428
+ description: "Encrypted OAuth state (injected by interceptor)"
9429
+ }))
9430
+ })
9431
+ }).skip(["auth"]).handler(async (c) => {
9432
+ const { params, body } = await c.data();
9433
+ const provider = requireEnabledProvider(params.provider);
9434
+ if (!body.state) {
9435
+ throw new ValidationError4({
9436
+ message: "OAuth state is required. Ensure the OAuth interceptor is configured."
9437
+ });
9438
+ }
9439
+ return { authUrl: provider.getAuthUrl(body.state) };
9440
+ });
9280
9441
  var oauthRouter = defineRouter4({
9281
9442
  oauthGoogleStart,
9282
9443
  oauthGoogleCallback,
9283
9444
  oauthStart,
9284
9445
  oauthProviders,
9285
9446
  getGoogleOAuthUrl,
9286
- oauthFinalize
9447
+ oauthFinalize,
9448
+ oauthProviderStart,
9449
+ oauthProviderCallback,
9450
+ getProviderOAuthUrl
9287
9451
  });
9288
9452
 
9289
9453
  // src/server/routes/admin/index.ts
@@ -9403,6 +9567,9 @@ var mainAuthRouter = defineRouter5({
9403
9567
  oauthProviders,
9404
9568
  getGoogleOAuthUrl,
9405
9569
  oauthFinalize,
9570
+ oauthProviderStart,
9571
+ oauthProviderCallback,
9572
+ getProviderOAuthUrl,
9406
9573
  // Invitation routes
9407
9574
  getInvitation,
9408
9575
  acceptInvitation: acceptInvitation2,
@@ -9432,11 +9599,11 @@ init_types();
9432
9599
  init_schema3();
9433
9600
 
9434
9601
  // src/server/lib/crypto.ts
9435
- import crypto4 from "crypto";
9602
+ import crypto5 from "crypto";
9436
9603
  import jwt3 from "jsonwebtoken";
9437
9604
  function generateKeyPairES256() {
9438
- const keyId = crypto4.randomUUID();
9439
- const { privateKey, publicKey } = crypto4.generateKeyPairSync("ec", {
9605
+ const keyId = crypto5.randomUUID();
9606
+ const { privateKey, publicKey } = crypto5.generateKeyPairSync("ec", {
9440
9607
  namedCurve: "P-256",
9441
9608
  // ES256
9442
9609
  publicKeyEncoding: {
@@ -9450,7 +9617,7 @@ function generateKeyPairES256() {
9450
9617
  });
9451
9618
  const privateKeyB64 = privateKey.toString("base64");
9452
9619
  const publicKeyB64 = publicKey.toString("base64");
9453
- const fingerprint = crypto4.createHash("sha256").update(publicKey).digest("hex");
9620
+ const fingerprint = crypto5.createHash("sha256").update(publicKey).digest("hex");
9454
9621
  return {
9455
9622
  privateKey: privateKeyB64,
9456
9623
  publicKey: publicKeyB64,
@@ -9460,8 +9627,8 @@ function generateKeyPairES256() {
9460
9627
  };
9461
9628
  }
9462
9629
  function generateKeyPairRS256() {
9463
- const keyId = crypto4.randomUUID();
9464
- const { privateKey, publicKey } = crypto4.generateKeyPairSync("rsa", {
9630
+ const keyId = crypto5.randomUUID();
9631
+ const { privateKey, publicKey } = crypto5.generateKeyPairSync("rsa", {
9465
9632
  modulusLength: 2048,
9466
9633
  publicKeyEncoding: {
9467
9634
  type: "spki",
@@ -9474,7 +9641,7 @@ function generateKeyPairRS256() {
9474
9641
  });
9475
9642
  const privateKeyB64 = privateKey.toString("base64");
9476
9643
  const publicKeyB64 = publicKey.toString("base64");
9477
- const fingerprint = crypto4.createHash("sha256").update(publicKey).digest("hex");
9644
+ const fingerprint = crypto5.createHash("sha256").update(publicKey).digest("hex");
9478
9645
  return {
9479
9646
  privateKey: privateKeyB64,
9480
9647
  publicKey: publicKeyB64,
@@ -9489,7 +9656,7 @@ function generateKeyPair(algorithm = "ES256") {
9489
9656
  function generateClientToken(payload, privateKeyB64, algorithm, options) {
9490
9657
  try {
9491
9658
  const privateKeyDER = Buffer.from(privateKeyB64, "base64");
9492
- const privateKeyObject = crypto4.createPrivateKey({
9659
+ const privateKeyObject = crypto5.createPrivateKey({
9493
9660
  key: privateKeyDER,
9494
9661
  format: "der",
9495
9662
  type: "pkcs8"
@@ -9533,10 +9700,10 @@ function shouldRotateKey(createdAt, rotationDays = 90) {
9533
9700
 
9534
9701
  // src/server/lib/session.ts
9535
9702
  import * as jose2 from "jose";
9536
- import { env as env9 } from "@spfn/auth/config";
9703
+ import { env as env10 } from "@spfn/auth/config";
9537
9704
  import { env as coreEnv } from "@spfn/core/config";
9538
9705
  async function getSessionSecretKey() {
9539
- const secret = env9.SPFN_AUTH_SESSION_SECRET;
9706
+ const secret = env10.SPFN_AUTH_SESSION_SECRET;
9540
9707
  const encoder = new TextEncoder();
9541
9708
  const data = encoder.encode(secret);
9542
9709
  const hashBuffer = await crypto.subtle.digest("SHA-256", data);
@@ -9618,14 +9785,14 @@ async function shouldRefreshSession(jwt4, thresholdHours = 24) {
9618
9785
  }
9619
9786
 
9620
9787
  // src/server/setup.ts
9621
- import { env as env10 } from "@spfn/auth/config";
9788
+ import { env as env11 } from "@spfn/auth/config";
9622
9789
  import { getRoleByName as getRoleByName2 } from "@spfn/auth/server";
9623
9790
  init_repositories();
9624
9791
  function parseAdminAccounts() {
9625
9792
  const accounts = [];
9626
- if (env10.SPFN_AUTH_ADMIN_ACCOUNTS) {
9793
+ if (env11.SPFN_AUTH_ADMIN_ACCOUNTS) {
9627
9794
  try {
9628
- const accountsJson = env10.SPFN_AUTH_ADMIN_ACCOUNTS;
9795
+ const accountsJson = env11.SPFN_AUTH_ADMIN_ACCOUNTS;
9629
9796
  const parsed = JSON.parse(accountsJson);
9630
9797
  if (!Array.isArray(parsed)) {
9631
9798
  authLogger.setup.error("\u274C SPFN_AUTH_ADMIN_ACCOUNTS must be an array");
@@ -9652,11 +9819,11 @@ function parseAdminAccounts() {
9652
9819
  return accounts;
9653
9820
  }
9654
9821
  }
9655
- const adminEmails = env10.SPFN_AUTH_ADMIN_EMAILS;
9822
+ const adminEmails = env11.SPFN_AUTH_ADMIN_EMAILS;
9656
9823
  if (adminEmails) {
9657
9824
  const emails = adminEmails.split(",").map((s) => s.trim());
9658
- const passwords = (env10.SPFN_AUTH_ADMIN_PASSWORDS || "").split(",").map((s) => s.trim());
9659
- const roles2 = (env10.SPFN_AUTH_ADMIN_ROLES || "").split(",").map((s) => s.trim());
9825
+ const passwords = (env11.SPFN_AUTH_ADMIN_PASSWORDS || "").split(",").map((s) => s.trim());
9826
+ const roles2 = (env11.SPFN_AUTH_ADMIN_ROLES || "").split(",").map((s) => s.trim());
9660
9827
  if (passwords.length !== emails.length) {
9661
9828
  authLogger.setup.error("\u274C SPFN_AUTH_ADMIN_EMAILS and SPFN_AUTH_ADMIN_PASSWORDS length mismatch");
9662
9829
  return accounts;
@@ -9678,8 +9845,8 @@ function parseAdminAccounts() {
9678
9845
  }
9679
9846
  return accounts;
9680
9847
  }
9681
- const adminEmail = env10.SPFN_AUTH_ADMIN_EMAIL;
9682
- const adminPassword = env10.SPFN_AUTH_ADMIN_PASSWORD;
9848
+ const adminEmail = env11.SPFN_AUTH_ADMIN_EMAIL;
9849
+ const adminPassword = env11.SPFN_AUTH_ADMIN_PASSWORD;
9683
9850
  if (adminEmail && adminPassword) {
9684
9851
  accounts.push({
9685
9852
  email: adminEmail,
@@ -9876,6 +10043,7 @@ export {
9876
10043
  registerService,
9877
10044
  removePermissionFromRole,
9878
10045
  requireAnyPermission,
10046
+ requireEnabledProvider,
9879
10047
  requirePermissions,
9880
10048
  requireRole,
9881
10049
  resendInvitation,