najm-auth 3.3.1 → 3.4.0

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/index.js CHANGED
@@ -3361,9 +3361,14 @@ var AuthService = class AuthService2 {
3361
3361
  return { recovered: true };
3362
3362
  }
3363
3363
  async logoutUser(userId, authorization) {
3364
- await this.tokenService.logout(userId, authorization);
3365
- this.cookieManager.clearRefreshToken();
3366
- this.cookieManager.clearSessionCookie();
3364
+ try {
3365
+ if (userId) {
3366
+ await this.tokenService.logout(userId, authorization);
3367
+ }
3368
+ } finally {
3369
+ this.cookieManager.clearRefreshToken();
3370
+ this.cookieManager.clearSessionCookie();
3371
+ }
3367
3372
  return { data: null, message: this.t("auth.success.logout") };
3368
3373
  }
3369
3374
  /**
@@ -3857,8 +3862,6 @@ __decorate20([
3857
3862
  ], AuthController.prototype, "recoverSession", null);
3858
3863
  __decorate20([
3859
3864
  Post("/logout"),
3860
- isAuth(),
3861
- RateLimit({ limit: 10, window: "15m", key: "user" }),
3862
3865
  __param5(0, User2("id")),
3863
3866
  __param5(1, Headers("authorization")),
3864
3867
  __metadata20("design:type", Function),
@@ -5396,7 +5399,7 @@ function toSingular(plural) {
5396
5399
  }
5397
5400
  __name(toSingular, "toSingular");
5398
5401
  function createResourceGuards(ownershipClass, resourceType, resource, options) {
5399
- var _a27, _b15;
5402
+ var _a28, _b15;
5400
5403
  const writeGuard = options?.adminGuard ?? isAdmin;
5401
5404
  let AccessGuard = class AccessGuard {
5402
5405
  static {
@@ -5417,7 +5420,7 @@ function createResourceGuards(ownershipClass, resourceType, resource, options) {
5417
5420
  __param12(1, Params4("id")),
5418
5421
  __metadata31("design:type", Function),
5419
5422
  __metadata31("design:paramtypes", [Object, String]),
5420
- __metadata31("design:returntype", typeof (_a27 = typeof Promise !== "undefined" && Promise) === "function" ? _a27 : Object)
5423
+ __metadata31("design:returntype", typeof (_a28 = typeof Promise !== "undefined" && Promise) === "function" ? _a28 : Object)
5421
5424
  ], AccessGuard.prototype, "canActivate", null);
5422
5425
  AccessGuard = __decorate31([
5423
5426
  Injectable15()
@@ -5578,7 +5581,7 @@ function configureOwnership(config) {
5578
5581
  Injectable15()
5579
5582
  ], GeneratedOwnershipService);
5580
5583
  function bodyGuard(resourceType, bodyField, optional = false) {
5581
- var _a27;
5584
+ var _a28;
5582
5585
  let BodyAccessGuard = class BodyAccessGuard {
5583
5586
  static {
5584
5587
  __name(this, "BodyAccessGuard");
@@ -5600,7 +5603,7 @@ function configureOwnership(config) {
5600
5603
  __param12(1, Body6()),
5601
5604
  __metadata31("design:type", Function),
5602
5605
  __metadata31("design:paramtypes", [Object, Object]),
5603
- __metadata31("design:returntype", typeof (_a27 = typeof Promise !== "undefined" && Promise) === "function" ? _a27 : Object)
5606
+ __metadata31("design:returntype", typeof (_a28 = typeof Promise !== "undefined" && Promise) === "function" ? _a28 : Object)
5604
5607
  ], BodyAccessGuard.prototype, "canActivate", null);
5605
5608
  BodyAccessGuard = __decorate31([
5606
5609
  Injectable15()
@@ -6155,17 +6158,162 @@ GoogleOAuthProvider = __decorate34([
6155
6158
  __metadata34("design:paramtypes", [typeof (_a21 = typeof GoogleTokenVerifier !== "undefined" && GoogleTokenVerifier) === "function" ? _a21 : Object])
6156
6159
  ], GoogleOAuthProvider);
6157
6160
 
6161
+ // src/oauth/github/GitHubOAuthProvider.ts
6162
+ import { Inject as Inject19, Injectable as Injectable19 } from "najm-core";
6163
+ var __decorate35 = function(decorators, target, key, desc) {
6164
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
6165
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
6166
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6167
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6168
+ };
6169
+ var __metadata35 = function(k, v) {
6170
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
6171
+ };
6172
+ var AUTHORIZATION_ENDPOINT2 = "https://github.com/login/oauth/authorize";
6173
+ var TOKEN_ENDPOINT2 = "https://github.com/login/oauth/access_token";
6174
+ var USER_ENDPOINT = "https://api.github.com/user";
6175
+ var EMAILS_ENDPOINT = "https://api.github.com/user/emails";
6176
+ var API_VERSION = "2022-11-28";
6177
+ var GitHubOAuthProvider = class GitHubOAuthProvider2 {
6178
+ static {
6179
+ __name(this, "GitHubOAuthProvider");
6180
+ }
6181
+ config;
6182
+ authorizationUrl(attempt, codeChallenge) {
6183
+ const github = this.githubConfig();
6184
+ const url = new URL(AUTHORIZATION_ENDPOINT2);
6185
+ url.search = new URLSearchParams({
6186
+ client_id: github.clientId,
6187
+ redirect_uri: github.callbackUrl,
6188
+ scope: "user:email",
6189
+ state: attempt.state,
6190
+ code_challenge: codeChallenge,
6191
+ code_challenge_method: "S256"
6192
+ }).toString();
6193
+ return url.toString();
6194
+ }
6195
+ async exchange(code, attempt) {
6196
+ const github = this.githubConfig();
6197
+ const token = await this.exchangeCode(code, attempt.codeVerifier, github);
6198
+ const headers = {
6199
+ accept: "application/vnd.github+json",
6200
+ authorization: `Bearer ${token}`,
6201
+ "user-agent": "najm-auth",
6202
+ "x-github-api-version": API_VERSION
6203
+ };
6204
+ const [user, emails] = await Promise.all([
6205
+ this.fetchJson(USER_ENDPOINT, headers),
6206
+ this.fetchJson(EMAILS_ENDPOINT, headers)
6207
+ ]);
6208
+ const primary = Array.isArray(emails) ? emails.find((entry) => entry.primary === true && entry.verified === true) : void 0;
6209
+ const email2 = primary?.email?.trim().toLowerCase();
6210
+ const providerAccountId = user.id === void 0 ? "" : String(user.id);
6211
+ const login = user.login?.trim() ?? "";
6212
+ if (!providerAccountId || !login || !email2) {
6213
+ throw new OAuthFlowError("oauth_verified_email_required", 403);
6214
+ }
6215
+ return {
6216
+ provider: "github",
6217
+ providerAccountId,
6218
+ email: email2,
6219
+ emailVerified: true,
6220
+ login,
6221
+ name: user.name?.trim() || login,
6222
+ picture: user.avatar_url?.trim() || void 0
6223
+ };
6224
+ }
6225
+ async exchangeCode(code, codeVerifier, github) {
6226
+ let response;
6227
+ try {
6228
+ response = await fetch(TOKEN_ENDPOINT2, {
6229
+ method: "POST",
6230
+ headers: {
6231
+ accept: "application/json",
6232
+ "content-type": "application/x-www-form-urlencoded"
6233
+ },
6234
+ body: new URLSearchParams({
6235
+ client_id: github.clientId,
6236
+ client_secret: github.clientSecret,
6237
+ code,
6238
+ redirect_uri: github.callbackUrl,
6239
+ code_verifier: codeVerifier
6240
+ }),
6241
+ signal: AbortSignal.timeout(15e3)
6242
+ });
6243
+ } catch {
6244
+ throw new OAuthFlowError("oauth_provider_error", 502);
6245
+ }
6246
+ if (!response.ok)
6247
+ throw new OAuthFlowError("oauth_provider_error", 502);
6248
+ let body;
6249
+ try {
6250
+ body = await response.json();
6251
+ } catch {
6252
+ throw new OAuthFlowError("oauth_provider_error", 502);
6253
+ }
6254
+ const accessToken = body.access_token;
6255
+ if (typeof accessToken !== "string" || !accessToken) {
6256
+ throw new OAuthFlowError("oauth_provider_error", 502);
6257
+ }
6258
+ return accessToken;
6259
+ }
6260
+ async fetchJson(url, headers) {
6261
+ let response;
6262
+ try {
6263
+ response = await fetch(url, {
6264
+ headers,
6265
+ signal: AbortSignal.timeout(15e3)
6266
+ });
6267
+ } catch {
6268
+ throw new OAuthFlowError("oauth_provider_error", 502);
6269
+ }
6270
+ if (!response.ok)
6271
+ throw new OAuthFlowError("oauth_provider_error", 502);
6272
+ try {
6273
+ return await response.json();
6274
+ } catch {
6275
+ throw new OAuthFlowError("oauth_provider_error", 502);
6276
+ }
6277
+ }
6278
+ githubConfig() {
6279
+ const github = this.config.oauth?.github;
6280
+ if (!github)
6281
+ throw new OAuthFlowError("oauth_provider_disabled", 404);
6282
+ return github;
6283
+ }
6284
+ };
6285
+ __decorate35([
6286
+ Inject19(AUTH_CONFIG),
6287
+ __metadata35("design:type", Object)
6288
+ ], GitHubOAuthProvider.prototype, "config", void 0);
6289
+ GitHubOAuthProvider = __decorate35([
6290
+ Injectable19()
6291
+ ], GitHubOAuthProvider);
6292
+
6293
+ // src/oauth/GitHubOAuthController.ts
6294
+ import { createHash as createHash5 } from "crypto";
6295
+ import { Controller as Controller6, Ctx as Ctx2, Get as Get5, Post as Post6, Query as Query2, User as User5 } from "najm-core";
6296
+ import { RateLimit as RateLimit3 } from "najm-rate";
6297
+
6298
+ // src/oauth/OAuthService.ts
6299
+ import { Inject as Inject22, Injectable as Injectable22, Log as Log2 } from "najm-core";
6300
+
6301
+ // src/oauth/OAuthAccountService.ts
6302
+ import { randomBytes as randomBytes3 } from "crypto";
6303
+ import { Inject as Inject21, Injectable as Injectable20 } from "najm-core";
6304
+ import { Transaction as Transaction5 } from "najm-database";
6305
+
6158
6306
  // src/oauth/OAuthAccountRepository.ts
6159
6307
  import { and as and6, eq as eq9 } from "drizzle-orm";
6160
- import { Inject as Inject19, Repository as Repository7 } from "najm-core";
6308
+ import { Inject as Inject20, Repository as Repository7 } from "najm-core";
6161
6309
  import { DB as DB7 } from "najm-database";
6162
- var __decorate35 = function(decorators, target, key, desc) {
6310
+ var __decorate36 = function(decorators, target, key, desc) {
6163
6311
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
6164
6312
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
6165
6313
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6166
6314
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6167
6315
  };
6168
- var __metadata35 = function(k, v) {
6316
+ var __metadata36 = function(k, v) {
6169
6317
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
6170
6318
  };
6171
6319
  var OAuthAccountRepository = class OAuthAccountRepository2 {
@@ -6193,29 +6341,26 @@ var OAuthAccountRepository = class OAuthAccountRepository2 {
6193
6341
  return account;
6194
6342
  }
6195
6343
  };
6196
- __decorate35([
6344
+ __decorate36([
6197
6345
  DB7(),
6198
- __metadata35("design:type", Object)
6346
+ __metadata36("design:type", Object)
6199
6347
  ], OAuthAccountRepository.prototype, "db", void 0);
6200
- __decorate35([
6201
- Inject19(AUTH_SCHEMA),
6202
- __metadata35("design:type", Object)
6348
+ __decorate36([
6349
+ Inject20(AUTH_SCHEMA),
6350
+ __metadata36("design:type", Object)
6203
6351
  ], OAuthAccountRepository.prototype, "schema", void 0);
6204
- OAuthAccountRepository = __decorate35([
6352
+ OAuthAccountRepository = __decorate36([
6205
6353
  Repository7()
6206
6354
  ], OAuthAccountRepository);
6207
6355
 
6208
6356
  // src/oauth/OAuthAccountService.ts
6209
- import { randomBytes as randomBytes3 } from "crypto";
6210
- import { Inject as Inject20, Injectable as Injectable19 } from "najm-core";
6211
- import { Transaction as Transaction5 } from "najm-database";
6212
- var __decorate36 = function(decorators, target, key, desc) {
6357
+ var __decorate37 = function(decorators, target, key, desc) {
6213
6358
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
6214
6359
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
6215
6360
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6216
6361
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6217
6362
  };
6218
- var __metadata36 = function(k, v) {
6363
+ var __metadata37 = function(k, v) {
6219
6364
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
6220
6365
  };
6221
6366
  var _a22;
@@ -6234,18 +6379,18 @@ var OAuthAccountService = class OAuthAccountService2 {
6234
6379
  this.users = users;
6235
6380
  }
6236
6381
  async resolveForLogin(identity) {
6237
- const linked = await this.accounts.getByProviderAccount("google", identity.providerAccountId);
6382
+ const linked = await this.accounts.getByProviderAccount(identity.provider, identity.providerAccountId);
6238
6383
  if (linked)
6239
6384
  return this.users.getById(linked.userId);
6240
6385
  const existingUser = await this.users.findByEmailInsensitive(identity.email);
6241
6386
  if (existingUser) {
6242
- if (!this.googleConfig().autoLinkVerifiedEmail) {
6387
+ if (!this.providerConfig(identity.provider).autoLinkVerifiedEmail) {
6243
6388
  throw new OAuthFlowError("oauth_account_link_required", 409);
6244
6389
  }
6245
6390
  await this.createLink(existingUser.id, identity);
6246
6391
  return this.users.getById(existingUser.id);
6247
6392
  }
6248
- if (!this.googleConfig().allowSignup) {
6393
+ if (!this.providerConfig(identity.provider).allowSignup) {
6249
6394
  throw new OAuthFlowError("oauth_signup_disabled", 403);
6250
6395
  }
6251
6396
  const password = `${randomBytes3(32).toString("base64url")}Aa1`;
@@ -6263,13 +6408,13 @@ var OAuthAccountService = class OAuthAccountService2 {
6263
6408
  const user = await this.users.getById(userId);
6264
6409
  if (user.status !== "active")
6265
6410
  throw new OAuthFlowError("oauth_account_inactive", 403);
6266
- const providerAccount = await this.accounts.getByProviderAccount("google", identity.providerAccountId);
6411
+ const providerAccount = await this.accounts.getByProviderAccount(identity.provider, identity.providerAccountId);
6267
6412
  if (providerAccount && providerAccount.userId !== userId) {
6268
6413
  throw new OAuthFlowError("oauth_provider_account_linked", 409);
6269
6414
  }
6270
6415
  if (providerAccount)
6271
6416
  return user;
6272
- const userProvider = await this.accounts.getByUserProvider(userId, "google");
6417
+ const userProvider = await this.accounts.getByUserProvider(userId, identity.provider);
6273
6418
  if (userProvider && userProvider.providerAccountId !== identity.providerAccountId) {
6274
6419
  throw new OAuthFlowError("oauth_user_provider_linked", 409);
6275
6420
  }
@@ -6280,69 +6425,61 @@ var OAuthAccountService = class OAuthAccountService2 {
6280
6425
  async createLink(userId, identity) {
6281
6426
  const created = await this.accounts.create({
6282
6427
  userId,
6283
- provider: "google",
6428
+ provider: identity.provider,
6284
6429
  providerAccountId: identity.providerAccountId
6285
6430
  });
6286
6431
  if (created)
6287
6432
  return;
6288
- const linked = await this.accounts.getByProviderAccount("google", identity.providerAccountId);
6433
+ const linked = await this.accounts.getByProviderAccount(identity.provider, identity.providerAccountId);
6289
6434
  if (!linked || linked.userId !== userId) {
6290
6435
  throw new OAuthFlowError("oauth_provider_account_linked", 409);
6291
6436
  }
6292
6437
  }
6293
- googleConfig() {
6294
- const google = this.config.oauth?.google;
6295
- if (!google)
6438
+ providerConfig(provider) {
6439
+ const providerConfig = this.config.oauth?.[provider];
6440
+ if (!providerConfig)
6296
6441
  throw new OAuthFlowError("oauth_provider_disabled", 404);
6297
- return google;
6442
+ return providerConfig;
6298
6443
  }
6299
6444
  };
6300
- __decorate36([
6301
- Inject20(AUTH_CONFIG),
6302
- __metadata36("design:type", Object)
6445
+ __decorate37([
6446
+ Inject21(AUTH_CONFIG),
6447
+ __metadata37("design:type", Object)
6303
6448
  ], OAuthAccountService.prototype, "config", void 0);
6304
- __decorate36([
6449
+ __decorate37([
6305
6450
  Transaction5(),
6306
- __metadata36("design:type", Function),
6307
- __metadata36("design:paramtypes", [Object]),
6308
- __metadata36("design:returntype", typeof (_c9 = typeof Promise !== "undefined" && Promise) === "function" ? _c9 : Object)
6451
+ __metadata37("design:type", Function),
6452
+ __metadata37("design:paramtypes", [Object]),
6453
+ __metadata37("design:returntype", typeof (_c9 = typeof Promise !== "undefined" && Promise) === "function" ? _c9 : Object)
6309
6454
  ], OAuthAccountService.prototype, "resolveForLogin", null);
6310
- __decorate36([
6455
+ __decorate37([
6311
6456
  Transaction5(),
6312
- __metadata36("design:type", Function),
6313
- __metadata36("design:paramtypes", [String, Object]),
6314
- __metadata36("design:returntype", typeof (_d7 = typeof Promise !== "undefined" && Promise) === "function" ? _d7 : Object)
6457
+ __metadata37("design:type", Function),
6458
+ __metadata37("design:paramtypes", [String, Object]),
6459
+ __metadata37("design:returntype", typeof (_d7 = typeof Promise !== "undefined" && Promise) === "function" ? _d7 : Object)
6315
6460
  ], OAuthAccountService.prototype, "linkUser", null);
6316
- OAuthAccountService = __decorate36([
6317
- Injectable19(),
6318
- __metadata36("design:paramtypes", [typeof (_a22 = typeof OAuthAccountRepository !== "undefined" && OAuthAccountRepository) === "function" ? _a22 : Object, typeof (_b12 = typeof UserService !== "undefined" && UserService) === "function" ? _b12 : Object])
6461
+ OAuthAccountService = __decorate37([
6462
+ Injectable20(),
6463
+ __metadata37("design:paramtypes", [typeof (_a22 = typeof OAuthAccountRepository !== "undefined" && OAuthAccountRepository) === "function" ? _a22 : Object, typeof (_b12 = typeof UserService !== "undefined" && UserService) === "function" ? _b12 : Object])
6319
6464
  ], OAuthAccountService);
6320
6465
 
6321
- // src/oauth/OAuthController.ts
6322
- import { createHash as createHash5 } from "crypto";
6323
- import { Controller as Controller6, Ctx as Ctx2, Get as Get5, Post as Post6, Query as Query2, User as User5 } from "najm-core";
6324
- import { RateLimit as RateLimit3 } from "najm-rate";
6325
-
6326
- // src/oauth/OAuthService.ts
6327
- import { Inject as Inject21, Injectable as Injectable21, Log as Log2 } from "najm-core";
6328
-
6329
6466
  // src/oauth/OAuthStateService.ts
6330
6467
  import { createHash as createHash4, randomBytes as randomBytes4, timingSafeEqual } from "crypto";
6331
- import { Injectable as Injectable20 } from "najm-core";
6468
+ import { Injectable as Injectable21 } from "najm-core";
6332
6469
  import { CookieService as CookieService3 } from "najm-cookies";
6333
- var __decorate37 = function(decorators, target, key, desc) {
6470
+ var __decorate38 = function(decorators, target, key, desc) {
6334
6471
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
6335
6472
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
6336
6473
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6337
6474
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6338
6475
  };
6339
- var __metadata37 = function(k, v) {
6476
+ var __metadata38 = function(k, v) {
6340
6477
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
6341
6478
  };
6342
6479
  var _a23;
6343
6480
  var _b13;
6344
6481
  var ATTEMPT_TTL_MS = 10 * 60 * 1e3;
6345
- var COOKIE_PREFIX = "najm.oauth.google.";
6482
+ var COOKIE_PREFIX = "najm.oauth.";
6346
6483
  var OAuthStateService = class OAuthStateService2 {
6347
6484
  static {
6348
6485
  __name(this, "OAuthStateService");
@@ -6357,7 +6494,7 @@ var OAuthStateService = class OAuthStateService2 {
6357
6494
  const state = randomBytes4(32).toString("base64url");
6358
6495
  const codeVerifier = randomBytes4(48).toString("base64url");
6359
6496
  const attempt = {
6360
- provider: "google",
6497
+ provider: input.provider,
6361
6498
  intent: input.intent,
6362
6499
  state,
6363
6500
  nonce: randomBytes4(32).toString("base64url"),
@@ -6367,7 +6504,7 @@ var OAuthStateService = class OAuthStateService2 {
6367
6504
  sessionVersion: input.sessionVersion,
6368
6505
  createdAt: Date.now()
6369
6506
  };
6370
- this.cookies.set(this.cookieName(state), this.encryption.encrypt(JSON.stringify(attempt)), {
6507
+ this.cookies.set(this.cookieName(input.provider, state), this.encryption.encrypt(JSON.stringify(attempt)), {
6371
6508
  httpOnly: true,
6372
6509
  sameSite: "Lax",
6373
6510
  path: "/",
@@ -6378,10 +6515,10 @@ var OAuthStateService = class OAuthStateService2 {
6378
6515
  codeChallenge: createHash4("sha256").update(codeVerifier).digest("base64url")
6379
6516
  };
6380
6517
  }
6381
- consume(state) {
6518
+ consume(provider, state) {
6382
6519
  if (!this.isSafeState(state))
6383
6520
  throw new OAuthFlowError("oauth_state_invalid");
6384
- const name = this.cookieName(state);
6521
+ const name = this.cookieName(provider, state);
6385
6522
  const encrypted = this.cookies.get(name);
6386
6523
  this.cookies.delete(name, { path: "/" });
6387
6524
  if (!encrypted)
@@ -6393,7 +6530,7 @@ var OAuthStateService = class OAuthStateService2 {
6393
6530
  if (expected.length !== actual.length || !timingSafeEqual(expected, actual)) {
6394
6531
  throw new OAuthFlowError("oauth_state_invalid");
6395
6532
  }
6396
- if (attempt.provider !== "google" || Date.now() - attempt.createdAt > ATTEMPT_TTL_MS) {
6533
+ if (attempt.provider !== provider || Date.now() - attempt.createdAt > ATTEMPT_TTL_MS) {
6397
6534
  throw new OAuthFlowError("oauth_state_invalid");
6398
6535
  }
6399
6536
  attempt.returnTo = this.validateReturnTo(attempt.returnTo);
@@ -6422,26 +6559,26 @@ var OAuthStateService = class OAuthStateService2 {
6422
6559
  throw new OAuthFlowError("oauth_redirect_invalid");
6423
6560
  }
6424
6561
  }
6425
- cookieName(state) {
6426
- return `${COOKIE_PREFIX}${state}`;
6562
+ cookieName(provider, state) {
6563
+ return `${COOKIE_PREFIX}${provider}.${state}`;
6427
6564
  }
6428
6565
  isSafeState(state) {
6429
6566
  return /^[A-Za-z0-9_-]{40,128}$/.test(state);
6430
6567
  }
6431
6568
  };
6432
- OAuthStateService = __decorate37([
6433
- Injectable20(),
6434
- __metadata37("design:paramtypes", [typeof (_a23 = typeof CookieService3 !== "undefined" && CookieService3) === "function" ? _a23 : Object, typeof (_b13 = typeof EncryptionService !== "undefined" && EncryptionService) === "function" ? _b13 : Object])
6569
+ OAuthStateService = __decorate38([
6570
+ Injectable21(),
6571
+ __metadata38("design:paramtypes", [typeof (_a23 = typeof CookieService3 !== "undefined" && CookieService3) === "function" ? _a23 : Object, typeof (_b13 = typeof EncryptionService !== "undefined" && EncryptionService) === "function" ? _b13 : Object])
6435
6572
  ], OAuthStateService);
6436
6573
 
6437
6574
  // src/oauth/OAuthService.ts
6438
- var __decorate38 = function(decorators, target, key, desc) {
6575
+ var __decorate39 = function(decorators, target, key, desc) {
6439
6576
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
6440
6577
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
6441
6578
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6442
6579
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6443
6580
  };
6444
- var __metadata38 = function(k, v) {
6581
+ var __metadata39 = function(k, v) {
6445
6582
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
6446
6583
  };
6447
6584
  var _a24;
@@ -6451,12 +6588,14 @@ var _d8;
6451
6588
  var _e5;
6452
6589
  var _f5;
6453
6590
  var _g4;
6591
+ var _h3;
6454
6592
  var OAuthService = class OAuthService2 {
6455
6593
  static {
6456
6594
  __name(this, "OAuthService");
6457
6595
  }
6458
6596
  state;
6459
6597
  google;
6598
+ github;
6460
6599
  accounts;
6461
6600
  sessions;
6462
6601
  tokens;
@@ -6464,9 +6603,10 @@ var OAuthService = class OAuthService2 {
6464
6603
  requirements;
6465
6604
  config;
6466
6605
  logger;
6467
- constructor(state, google, accounts, sessions, tokens, users, requirements) {
6606
+ constructor(state, google, github, accounts, sessions, tokens, users, requirements) {
6468
6607
  this.state = state;
6469
6608
  this.google = google;
6609
+ this.github = github;
6470
6610
  this.accounts = accounts;
6471
6611
  this.sessions = sessions;
6472
6612
  this.tokens = tokens;
@@ -6474,36 +6614,59 @@ var OAuthService = class OAuthService2 {
6474
6614
  this.requirements = requirements;
6475
6615
  }
6476
6616
  startGoogleLogin(returnTo) {
6477
- this.googleConfig();
6478
- const { attempt, codeChallenge } = this.state.create({ intent: "login", returnTo });
6479
- return this.google.authorizationUrl(attempt, codeChallenge);
6617
+ return this.startLogin("google", returnTo);
6618
+ }
6619
+ startGitHubLogin(returnTo) {
6620
+ return this.startLogin("github", returnTo);
6621
+ }
6622
+ startGoogleLink(userId, returnTo) {
6623
+ return this.startLink("google", userId, returnTo);
6624
+ }
6625
+ startGitHubLink(userId, returnTo) {
6626
+ return this.startLink("github", userId, returnTo);
6480
6627
  }
6481
- async startGoogleLink(userId, returnTo) {
6482
- this.googleConfig();
6628
+ finishGoogleCallback(params) {
6629
+ return this.finishCallback("google", params);
6630
+ }
6631
+ finishGitHubCallback(params) {
6632
+ return this.finishCallback("github", params);
6633
+ }
6634
+ startLogin(provider, returnTo) {
6635
+ this.providerConfig(provider);
6636
+ const { attempt, codeChallenge } = this.state.create({
6637
+ provider,
6638
+ intent: "login",
6639
+ returnTo
6640
+ });
6641
+ return this.provider(provider).authorizationUrl(attempt, codeChallenge);
6642
+ }
6643
+ async startLink(provider, userId, returnTo) {
6644
+ this.providerConfig(provider);
6483
6645
  const user = await this.users.getById(userId);
6484
6646
  if (user.status !== "active")
6485
6647
  throw new OAuthFlowError("oauth_account_inactive", 403);
6486
6648
  const sessionVersion = await this.tokens.getSessionVersion(userId);
6487
6649
  const { attempt, codeChallenge } = this.state.create({
6650
+ provider,
6488
6651
  intent: "link",
6489
6652
  returnTo,
6490
6653
  userId,
6491
6654
  sessionVersion
6492
6655
  });
6493
- return { authorizationUrl: this.google.authorizationUrl(attempt, codeChallenge) };
6656
+ return { authorizationUrl: this.provider(provider).authorizationUrl(attempt, codeChallenge) };
6494
6657
  }
6495
- async finishGoogleCallback(params) {
6658
+ async finishCallback(provider, params) {
6496
6659
  try {
6497
- this.googleConfig();
6660
+ this.providerConfig(provider);
6498
6661
  if (!params.state)
6499
6662
  throw new OAuthFlowError("oauth_state_invalid");
6500
- const attempt = this.state.consume(params.state);
6663
+ const attempt = this.state.consume(provider, params.state);
6501
6664
  if (params.error) {
6502
6665
  throw new OAuthFlowError(params.error === "access_denied" ? "oauth_access_denied" : "oauth_provider_error");
6503
6666
  }
6504
6667
  if (!params.code)
6505
6668
  throw new OAuthFlowError("oauth_provider_error");
6506
- const identity = await this.google.exchange(params.code, attempt);
6669
+ const identity = await this.provider(provider).exchange(params.code, attempt);
6507
6670
  if (attempt.intent === "link") {
6508
6671
  if (!attempt.userId || attempt.sessionVersion === void 0) {
6509
6672
  throw new OAuthFlowError("oauth_state_invalid");
@@ -6520,25 +6683,26 @@ var OAuthService = class OAuthService2 {
6520
6683
  }
6521
6684
  await this.sessions.establish(user);
6522
6685
  }
6523
- return this.frontendSuccessUrl(attempt.returnTo, attempt.intent);
6686
+ return this.frontendSuccessUrl(provider, attempt.returnTo, attempt.intent);
6524
6687
  } catch (error) {
6525
6688
  const code = this.publicErrorCode(error);
6526
- this.logger.warn("Google OAuth callback failed", { provider: "google", code });
6527
- return this.frontendErrorUrl(code);
6689
+ this.logger.warn("OAuth callback failed", { provider, code });
6690
+ return this.frontendErrorUrl(provider, code);
6528
6691
  }
6529
6692
  }
6530
- frontendSuccessUrl(returnTo, mode) {
6531
- const google = this.googleConfig();
6532
- const url = new URL(google.frontendCallbackPath, this.config.frontendUrl);
6533
- url.searchParams.set("provider", "google");
6693
+ frontendSuccessUrl(provider, returnTo, mode) {
6694
+ const providerConfig = this.providerConfig(provider);
6695
+ const url = new URL(providerConfig.frontendCallbackPath, this.config.frontendUrl);
6696
+ url.searchParams.set("provider", provider);
6534
6697
  url.searchParams.set("mode", mode);
6535
6698
  url.searchParams.set("returnTo", this.state.validateReturnTo(returnTo));
6536
6699
  return url.toString();
6537
6700
  }
6538
- frontendErrorUrl(code) {
6539
- const path2 = this.config.oauth?.google?.errorRedirectPath ?? "/login";
6701
+ frontendErrorUrl(provider, code) {
6702
+ const path2 = this.config.oauth?.[provider]?.errorRedirectPath ?? "/login";
6540
6703
  const url = new URL(path2, this.config.frontendUrl);
6541
6704
  url.searchParams.set("oauthError", code);
6705
+ url.searchParams.set("provider", provider);
6542
6706
  return url.toString();
6543
6707
  }
6544
6708
  publicErrorCode(error) {
@@ -6548,34 +6712,37 @@ var OAuthService = class OAuthService2 {
6548
6712
  return error.message;
6549
6713
  return "oauth_provider_error";
6550
6714
  }
6551
- googleConfig() {
6552
- const google = this.config.oauth?.google;
6553
- if (!google)
6715
+ provider(provider) {
6716
+ return provider === "google" ? this.google : this.github;
6717
+ }
6718
+ providerConfig(provider) {
6719
+ const providerConfig = this.config.oauth?.[provider];
6720
+ if (!providerConfig)
6554
6721
  throw new OAuthFlowError("oauth_provider_disabled", 404);
6555
- return google;
6722
+ return providerConfig;
6556
6723
  }
6557
6724
  };
6558
- __decorate38([
6559
- Inject21(AUTH_CONFIG),
6560
- __metadata38("design:type", Object)
6725
+ __decorate39([
6726
+ Inject22(AUTH_CONFIG),
6727
+ __metadata39("design:type", Object)
6561
6728
  ], OAuthService.prototype, "config", void 0);
6562
- __decorate38([
6729
+ __decorate39([
6563
6730
  Log2(),
6564
- __metadata38("design:type", Object)
6731
+ __metadata39("design:type", Object)
6565
6732
  ], OAuthService.prototype, "logger", void 0);
6566
- OAuthService = __decorate38([
6567
- Injectable21(),
6568
- __metadata38("design:paramtypes", [typeof (_a24 = typeof OAuthStateService !== "undefined" && OAuthStateService) === "function" ? _a24 : Object, typeof (_b14 = typeof GoogleOAuthProvider !== "undefined" && GoogleOAuthProvider) === "function" ? _b14 : Object, typeof (_c10 = typeof OAuthAccountService !== "undefined" && OAuthAccountService) === "function" ? _c10 : Object, typeof (_d8 = typeof AuthSessionService !== "undefined" && AuthSessionService) === "function" ? _d8 : Object, typeof (_e5 = typeof TokenService !== "undefined" && TokenService) === "function" ? _e5 : Object, typeof (_f5 = typeof UserService !== "undefined" && UserService) === "function" ? _f5 : Object, typeof (_g4 = typeof CredentialSetupRequirementService !== "undefined" && CredentialSetupRequirementService) === "function" ? _g4 : Object])
6733
+ OAuthService = __decorate39([
6734
+ Injectable22(),
6735
+ __metadata39("design:paramtypes", [typeof (_a24 = typeof OAuthStateService !== "undefined" && OAuthStateService) === "function" ? _a24 : Object, typeof (_b14 = typeof GoogleOAuthProvider !== "undefined" && GoogleOAuthProvider) === "function" ? _b14 : Object, typeof (_c10 = typeof GitHubOAuthProvider !== "undefined" && GitHubOAuthProvider) === "function" ? _c10 : Object, typeof (_d8 = typeof OAuthAccountService !== "undefined" && OAuthAccountService) === "function" ? _d8 : Object, typeof (_e5 = typeof AuthSessionService !== "undefined" && AuthSessionService) === "function" ? _e5 : Object, typeof (_f5 = typeof TokenService !== "undefined" && TokenService) === "function" ? _f5 : Object, typeof (_g4 = typeof UserService !== "undefined" && UserService) === "function" ? _g4 : Object, typeof (_h3 = typeof CredentialSetupRequirementService !== "undefined" && CredentialSetupRequirementService) === "function" ? _h3 : Object])
6569
6736
  ], OAuthService);
6570
6737
 
6571
- // src/oauth/OAuthController.ts
6572
- var __decorate39 = function(decorators, target, key, desc) {
6738
+ // src/oauth/GitHubOAuthController.ts
6739
+ var __decorate40 = function(decorators, target, key, desc) {
6573
6740
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
6574
6741
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
6575
6742
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6576
6743
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6577
6744
  };
6578
- var __metadata39 = function(k, v) {
6745
+ var __metadata40 = function(k, v) {
6579
6746
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
6580
6747
  };
6581
6748
  var __param13 = function(paramIndex, decorator) {
@@ -6585,63 +6752,141 @@ var __param13 = function(paramIndex, decorator) {
6585
6752
  };
6586
6753
  var _a25;
6587
6754
  var callbackKey = /* @__PURE__ */ __name((ctx, { clientIp }) => {
6588
- const ip = clientIp;
6589
6755
  const state = ctx.req.query("state") ?? "none";
6590
6756
  const fingerprint = createHash5("sha256").update(state).digest("base64url").slice(0, 24);
6591
- return `${ip}:${fingerprint}`;
6757
+ return `${clientIp}:${fingerprint}`;
6592
6758
  }, "callbackKey");
