najm-auth 3.3.2 → 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
@@ -5399,7 +5399,7 @@ function toSingular(plural) {
5399
5399
  }
5400
5400
  __name(toSingular, "toSingular");
5401
5401
  function createResourceGuards(ownershipClass, resourceType, resource, options) {
5402
- var _a27, _b15;
5402
+ var _a28, _b15;
5403
5403
  const writeGuard = options?.adminGuard ?? isAdmin;
5404
5404
  let AccessGuard = class AccessGuard {
5405
5405
  static {
@@ -5420,7 +5420,7 @@ function createResourceGuards(ownershipClass, resourceType, resource, options) {
5420
5420
  __param12(1, Params4("id")),
5421
5421
  __metadata31("design:type", Function),
5422
5422
  __metadata31("design:paramtypes", [Object, String]),
5423
- __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)
5424
5424
  ], AccessGuard.prototype, "canActivate", null);
5425
5425
  AccessGuard = __decorate31([
5426
5426
  Injectable15()
@@ -5581,7 +5581,7 @@ function configureOwnership(config) {
5581
5581
  Injectable15()
5582
5582
  ], GeneratedOwnershipService);
5583
5583
  function bodyGuard(resourceType, bodyField, optional = false) {
5584
- var _a27;
5584
+ var _a28;
5585
5585
  let BodyAccessGuard = class BodyAccessGuard {
5586
5586
  static {
5587
5587
  __name(this, "BodyAccessGuard");
@@ -5603,7 +5603,7 @@ function configureOwnership(config) {
5603
5603
  __param12(1, Body6()),
5604
5604
  __metadata31("design:type", Function),
5605
5605
  __metadata31("design:paramtypes", [Object, Object]),
5606
- __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)
5607
5607
  ], BodyAccessGuard.prototype, "canActivate", null);
5608
5608
  BodyAccessGuard = __decorate31([
5609
5609
  Injectable15()
@@ -6158,17 +6158,162 @@ GoogleOAuthProvider = __decorate34([
6158
6158
  __metadata34("design:paramtypes", [typeof (_a21 = typeof GoogleTokenVerifier !== "undefined" && GoogleTokenVerifier) === "function" ? _a21 : Object])
6159
6159
  ], GoogleOAuthProvider);
6160
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
+
6161
6306
  // src/oauth/OAuthAccountRepository.ts
6162
6307
  import { and as and6, eq as eq9 } from "drizzle-orm";
6163
- import { Inject as Inject19, Repository as Repository7 } from "najm-core";
6308
+ import { Inject as Inject20, Repository as Repository7 } from "najm-core";
6164
6309
  import { DB as DB7 } from "najm-database";
6165
- var __decorate35 = function(decorators, target, key, desc) {
6310
+ var __decorate36 = function(decorators, target, key, desc) {
6166
6311
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
6167
6312
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
6168
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;
6169
6314
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6170
6315
  };
6171
- var __metadata35 = function(k, v) {
6316
+ var __metadata36 = function(k, v) {
6172
6317
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
6173
6318
  };
6174
6319
  var OAuthAccountRepository = class OAuthAccountRepository2 {
@@ -6196,29 +6341,26 @@ var OAuthAccountRepository = class OAuthAccountRepository2 {
6196
6341
  return account;
6197
6342
  }
6198
6343
  };
6199
- __decorate35([
6344
+ __decorate36([
6200
6345
  DB7(),
6201
- __metadata35("design:type", Object)
6346
+ __metadata36("design:type", Object)
6202
6347
  ], OAuthAccountRepository.prototype, "db", void 0);
6203
- __decorate35([
6204
- Inject19(AUTH_SCHEMA),
6205
- __metadata35("design:type", Object)
6348
+ __decorate36([
6349
+ Inject20(AUTH_SCHEMA),
6350
+ __metadata36("design:type", Object)
6206
6351
  ], OAuthAccountRepository.prototype, "schema", void 0);
6207
- OAuthAccountRepository = __decorate35([
6352
+ OAuthAccountRepository = __decorate36([
6208
6353
  Repository7()
6209
6354
  ], OAuthAccountRepository);
6210
6355
 
6211
6356
  // src/oauth/OAuthAccountService.ts
6212
- import { randomBytes as randomBytes3 } from "crypto";
6213
- import { Inject as Inject20, Injectable as Injectable19 } from "najm-core";
6214
- import { Transaction as Transaction5 } from "najm-database";
6215
- var __decorate36 = function(decorators, target, key, desc) {
6357
+ var __decorate37 = function(decorators, target, key, desc) {
6216
6358
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
6217
6359
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
6218
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;
6219
6361
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6220
6362
  };
6221
- var __metadata36 = function(k, v) {
6363
+ var __metadata37 = function(k, v) {
6222
6364
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
6223
6365
  };
6224
6366
  var _a22;
@@ -6237,18 +6379,18 @@ var OAuthAccountService = class OAuthAccountService2 {
6237
6379
  this.users = users;
6238
6380
  }
6239
6381
  async resolveForLogin(identity) {
6240
- const linked = await this.accounts.getByProviderAccount("google", identity.providerAccountId);
6382
+ const linked = await this.accounts.getByProviderAccount(identity.provider, identity.providerAccountId);
6241
6383
  if (linked)
6242
6384
  return this.users.getById(linked.userId);
6243
6385
  const existingUser = await this.users.findByEmailInsensitive(identity.email);
6244
6386
  if (existingUser) {
6245
- if (!this.googleConfig().autoLinkVerifiedEmail) {
6387
+ if (!this.providerConfig(identity.provider).autoLinkVerifiedEmail) {
6246
6388
  throw new OAuthFlowError("oauth_account_link_required", 409);
6247
6389
  }
6248
6390
  await this.createLink(existingUser.id, identity);
6249
6391
  return this.users.getById(existingUser.id);
6250
6392
  }
6251
- if (!this.googleConfig().allowSignup) {
6393
+ if (!this.providerConfig(identity.provider).allowSignup) {
6252
6394
  throw new OAuthFlowError("oauth_signup_disabled", 403);
6253
6395
  }
6254
6396
  const password = `${randomBytes3(32).toString("base64url")}Aa1`;
@@ -6266,13 +6408,13 @@ var OAuthAccountService = class OAuthAccountService2 {
6266
6408
  const user = await this.users.getById(userId);
6267
6409
  if (user.status !== "active")
6268
6410
  throw new OAuthFlowError("oauth_account_inactive", 403);
6269
- const providerAccount = await this.accounts.getByProviderAccount("google", identity.providerAccountId);
6411
+ const providerAccount = await this.accounts.getByProviderAccount(identity.provider, identity.providerAccountId);
6270
6412
  if (providerAccount && providerAccount.userId !== userId) {
6271
6413
  throw new OAuthFlowError("oauth_provider_account_linked", 409);
6272
6414
  }
6273
6415
  if (providerAccount)
6274
6416
  return user;
6275
- const userProvider = await this.accounts.getByUserProvider(userId, "google");
6417
+ const userProvider = await this.accounts.getByUserProvider(userId, identity.provider);
6276
6418
  if (userProvider && userProvider.providerAccountId !== identity.providerAccountId) {
6277
6419
  throw new OAuthFlowError("oauth_user_provider_linked", 409);
6278
6420
  }
@@ -6283,69 +6425,61 @@ var OAuthAccountService = class OAuthAccountService2 {
6283
6425
  async createLink(userId, identity) {
6284
6426
  const created = await this.accounts.create({
6285
6427
  userId,
6286
- provider: "google",
6428
+ provider: identity.provider,
6287
6429
  providerAccountId: identity.providerAccountId
6288
6430
  });
6289
6431
  if (created)
6290
6432
  return;
6291
- const linked = await this.accounts.getByProviderAccount("google", identity.providerAccountId);
6433
+ const linked = await this.accounts.getByProviderAccount(identity.provider, identity.providerAccountId);
6292
6434
  if (!linked || linked.userId !== userId) {
6293
6435
  throw new OAuthFlowError("oauth_provider_account_linked", 409);
6294
6436
  }
6295
6437
  }
6296
- googleConfig() {
6297
- const google = this.config.oauth?.google;
6298
- if (!google)
6438
+ providerConfig(provider) {
6439
+ const providerConfig = this.config.oauth?.[provider];
6440
+ if (!providerConfig)
6299
6441
  throw new OAuthFlowError("oauth_provider_disabled", 404);
6300
- return google;
6442
+ return providerConfig;
6301
6443
  }
6302
6444
  };
6303
- __decorate36([
6304
- Inject20(AUTH_CONFIG),
6305
- __metadata36("design:type", Object)
6445
+ __decorate37([
6446
+ Inject21(AUTH_CONFIG),
6447
+ __metadata37("design:type", Object)
6306
6448
  ], OAuthAccountService.prototype, "config", void 0);
6307
- __decorate36([
6449
+ __decorate37([
6308
6450
  Transaction5(),
6309
- __metadata36("design:type", Function),
6310
- __metadata36("design:paramtypes", [Object]),
6311
- __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)
6312
6454
  ], OAuthAccountService.prototype, "resolveForLogin", null);
6313
- __decorate36([
6455
+ __decorate37([
6314
6456
  Transaction5(),
6315
- __metadata36("design:type", Function),
6316
- __metadata36("design:paramtypes", [String, Object]),
6317
- __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)
6318
6460
  ], OAuthAccountService.prototype, "linkUser", null);
6319
- OAuthAccountService = __decorate36([
6320
- Injectable19(),
6321
- __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])
6322
6464
  ], OAuthAccountService);
6323
6465
 
6324
- // src/oauth/OAuthController.ts
6325
- import { createHash as createHash5 } from "crypto";
6326
- import { Controller as Controller6, Ctx as Ctx2, Get as Get5, Post as Post6, Query as Query2, User as User5 } from "najm-core";
6327
- import { RateLimit as RateLimit3 } from "najm-rate";
6328
-
6329
- // src/oauth/OAuthService.ts
6330
- import { Inject as Inject21, Injectable as Injectable21, Log as Log2 } from "najm-core";
6331
-
6332
6466
  // src/oauth/OAuthStateService.ts
6333
6467
  import { createHash as createHash4, randomBytes as randomBytes4, timingSafeEqual } from "crypto";
6334
- import { Injectable as Injectable20 } from "najm-core";
6468
+ import { Injectable as Injectable21 } from "najm-core";
6335
6469
  import { CookieService as CookieService3 } from "najm-cookies";
6336
- var __decorate37 = function(decorators, target, key, desc) {
6470
+ var __decorate38 = function(decorators, target, key, desc) {
6337
6471
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
6338
6472
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
6339
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;
6340
6474
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6341
6475
  };
6342
- var __metadata37 = function(k, v) {
6476
+ var __metadata38 = function(k, v) {
6343
6477
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
6344
6478
  };
6345
6479
  var _a23;
6346
6480
  var _b13;
6347
6481
  var ATTEMPT_TTL_MS = 10 * 60 * 1e3;
6348
- var COOKIE_PREFIX = "najm.oauth.google.";
6482
+ var COOKIE_PREFIX = "najm.oauth.";
6349
6483
  var OAuthStateService = class OAuthStateService2 {
6350
6484
  static {
6351
6485
  __name(this, "OAuthStateService");
@@ -6360,7 +6494,7 @@ var OAuthStateService = class OAuthStateService2 {
6360
6494
  const state = randomBytes4(32).toString("base64url");
6361
6495
  const codeVerifier = randomBytes4(48).toString("base64url");
6362
6496
  const attempt = {
6363
- provider: "google",
6497
+ provider: input.provider,
6364
6498
  intent: input.intent,
6365
6499
  state,
6366
6500
  nonce: randomBytes4(32).toString("base64url"),
@@ -6370,7 +6504,7 @@ var OAuthStateService = class OAuthStateService2 {
6370
6504
  sessionVersion: input.sessionVersion,
6371
6505
  createdAt: Date.now()
6372
6506
  };
6373
- 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)), {
6374
6508
  httpOnly: true,
6375
6509
  sameSite: "Lax",
6376
6510
  path: "/",
@@ -6381,10 +6515,10 @@ var OAuthStateService = class OAuthStateService2 {
6381
6515
  codeChallenge: createHash4("sha256").update(codeVerifier).digest("base64url")
6382
6516
  };
6383
6517
  }
6384
- consume(state) {
6518
+ consume(provider, state) {
6385
6519
  if (!this.isSafeState(state))
6386
6520
  throw new OAuthFlowError("oauth_state_invalid");
6387
- const name = this.cookieName(state);
6521
+ const name = this.cookieName(provider, state);
6388
6522
  const encrypted = this.cookies.get(name);
6389
6523
  this.cookies.delete(name, { path: "/" });
6390
6524
  if (!encrypted)
@@ -6396,7 +6530,7 @@ var OAuthStateService = class OAuthStateService2 {
6396
6530
  if (expected.length !== actual.length || !timingSafeEqual(expected, actual)) {
6397
6531
  throw new OAuthFlowError("oauth_state_invalid");
6398
6532
  }
6399
- if (attempt.provider !== "google" || Date.now() - attempt.createdAt > ATTEMPT_TTL_MS) {
6533
+ if (attempt.provider !== provider || Date.now() - attempt.createdAt > ATTEMPT_TTL_MS) {
6400
6534
  throw new OAuthFlowError("oauth_state_invalid");
6401
6535
  }
6402
6536
  attempt.returnTo = this.validateReturnTo(attempt.returnTo);
@@ -6425,26 +6559,26 @@ var OAuthStateService = class OAuthStateService2 {
6425
6559
  throw new OAuthFlowError("oauth_redirect_invalid");
6426
6560
  }
6427
6561
  }
6428
- cookieName(state) {
6429
- return `${COOKIE_PREFIX}${state}`;
6562
+ cookieName(provider, state) {
6563
+ return `${COOKIE_PREFIX}${provider}.${state}`;
6430
6564
  }
6431
6565
  isSafeState(state) {
6432
6566
  return /^[A-Za-z0-9_-]{40,128}$/.test(state);
6433
6567
  }
6434
6568
  };
6435
- OAuthStateService = __decorate37([
6436
- Injectable20(),
6437
- __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])
6438
6572
  ], OAuthStateService);
6439
6573
 
6440
6574
  // src/oauth/OAuthService.ts
6441
- var __decorate38 = function(decorators, target, key, desc) {
6575
+ var __decorate39 = function(decorators, target, key, desc) {
6442
6576
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
6443
6577
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
6444
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;
6445
6579
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6446
6580
  };
6447
- var __metadata38 = function(k, v) {
6581
+ var __metadata39 = function(k, v) {
6448
6582
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
6449
6583
  };
6450
6584
  var _a24;
@@ -6454,12 +6588,14 @@ var _d8;
6454
6588
  var _e5;
6455
6589
  var _f5;
6456
6590
  var _g4;
6591
+ var _h3;
6457
6592
  var OAuthService = class OAuthService2 {
6458
6593
  static {
6459
6594
  __name(this, "OAuthService");
6460
6595
  }
6461
6596
  state;
6462
6597
  google;
6598
+ github;
6463
6599
  accounts;
6464
6600
  sessions;
6465
6601
  tokens;
@@ -6467,9 +6603,10 @@ var OAuthService = class OAuthService2 {
6467
6603
  requirements;
6468
6604
  config;
6469
6605
  logger;
6470
- constructor(state, google, accounts, sessions, tokens, users, requirements) {
6606
+ constructor(state, google, github, accounts, sessions, tokens, users, requirements) {
6471
6607
  this.state = state;
6472
6608
  this.google = google;
6609
+ this.github = github;
6473
6610
  this.accounts = accounts;
6474
6611
  this.sessions = sessions;
6475
6612
  this.tokens = tokens;
@@ -6477,36 +6614,59 @@ var OAuthService = class OAuthService2 {
6477
6614
  this.requirements = requirements;
6478
6615
  }
6479
6616
  startGoogleLogin(returnTo) {
6480
- this.googleConfig();
6481
- const { attempt, codeChallenge } = this.state.create({ intent: "login", returnTo });
6482
- 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);
6627
+ }
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);
6483
6642
  }
6484
- async startGoogleLink(userId, returnTo) {
6485
- this.googleConfig();
6643
+ async startLink(provider, userId, returnTo) {
6644
+ this.providerConfig(provider);
6486
6645
  const user = await this.users.getById(userId);
6487
6646
  if (user.status !== "active")
6488
6647
  throw new OAuthFlowError("oauth_account_inactive", 403);
6489
6648
  const sessionVersion = await this.tokens.getSessionVersion(userId);
6490
6649
  const { attempt, codeChallenge } = this.state.create({
6650
+ provider,
6491
6651
  intent: "link",
6492
6652
  returnTo,
6493
6653
  userId,
6494
6654
  sessionVersion
6495
6655
  });
6496
- return { authorizationUrl: this.google.authorizationUrl(attempt, codeChallenge) };
6656
+ return { authorizationUrl: this.provider(provider).authorizationUrl(attempt, codeChallenge) };
6497
6657
  }
6498
- async finishGoogleCallback(params) {
6658
+ async finishCallback(provider, params) {
6499
6659
  try {
6500
- this.googleConfig();
6660
+ this.providerConfig(provider);
6501
6661
  if (!params.state)
6502
6662
  throw new OAuthFlowError("oauth_state_invalid");
6503
- const attempt = this.state.consume(params.state);
6663
+ const attempt = this.state.consume(provider, params.state);
6504
6664
  if (params.error) {
6505
6665
  throw new OAuthFlowError(params.error === "access_denied" ? "oauth_access_denied" : "oauth_provider_error");
6506
6666
  }
6507
6667
  if (!params.code)
6508
6668
  throw new OAuthFlowError("oauth_provider_error");
6509
- const identity = await this.google.exchange(params.code, attempt);
6669
+ const identity = await this.provider(provider).exchange(params.code, attempt);
6510
6670
  if (attempt.intent === "link") {
6511
6671
  if (!attempt.userId || attempt.sessionVersion === void 0) {
6512
6672
  throw new OAuthFlowError("oauth_state_invalid");
@@ -6523,25 +6683,26 @@ var OAuthService = class OAuthService2 {
6523
6683
  }
6524
6684
  await this.sessions.establish(user);
6525
6685
  }
6526
- return this.frontendSuccessUrl(attempt.returnTo, attempt.intent);
6686
+ return this.frontendSuccessUrl(provider, attempt.returnTo, attempt.intent);
6527
6687
  } catch (error) {
6528
6688
  const code = this.publicErrorCode(error);
6529
- this.logger.warn("Google OAuth callback failed", { provider: "google", code });
6530
- return this.frontendErrorUrl(code);
6689
+ this.logger.warn("OAuth callback failed", { provider, code });
6690
+ return this.frontendErrorUrl(provider, code);
6531
6691
  }
6532
6692
  }
6533
- frontendSuccessUrl(returnTo, mode) {
6534
- const google = this.googleConfig();
6535
- const url = new URL(google.frontendCallbackPath, this.config.frontendUrl);
6536
- 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);
6537
6697
  url.searchParams.set("mode", mode);
6538
6698
  url.searchParams.set("returnTo", this.state.validateReturnTo(returnTo));
6539
6699
  return url.toString();
6540
6700
  }
6541
- frontendErrorUrl(code) {
6542
- const path2 = this.config.oauth?.google?.errorRedirectPath ?? "/login";
6701
+ frontendErrorUrl(provider, code) {
6702
+ const path2 = this.config.oauth?.[provider]?.errorRedirectPath ?? "/login";
6543
6703
  const url = new URL(path2, this.config.frontendUrl);
6544
6704
  url.searchParams.set("oauthError", code);
6705
+ url.searchParams.set("provider", provider);
6545
6706
  return url.toString();
6546
6707
  }
6547
6708
  publicErrorCode(error) {
@@ -6551,34 +6712,37 @@ var OAuthService = class OAuthService2 {
6551
6712
  return error.message;
6552
6713
  return "oauth_provider_error";
6553
6714
  }
6554
- googleConfig() {
6555
- const google = this.config.oauth?.google;
6556
- 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)
6557
6721
  throw new OAuthFlowError("oauth_provider_disabled", 404);
6558
- return google;
6722
+ return providerConfig;
6559
6723
  }
6560
6724
  };
6561
- __decorate38([
6562
- Inject21(AUTH_CONFIG),
6563
- __metadata38("design:type", Object)
6725
+ __decorate39([
6726
+ Inject22(AUTH_CONFIG),
6727
+ __metadata39("design:type", Object)
6564
6728
  ], OAuthService.prototype, "config", void 0);
6565
- __decorate38([
6729
+ __decorate39([
6566
6730
  Log2(),
6567
- __metadata38("design:type", Object)
6731
+ __metadata39("design:type", Object)
6568
6732
  ], OAuthService.prototype, "logger", void 0);
6569
- OAuthService = __decorate38([
6570
- Injectable21(),
6571
- __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])
6572
6736
  ], OAuthService);
6573
6737
 
6574
- // src/oauth/OAuthController.ts
6575
- var __decorate39 = function(decorators, target, key, desc) {
6738
+ // src/oauth/GitHubOAuthController.ts
6739
+ var __decorate40 = function(decorators, target, key, desc) {
6576
6740
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
6577
6741
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
6578
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;
6579
6743
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6580
6744
  };
6581
- var __metadata39 = function(k, v) {
6745
+ var __metadata40 = function(k, v) {
6582
6746
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
6583
6747
  };
6584
6748
  var __param13 = function(paramIndex, decorator) {
@@ -6588,63 +6752,141 @@ var __param13 = function(paramIndex, decorator) {
6588
6752
  };
6589
6753
  var _a25;
6590
6754
  var callbackKey = /* @__PURE__ */ __name((ctx, { clientIp }) => {
6591
- const ip = clientIp;
6592
6755
  const state = ctx.req.query("state") ?? "none";
6593
6756
  const fingerprint = createHash5("sha256").update(state).digest("base64url").slice(0, 24);
6594
- return `${ip}:${fingerprint}`;
6757
+ return `${clientIp}:${fingerprint}`;
6595
6758
  }, "callbackKey");
6596
- var OAuthController = class OAuthController2 {
6759
+ var GitHubOAuthController = class GitHubOAuthController2 {
6597
6760
  static {
6598
- __name(this, "OAuthController");
6761
+ __name(this, "GitHubOAuthController");
6599
6762
  }
6600
6763
  oauth;
6601
6764
  constructor(oauth) {
6602
6765
  this.oauth = oauth;
6603
6766
  }
6604
6767
  start(ctx, returnTo) {
6605
- return ctx.redirect(this.oauth.startGoogleLogin(returnTo), 302);
6768
+ return ctx.redirect(this.oauth.startGitHubLogin(returnTo), 302);
6606
6769
  }
6607
6770
  async callback(ctx, code, state, error) {
6608
- const redirect = await this.oauth.finishGoogleCallback({ code, state, error });
6771
+ const redirect = await this.oauth.finishGitHubCallback({ code, state, error });
6609
6772
  return ctx.redirect(redirect, 302);
6610
6773
  }
6611
6774
  link(userId, returnTo) {
6612
- return this.oauth.startGoogleLink(userId, returnTo);
6775
+ return this.oauth.startGitHubLink(userId, returnTo);
6613
6776
  }
6614
6777
  };
6615
- __decorate39([
6778
+ __decorate40([
6616
6779
  Get5("/start"),
6617
6780
  RateLimit3({ limit: 20, window: "15m", key: "ip" }),
6618
6781
  __param13(0, Ctx2()),
6619
6782
  __param13(1, Query2("returnTo")),
6620
- __metadata39("design:type", Function),
6621
- __metadata39("design:paramtypes", [Object, String]),
6622
- __metadata39("design:returntype", void 0)
6623
- ], OAuthController.prototype, "start", null);
6624
- __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([
6625
6788
  Get5("/callback"),
6626
6789
  RateLimit3({ limit: 20, window: "15m", key: callbackKey }),
6627
6790
  __param13(0, Ctx2()),
6628
6791
  __param13(1, Query2("code")),
6629
6792
  __param13(2, Query2("state")),
6630
6793
  __param13(3, Query2("error")),
6631
- __metadata39("design:type", Function),
6632
- __metadata39("design:paramtypes", [Object, String, String, String]),
6633
- __metadata39("design:returntype", Promise)
6634
- ], OAuthController.prototype, "callback", null);
6635
- __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([
6636
6799
  Post6("/link"),
6637
6800
  isAuth(),
6638
6801
  RateLimit3({ limit: 10, window: "15m", key: "user" }),
6639
6802
  __param13(0, User5("id")),
6640
6803
  __param13(1, Query2("returnTo")),
6641
- __metadata39("design:type", Function),
6642
- __metadata39("design:paramtypes", [String, String]),
6643
- __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)
6644
6886
  ], OAuthController.prototype, "link", null);
6645
- OAuthController = __decorate39([
6646
- Controller6("/auth/oauth/google"),
6647
- __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])
6648
6890
  ], OAuthController);
6649
6891
 
6650
6892
  // src/oauth/index.ts
@@ -6654,13 +6896,15 @@ var OAUTH_MODULE = [
6654
6896
  OAuthStateService,
6655
6897
  GoogleTokenVerifier,
6656
6898
  GoogleOAuthProvider,
6899
+ GitHubOAuthProvider,
6657
6900
  OAuthService,
6658
- OAuthController
6901
+ OAuthController,
6902
+ GitHubOAuthController
6659
6903
  ];
6660
6904
 
6661
6905
  // src/credentialSetup/CredentialSetupController.ts
6662
- import { Body as Body7, Controller as Controller7, Get as Get6, Post as Post7, ResMsg as ResMsg6 } from "najm-core";
6663
- 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";
6664
6908
  import { Validate as Validate6 } from "najm-validation";
6665
6909
 
6666
6910
  // src/credentialSetup/CredentialSetupDto.ts
@@ -6671,21 +6915,21 @@ var credentialSetupChangeDto = z5.object({
6671
6915
  });
6672
6916
 
6673
6917
  // src/credentialSetup/CredentialSetupController.ts
6674
- var __decorate40 = function(decorators, target, key, desc) {
6918
+ var __decorate42 = function(decorators, target, key, desc) {
6675
6919
  var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
6676
6920
  if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
6677
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;
6678
6922
  return c > 3 && r && Object.defineProperty(target, key, r), r;
6679
6923
  };
6680
- var __metadata40 = function(k, v) {
6924
+ var __metadata42 = function(k, v) {
6681
6925
  if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
6682
6926
  };
6683
- var __param14 = function(paramIndex, decorator) {
6927
+ var __param15 = function(paramIndex, decorator) {
6684
6928
  return function(target, key) {
6685
6929
  decorator(target, key, paramIndex);
6686
6930
  };
6687
6931
  };
6688
- var _a26;
6932
+ var _a27;
6689
6933
  var CredentialSetupController = class CredentialSetupController2 {
6690
6934
  static {
6691
6935
  __name(this, "CredentialSetupController");
@@ -6704,35 +6948,35 @@ var CredentialSetupController = class CredentialSetupController2 {
6704
6948
  return this.passwords.cancel();
6705
6949
  }
6706
6950
  };
6707
- __decorate40([
6708
- Get6("/setup"),
6709
- RateLimit4({ limit: 30, window: "15m", key: "ip" }),
6951
+ __decorate42([
6952
+ Get7("/setup"),
6953
+ RateLimit5({ limit: 30, window: "15m", key: "ip" }),
6710
6954
  ResMsg6("auth.success.credentialSetupPending"),
6711
- __metadata40("design:type", Function),
6712
- __metadata40("design:paramtypes", []),
6713
- __metadata40("design:returntype", void 0)
6955
+ __metadata42("design:type", Function),
6956
+ __metadata42("design:paramtypes", []),
6957
+ __metadata42("design:returntype", void 0)
6714
6958
  ], CredentialSetupController.prototype, "status", null);
6715
- __decorate40([
6716
- Post7("/change"),
6717
- RateLimit4({ limit: 5, window: "15m", key: "ip" }),
6959
+ __decorate42([
6960
+ Post8("/change"),
6961
+ RateLimit5({ limit: 5, window: "15m", key: "ip" }),
6718
6962
  Validate6(credentialSetupChangeDto),
6719
6963
  ResMsg6("auth.success.credentialSetupPasswordReplaced"),
6720
- __param14(0, Body7()),
6721
- __metadata40("design:type", Function),
6722
- __metadata40("design:paramtypes", [Object]),
6723
- __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)
6724
6968
  ], CredentialSetupController.prototype, "change", null);
6725
- __decorate40([
6726
- Post7("/cancel"),
6727
- RateLimit4({ limit: 10, window: "15m", key: "ip" }),
6969
+ __decorate42([
6970
+ Post8("/cancel"),
6971
+ RateLimit5({ limit: 10, window: "15m", key: "ip" }),
6728
6972
  ResMsg6("auth.success.credentialSetupCancelled"),
6729
- __metadata40("design:type", Function),
6730
- __metadata40("design:paramtypes", []),
6731
- __metadata40("design:returntype", void 0)
6973
+ __metadata42("design:type", Function),
6974
+ __metadata42("design:paramtypes", []),
6975
+ __metadata42("design:returntype", void 0)
6732
6976
  ], CredentialSetupController.prototype, "cancel", null);
6733
- CredentialSetupController = __decorate40([
6734
- Controller7("/auth/credential-setup"),
6735
- __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])
6736
6980
  ], CredentialSetupController);
6737
6981
 
6738
6982
  // src/credentialSetup/index.ts
@@ -6758,6 +7002,19 @@ var validateFrontendPath = /* @__PURE__ */ __name((value, name) => {
6758
7002
  }
6759
7003
  return value;
6760
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");
6761
7018
  var resolveGoogleConfig = /* @__PURE__ */ __name((config) => {
6762
7019
  const configuredGoogle = config?.oauth?.google;
6763
7020
  if (!configuredGoogle)
@@ -6771,20 +7028,10 @@ var resolveGoogleConfig = /* @__PURE__ */ __name((config) => {
6771
7028
  throw Err15.configRequired("auth.oauth.google", "GOOGLE_CLIENT_SECRET");
6772
7029
  const frontendUrl = config?.frontendUrl ?? process.env.FRONTEND_URL ?? "http://localhost:3000";
6773
7030
  const callbackUrl = google.callbackUrl ?? process.env.GOOGLE_CALLBACK_URL ?? `${frontendUrl.replace(/\/$/, "")}/api/auth/oauth/google/callback`;
6774
- let callback;
6775
- try {
6776
- callback = new URL(callbackUrl);
6777
- } catch {
6778
- throw new Error("auth.oauth.google.callbackUrl must be an absolute URL");
6779
- }
6780
- const local = callback.hostname === "localhost" || callback.hostname === "127.0.0.1" || callback.hostname === "[::1]" || callback.hostname === "::1";
6781
- if (callback.protocol !== "https:" && !(local && callback.protocol === "http:")) {
6782
- throw new Error("auth.oauth.google.callbackUrl must use HTTPS (HTTP is allowed only for localhost)");
6783
- }
6784
7031
  return {
6785
7032
  clientId,
6786
7033
  clientSecret,
6787
- callbackUrl: callback.toString(),
7034
+ callbackUrl: validateCallbackUrl(callbackUrl, "auth.oauth.google.callbackUrl"),
6788
7035
  frontendCallbackPath: validateFrontendPath(google.frontendCallbackPath ?? "/auth/oauth/callback", "auth.oauth.google.frontendCallbackPath"),
6789
7036
  errorRedirectPath: validateFrontendPath(google.errorRedirectPath ?? "/login", "auth.oauth.google.errorRedirectPath"),
6790
7037
  allowSignup: google.allowSignup ?? true,
@@ -6792,6 +7039,29 @@ var resolveGoogleConfig = /* @__PURE__ */ __name((config) => {
6792
7039
  allowedHostedDomains: [...new Set((google.allowedHostedDomains ?? []).map((domain) => domain.trim().toLowerCase()).filter(Boolean))]
6793
7040
  };
6794
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");
6795
7065
  var resolveCredentialSetupConfig = /* @__PURE__ */ __name((config) => {
6796
7066
  const password = config?.credentialSetup?.password ?? {};
6797
7067
  const ttlMs = password.ttlMs ?? DEFAULT_CREDENTIAL_SETUP_TTL_MS;
@@ -6842,7 +7112,8 @@ var resolveAuthConfig = /* @__PURE__ */ __name((config) => {
6842
7112
  },
6843
7113
  credentialSetup: resolveCredentialSetupConfig(config),
6844
7114
  oauth: {
6845
- google: resolveGoogleConfig(config)
7115
+ google: resolveGoogleConfig(config),
7116
+ github: resolveGitHubConfig(config)
6846
7117
  }
6847
7118
  };
6848
7119
  if (!finalConfig.jwt.accessSecret) {
@@ -6855,8 +7126,8 @@ var resolveAuthConfig = /* @__PURE__ */ __name((config) => {
6855
7126
  }, "resolveAuthConfig");
6856
7127
  var selectAuthSchema = /* @__PURE__ */ __name((config) => {
6857
7128
  if (config?.schema) {
6858
- if (config.oauth?.google && !config.schema.oauthAccounts) {
6859
- 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");
6860
7131
  }
6861
7132
  if (!config.schema.credentialSetupSessions) {
6862
7133
  throw new Error("auth.schema.credentialSetupSessions is required \u2014 re-export it from najm-auth/pg or najm-auth/sqlite");