6593
- var OAuthController = class OAuthController2 {
6759
+ var GitHubOAuthController = class GitHubOAuthController2 {
6594
6760
  static {
6595
- __name(this, "OAuthController");
6761
+ __name(this, "GitHubOAuthController");
6596
6762
  }
6597
6763
  oauth;
6598
6764
  constructor(oauth) {
6599
6765
  this.oauth = oauth;
6600
6766
  }
6601
6767
  start(ctx, returnTo) {
6602
- return ctx.redirect(this.oauth.startGoogleLogin(returnTo), 302);
6768
+ return ctx.redirect(this.oauth.startGitHubLogin(returnTo), 302);
6603
6769
  }
6604
6770
  async callback(ctx, code, state, error) {
6605
- const redirect = await this.oauth.finishGoogleCallback({ code, state, error });
6771
+ const redirect = await this.oauth.finishGitHubCallback({ code, state, error });
6606
6772
  return ctx.redirect(redirect, 302);
6607
6773
  }
6608
6774
  link(userId, returnTo) {
6609
- return this.oauth.startGoogleLink(userId, returnTo);
6775
+ return this.oauth.startGitHubLink(userId, returnTo);
6610
6776
  }
6611
6777
  };
6612
- __decorate39([
6778
+ __decorate40([
6613
6779
  Get5("/start"),
6614
6780
  RateLimit3({ limit: 20, window: "15m", key: "ip" }),
6615
6781
  __param13(0, Ctx2()),
6616
6782
  __param13(1, Query2("returnTo")),
6617
- __metadata39("design:type", Function),
6618
- __metadata39("design:paramtypes", [Object, String]),
6619
- __metadata39("design:returntype", void 0)
6620
- ], OAuthController.prototype, "start", null);
6621
- __decorate39([
6783
+ __metadata40("design:type", Function),
6784
+ __metadata40("design:paramtypes", [Object, String]),
6785
+ __metadata40("design:returntype", void 0)
6786
+ ], GitHubOAuthController.prototype, "start", null);
6787
+ __decorate40([
6622
6788
  Get5("/callback"),
6623
6789
  RateLimit3({ limit: 20, window: "15m", key: callbackKey }),
6624
6790
  __param13(0, Ctx2()),
6625
6791
  __param13(1, Query2("code")),
6626
6792
  __param13(2, Query2("state")),
6627
6793
  __param13(3, Query2("error")),
6628
- __metadata39("design:type", Function),
6629
- __metadata39("design:paramtypes", [Object, String, String, String]),
6630
- __metadata39("design:returntype", Promise)
6631
- ], OAuthController.prototype, "callback", null);
6632
- __decorate39([
6794
+ __metadata40("design:type", Function),
6795
+ __metadata40("design:paramtypes", [Object, String, String, String]),
6796
+ __metadata40("design:returntype", Promise)
6797
+ ], GitHubOAuthController.prototype, "callback", null);
6798
+ __decorate40([
6633
6799
  Post6("/link"),
6634
6800
  isAuth(),
6635
6801
  RateLimit3({ limit: 10, window: "15m", key: "user" }),
6636
6802
  __param13(0, User5("id")),
6637
6803
  __param13(1, Query2("returnTo")),
6638
- __metadata39("design:type", Function),
6639
- __metadata39("design:paramtypes", [String, String]),
6640
- __metadata39("design:returntype", void 0)
6804
+ __metadata40("design:type", Function),
6805
+ __metadata40("design:paramtypes", [String, String]),
6806
+ __metadata40("design:returntype", void 0)
6807
+ ], GitHubOAuthController.prototype, "link", null);
6808
+ GitHubOAuthController = __decorate40([
6809
+ Controller6("/auth/oauth/github"),
6810
+ __metadata40("design:paramtypes", [typeof (_a25 = typeof OAuthService !== "undefined" && OAuthService) === "function" ? _a25 : Object])
6811
+ ], GitHubOAuthController);
6812
+
6813
+ // src/oauth/OAuthController.ts
6814
+ import { createHash as createHash6 } from "crypto";
6815
+ import { Controller as Controller7, Ctx as Ctx3, Get as Get6, Post as Post7, Query as Query3, User as User6 } from "najm-core";
6816
+ import { RateLimit as RateLimit4 } from "najm-rate";
6817
+ var __decorate41 = function(decorators, target, key, desc) {
6818
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
6819
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
6820
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6821
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
6822
+ };
6823
+ var __metadata41 = function(k, v) {
6824
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
6825
+ };
6826
+ var __param14 = function(paramIndex, decorator) {
6827
+ return function(target, key) {
6828
+ decorator(target, key, paramIndex);
6829
+ };
6830
+ };
6831
+ var _a26;
6832
+ var callbackKey2 = /* @__PURE__ */ __name((ctx, { clientIp }) => {
6833
+ const ip = clientIp;
6834
+ const state = ctx.req.query("state") ?? "none";
6835
+ const fingerprint = createHash6("sha256").update(state).digest("base64url").slice(0, 24);
6836
+ return `${ip}:${fingerprint}`;
6837
+ }, "callbackKey");
6838
+ var OAuthController = class OAuthController2 {
6839
+ static {
6840
+ __name(this, "OAuthController");
6841
+ }
6842
+ oauth;
6843
+ constructor(oauth) {
6844
+ this.oauth = oauth;
6845
+ }
6846
+ start(ctx, returnTo) {
6847
+ return ctx.redirect(this.oauth.startGoogleLogin(returnTo), 302);
6848
+ }
6849
+ async callback(ctx, code, state, error) {
6850
+ const redirect = await this.oauth.finishGoogleCallback({ code, state, error });
6851
+ return ctx.redirect(redirect, 302);
6852
+ }
6853
+ link(userId, returnTo) {
6854
+ return this.oauth.startGoogleLink(userId, returnTo);
6855
+ }
6856
+ };
6857
+ __decorate41([
6858
+ Get6("/start"),
6859
+ RateLimit4({ limit: 20, window: "15m", key: "ip" }),
6860
+ __param14(0, Ctx3()),
6861
+ __param14(1, Query3("returnTo")),
6862
+ __metadata41("design:type", Function),
6863
+ __metadata41("design:paramtypes", [Object, String]),
6864
+ __metadata41("design:returntype", void 0)
6865
+ ], OAuthController.prototype, "start", null);
6866
+ __decorate41([
6867
+ Get6("/callback"),
6868
+ RateLimit4({ limit: 20, window: "15m", key: callbackKey2 }),
6869
+ __param14(0, Ctx3()),
6870
+ __param14(1, Query3("code")),
6871
+ __param14(2, Query3("state")),
6872
+ __param14(3, Query3("error")),
6873
+ __metadata41("design:type", Function),
6874
+ __metadata41("design:paramtypes", [Object, String, String, String]),
6875
+ __metadata41("design:returntype", Promise)
6876
+ ], OAuthController.prototype, "callback", null);
6877
+ __decorate41([
6878
+ Post7("/link"),
6879
+ isAuth(),
6880
+ RateLimit4({ limit: 10, window: "15m", key: "user" }),
6881
+ __param14(0, User6("id")),
6882
+ __param14(1, Query3("returnTo")),
6883
+ __metadata41("design:type", Function),
6884
+ __metadata41("design:paramtypes", [String, String]),
6885
+ __metadata41("design:returntype", void 0)
6641
6886
  ], OAuthController.prototype, "link", null);
6642
- OAuthController = __decorate39([
6643
- Controller6("/auth/oauth/google"),
6644
- __metadata39("design:paramtypes", [typeof (_a25 = typeof OAuthService !== "undefined" && OAuthService) === "function" ? _a25 : Object])
6887
+ OAuthController = __decorate41([
6888
+ Controller7("/auth/oauth/google"),
6889
+ __metadata41("design:paramtypes", [typeof (_a26 = typeof OAuthService !== "undefined" && OAuthService) === "function" ? _a26 : Object])
6645
6890
  ], OAuthController);
6646
6891
 
6647
6892
  // src/oauth/index.ts
@@ -6651,13 +6896,15 @@ var OAUTH_MODULE = [
6651
6896
  OAuthStateService,
6652
6897
  GoogleTokenVerifier,
6653
6898
  GoogleOAuthProvider,
6899
+ GitHubOAuthProvider,
6654
6900
  OAuthService,
6655
- OAuthController
6901
+ OAuthController,
6902
+ GitHubOAuthController
6656
6903
  ];
6657
6904
 
6658
6905
  // src/credentialSetup/CredentialSetupController.ts
6659
- import { Body as Body7, Controller as Controller7, Get as Get6, Post as Post7, ResMsg as ResMsg6 } from "najm-core";
6660
- import { RateLimit as RateLimit4 } from "najm-rate";
6906
+ import { Body as Body7, Controller as Controller8, Get as Get7, Post as Post8, ResMsg as ResMsg6 } from "najm-core";
6907
+ import { RateLimit as RateLimit5 } from "najm-rate";
6661
6908
  import { Validate as Validate6 } from "najm-validation";
6662
6909
 
6663
6910
  // src/credentialSetup/CredentialSetupDto.ts
@@ -6668,21 +6915,21 @@ var credentialSetupChangeDto = z5.object({
6668
6915
  });
6669
6916
 
6670
6917
  // src/credentialSetup/CredentialSetupController.ts
6671
- var __decorate40 = function(decorators, target, key, desc) {
6918
+ var __decorate42 = function(decorators, target, key, desc) {
6672
6919
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
6673
6920
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
6674
6921
  else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
6675
6922
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6676
6923
  };
6677
- var __metadata40 = function(k, v) {
6924
+ var __metadata42 = function(k, v) {
6678
6925
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
6679
6926
  };
6680
- var __param14 = function(paramIndex, decorator) {
6927
+ var __param15 = function(paramIndex, decorator) {
6681
6928
  return function(target, key) {
6682
6929
  decorator(target, key, paramIndex);
6683
6930
  };
6684
6931
  };
6685
- var _a26;
6932
+ var _a27;
6686
6933
  var CredentialSetupController = class CredentialSetupController2 {
6687
6934
  static {
6688
6935
  __name(this, "CredentialSetupController");
@@ -6701,35 +6948,35 @@ var CredentialSetupController = class CredentialSetupController2 {
6701
6948
  return this.passwords.cancel();
6702
6949
  }
6703
6950
  };
6704
- __decorate40([
6705
- Get6("/setup"),
6706
- RateLimit4({ limit: 30, window: "15m", key: "ip" }),
6951
+ __decorate42([
6952
+ Get7("/setup"),
6953
+ RateLimit5({ limit: 30, window: "15m", key: "ip" }),
6707
6954
  ResMsg6("auth.success.credentialSetupPending"),
6708
- __metadata40("design:type", Function),
6709
- __metadata40("design:paramtypes", []),
6710
- __metadata40("design:returntype", void 0)
6955
+ __metadata42("design:type", Function),
6956
+ __metadata42("design:paramtypes", []),
6957
+ __metadata42("design:returntype", void 0)
6711
6958
  ], CredentialSetupController.prototype, "status", null);
6712
- __decorate40([
6713
- Post7("/change"),
6714
- RateLimit4({ limit: 5, window: "15m", key: "ip" }),
6959
+ __decorate42([
6960
+ Post8("/change"),
6961
+ RateLimit5({ limit: 5, window: "15m", key: "ip" }),
6715
6962
  Validate6(credentialSetupChangeDto),
6716
6963
  ResMsg6("auth.success.credentialSetupPasswordReplaced"),
6717
- __param14(0, Body7()),
6718
- __metadata40("design:type", Function),
6719
- __metadata40("design:paramtypes", [Object]),
6720
- __metadata40("design:returntype", void 0)
6964
+ __param15(0, Body7()),
6965
+ __metadata42("design:type", Function),
6966
+ __metadata42("design:paramtypes", [Object]),
6967
+ __metadata42("design:returntype", void 0)
6721
6968
  ], CredentialSetupController.prototype, "change", null);
6722
- __decorate40([
6723
- Post7("/cancel"),
6724
- RateLimit4({ limit: 10, window: "15m", key: "ip" }),
6969
+ __decorate42([
6970
+ Post8("/cancel"),
6971
+ RateLimit5({ limit: 10, window: "15m", key: "ip" }),
6725
6972
  ResMsg6("auth.success.credentialSetupCancelled"),
6726
- __metadata40("design:type", Function),
6727
- __metadata40("design:paramtypes", []),
6728
- __metadata40("design:returntype", void 0)
6973
+ __metadata42("design:type", Function),
6974
+ __metadata42("design:paramtypes", []),
6975
+ __metadata42("design:returntype", void 0)
6729
6976
  ], CredentialSetupController.prototype, "cancel", null);
6730
- CredentialSetupController = __decorate40([
6731
- Controller7("/auth/credential-setup"),
6732
- __metadata40("design:paramtypes", [typeof (_a26 = typeof PasswordSetupService !== "undefined" && PasswordSetupService) === "function" ? _a26 : Object])
6977
+ CredentialSetupController = __decorate42([
6978
+ Controller8("/auth/credential-setup"),
6979
+ __metadata42("design:paramtypes", [typeof (_a27 = typeof PasswordSetupService !== "undefined" && PasswordSetupService) === "function" ? _a27 : Object])
6733
6980
  ], CredentialSetupController);
6734
6981
 
6735
6982
  // src/credentialSetup/index.ts
@@ -6755,6 +7002,19 @@ var validateFrontendPath = /* @__PURE__ */ __name((value, name) => {
6755
7002
  }
6756
7003
  return value;
6757
7004
  }, "validateFrontendPath");
7005
+ var validateCallbackUrl = /* @__PURE__ */ __name((value, name) => {
7006
+ let callback;
7007
+ try {
7008
+ callback = new URL(value);
7009
+ } catch {
7010
+ throw new Error(`${name} must be an absolute URL`);
7011
+ }
7012
+ const local = callback.hostname === "localhost" || callback.hostname === "127.0.0.1" || callback.hostname === "[::1]" || callback.hostname === "::1";
7013
+ if (callback.protocol !== "https:" && !(local && callback.protocol === "http:")) {
7014
+ throw new Error(`${name} must use HTTPS (HTTP is allowed only for localhost)`);
7015
+ }
7016
+ return callback.toString();
7017
+ }, "validateCallbackUrl");
6758
7018
  var resolveGoogleConfig = /* @__PURE__ */ __name((config) => {
6759
7019
  const configuredGoogle = config?.oauth?.google;
6760
7020
  if (!configuredGoogle)
@@ -6768,20 +7028,10 @@ var resolveGoogleConfig = /* @__PURE__ */ __name((config) => {
6768
7028
  throw Err15.configRequired("auth.oauth.google", "GOOGLE_CLIENT_SECRET");
6769
7029
  const frontendUrl = config?.frontendUrl ?? process.env.FRONTEND_URL ?? "http://localhost:3000";
6770
7030
  const callbackUrl = google.callbackUrl ?? process.env.GOOGLE_CALLBACK_URL ?? `${frontendUrl.replace(/\/$/, "")}/api/auth/oauth/google/callback`;
6771
- let callback;
6772
- try {
6773
- callback = new URL(callbackUrl);
6774
- } catch {
6775
- throw new Error("auth.oauth.google.callbackUrl must be an absolute URL");
6776
- }
6777
- const local = callback.hostname === "localhost" || callback.hostname === "127.0.0.1" || callback.hostname === "[::1]" || callback.hostname === "::1";
6778
- if (callback.protocol !== "https:" && !(local && callback.protocol === "http:")) {
6779
- throw new Error("auth.oauth.google.callbackUrl must use HTTPS (HTTP is allowed only for localhost)");
6780
- }
6781
7031
  return {
6782
7032
  clientId,
6783
7033
  clientSecret,
6784
- callbackUrl: callback.toString(),
7034
+ callbackUrl: validateCallbackUrl(callbackUrl, "auth.oauth.google.callbackUrl"),
6785
7035
  frontendCallbackPath: validateFrontendPath(google.frontendCallbackPath ?? "/auth/oauth/callback", "auth.oauth.google.frontendCallbackPath"),
6786
7036
  errorRedirectPath: validateFrontendPath(google.errorRedirectPath ?? "/login", "auth.oauth.google.errorRedirectPath"),
6787
7037
  allowSignup: google.allowSignup ?? true,
@@ -6789,6 +7039,29 @@ var resolveGoogleConfig = /* @__PURE__ */ __name((config) => {
6789
7039
  allowedHostedDomains: [...new Set((google.allowedHostedDomains ?? []).map((domain) => domain.trim().toLowerCase()).filter(Boolean))]
6790
7040
  };
6791
7041
  }, "resolveGoogleConfig");
7042
+ var resolveGitHubConfig = /* @__PURE__ */ __name((config) => {
7043
+ const configuredGitHub = config?.oauth?.github;
7044
+ if (!configuredGitHub)
7045
+ return void 0;
7046
+ const github = configuredGitHub === true ? {} : configuredGitHub;
7047
+ const clientId = github.clientId ?? process.env.GITHUB_CLIENT_ID ?? "";
7048
+ const clientSecret = github.clientSecret ?? process.env.GITHUB_CLIENT_SECRET ?? "";
7049
+ if (!clientId)
7050
+ throw Err15.configRequired("auth.oauth.github", "GITHUB_CLIENT_ID");
7051
+ if (!clientSecret)
7052
+ throw Err15.configRequired("auth.oauth.github", "GITHUB_CLIENT_SECRET");
7053
+ const frontendUrl = config?.frontendUrl ?? process.env.FRONTEND_URL ?? "http://localhost:3000";
7054
+ const callbackUrl = github.callbackUrl ?? process.env.GITHUB_CALLBACK_URL ?? `${frontendUrl.replace(/\/$/, "")}/api/auth/oauth/github/callback`;
7055
+ return {
7056
+ clientId,
7057
+ clientSecret,
7058
+ callbackUrl: validateCallbackUrl(callbackUrl, "auth.oauth.github.callbackUrl"),
7059
+ frontendCallbackPath: validateFrontendPath(github.frontendCallbackPath ?? "/auth/oauth/callback", "auth.oauth.github.frontendCallbackPath"),
7060
+ errorRedirectPath: validateFrontendPath(github.errorRedirectPath ?? "/login", "auth.oauth.github.errorRedirectPath"),
7061
+ allowSignup: github.allowSignup ?? true,
7062
+ autoLinkVerifiedEmail: github.autoLinkVerifiedEmail ?? false
7063
+ };
7064
+ }, "resolveGitHubConfig");
6792
7065
  var resolveCredentialSetupConfig = /* @__PURE__ */ __name((config) => {
6793
7066
  const password = config?.credentialSetup?.password ?? {};
6794
7067
  const ttlMs = password.ttlMs ?? DEFAULT_CREDENTIAL_SETUP_TTL_MS;
@@ -6839,7 +7112,8 @@ var resolveAuthConfig = /* @__PURE__ */ __name((config) => {
6839
7112
  },
6840
7113
  credentialSetup: resolveCredentialSetupConfig(config),
6841
7114
  oauth: {
6842
- google: resolveGoogleConfig(config)
7115
+ google: resolveGoogleConfig(config),
7116
+ github: resolveGitHubConfig(config)
6843
7117
  }
6844
7118
  };
6845
7119
  if (!finalConfig.jwt.accessSecret) {
@@ -6852,8 +7126,8 @@ var resolveAuthConfig = /* @__PURE__ */ __name((config) => {
6852
7126
  }, "resolveAuthConfig");
6853
7127
  var selectAuthSchema = /* @__PURE__ */ __name((config) => {
6854
7128
  if (config?.schema) {
6855
- if (config.oauth?.google && !config.schema.oauthAccounts) {
6856
- throw new Error("auth.schema.oauthAccounts is required when Google OAuth is enabled");
7129
+ if ((config.oauth?.google || config.oauth?.github) && !config.schema.oauthAccounts) {
7130
+ throw new Error("auth.schema.oauthAccounts is required when OAuth is enabled");
6857
7131
  }
6858
7132
  if (!config.schema.credentialSetupSessions) {
6859
7133
  throw new Error("auth.schema.credentialSetupSessions is required \u2014 re-export it from najm-auth/pg or najm-auth/sqlite");