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/README.md +45 -18
- package/dist/{NajmAuthClient-ZtXTIUSF.d.ts → NajmAuthClient-BygfBCpF.d.ts} +3 -1
- package/dist/client/index.d.ts +3 -3
- package/dist/client/index.js +9 -2
- package/dist/client/react/index.d.ts +23 -3
- package/dist/client/react/index.js +63 -6
- package/dist/client/server/index.d.ts +4 -4
- package/dist/client/server/index.js +9 -2
- package/dist/client/server/react.d.ts +2 -2
- package/dist/{getSession-BthP85UA.d.ts → getSession-DBX8XpsR.d.ts} +1 -1
- package/dist/index.d.ts +45 -14
- package/dist/index.js +448 -174
- package/dist/schema/pg.d.ts +2 -2
- package/dist/schema/sqlite.d.ts +4 -4
- package/dist/{types-BaSfgxqE.d.ts → types-CI2t8wpJ.d.ts} +1 -1
- package/package.json +1 -1
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
|
-
|
|
3365
|
-
|
|
3366
|
-
|
|
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
|
|
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 (
|
|
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
|
|
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 (
|
|
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
|
|
6308
|
+
import { Inject as Inject20, Repository as Repository7 } from "najm-core";
|
|
6161
6309
|
import { DB as DB7 } from "najm-database";
|
|
6162
|
-
var
|
|
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
|
|
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
|
-
|
|
6344
|
+
__decorate36([
|
|
6197
6345
|
DB7(),
|
|
6198
|
-
|
|
6346
|
+
__metadata36("design:type", Object)
|
|
6199
6347
|
], OAuthAccountRepository.prototype, "db", void 0);
|
|
6200
|
-
|
|
6201
|
-
|
|
6202
|
-
|
|
6348
|
+
__decorate36([
|
|
6349
|
+
Inject20(AUTH_SCHEMA),
|
|
6350
|
+
__metadata36("design:type", Object)
|
|
6203
6351
|
], OAuthAccountRepository.prototype, "schema", void 0);
|
|
6204
|
-
OAuthAccountRepository =
|
|
6352
|
+
OAuthAccountRepository = __decorate36([
|
|
6205
6353
|
Repository7()
|
|
6206
6354
|
], OAuthAccountRepository);
|
|
6207
6355
|
|
|
6208
6356
|
// src/oauth/OAuthAccountService.ts
|
|
6209
|
-
|
|
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
|
|
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(
|
|
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.
|
|
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.
|
|
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(
|
|
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,
|
|
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:
|
|
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(
|
|
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
|
-
|
|
6294
|
-
const
|
|
6295
|
-
if (!
|
|
6438
|
+
providerConfig(provider) {
|
|
6439
|
+
const providerConfig = this.config.oauth?.[provider];
|
|
6440
|
+
if (!providerConfig)
|
|
6296
6441
|
throw new OAuthFlowError("oauth_provider_disabled", 404);
|
|
6297
|
-
return
|
|
6442
|
+
return providerConfig;
|
|
6298
6443
|
}
|
|
6299
6444
|
};
|
|
6300
|
-
|
|
6301
|
-
|
|
6302
|
-
|
|
6445
|
+
__decorate37([
|
|
6446
|
+
Inject21(AUTH_CONFIG),
|
|
6447
|
+
__metadata37("design:type", Object)
|
|
6303
6448
|
], OAuthAccountService.prototype, "config", void 0);
|
|
6304
|
-
|
|
6449
|
+
__decorate37([
|
|
6305
6450
|
Transaction5(),
|
|
6306
|
-
|
|
6307
|
-
|
|
6308
|
-
|
|
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
|
-
|
|
6455
|
+
__decorate37([
|
|
6311
6456
|
Transaction5(),
|
|
6312
|
-
|
|
6313
|
-
|
|
6314
|
-
|
|
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 =
|
|
6317
|
-
|
|
6318
|
-
|
|
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
|
|
6468
|
+
import { Injectable as Injectable21 } from "najm-core";
|
|
6332
6469
|
import { CookieService as CookieService3 } from "najm-cookies";
|
|
6333
|
-
var
|
|
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
|
|
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.
|
|
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:
|
|
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 !==
|
|
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 =
|
|
6433
|
-
|
|
6434
|
-
|
|
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
|
|
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
|
|
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.
|
|
6478
|
-
|
|
6479
|
-
|
|
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
|
-
|
|
6482
|
-
this.
|
|
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.
|
|
6656
|
+
return { authorizationUrl: this.provider(provider).authorizationUrl(attempt, codeChallenge) };
|
|
6494
6657
|
}
|
|
6495
|
-
async
|
|
6658
|
+
async finishCallback(provider, params) {
|
|
6496
6659
|
try {
|
|
6497
|
-
this.
|
|
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.
|
|
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("
|
|
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
|
|
6532
|
-
const url = new URL(
|
|
6533
|
-
url.searchParams.set("provider",
|
|
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?.
|
|
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
|
-
|
|
6552
|
-
|
|
6553
|
-
|
|
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
|
|
6722
|
+
return providerConfig;
|
|
6556
6723
|
}
|
|
6557
6724
|
};
|
|
6558
|
-
|
|
6559
|
-
|
|
6560
|
-
|
|
6725
|
+
__decorate39([
|
|
6726
|
+
Inject22(AUTH_CONFIG),
|
|
6727
|
+
__metadata39("design:type", Object)
|
|
6561
6728
|
], OAuthService.prototype, "config", void 0);
|
|
6562
|
-
|
|
6729
|
+
__decorate39([
|
|
6563
6730
|
Log2(),
|
|
6564
|
-
|
|
6731
|
+
__metadata39("design:type", Object)
|
|
6565
6732
|
], OAuthService.prototype, "logger", void 0);
|
|
6566
|
-
OAuthService =
|
|
6567
|
-
|
|
6568
|
-
|
|
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/
|
|
6572
|
-
var
|
|
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
|
|
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 `${
|
|
6757
|
+
return `${clientIp}:${fingerprint}`;
|
|
6592
6758
|
}, "callbackKey");
|
|
6593
|
-
var
|
|
6759
|
+
var GitHubOAuthController = class GitHubOAuthController2 {
|
|
6594
6760
|
static {
|
|
6595
|
-
__name(this, "
|
|
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.
|
|
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.
|
|
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.
|
|
6775
|
+
return this.oauth.startGitHubLink(userId, returnTo);
|
|
6610
6776
|
}
|
|
6611
6777
|
};
|
|
6612
|
-
|
|
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
|
-
|
|
6618
|
-
|
|
6619
|
-
|
|
6620
|
-
],
|
|
6621
|
-
|
|
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
|
-
|
|
6629
|
-
|
|
6630
|
-
|
|
6631
|
-
],
|
|
6632
|
-
|
|
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
|
-
|
|
6639
|
-
|
|
6640
|
-
|
|
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 =
|
|
6643
|
-
|
|
6644
|
-
|
|
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
|
|
6660
|
-
import { RateLimit as
|
|
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
|
|
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
|
|
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
|
|
6927
|
+
var __param15 = function(paramIndex, decorator) {
|
|
6681
6928
|
return function(target, key) {
|
|
6682
6929
|
decorator(target, key, paramIndex);
|
|
6683
6930
|
};
|
|
6684
6931
|
};
|
|
6685
|
-
var
|
|
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
|
-
|
|
6705
|
-
|
|
6706
|
-
|
|
6951
|
+
__decorate42([
|
|
6952
|
+
Get7("/setup"),
|
|
6953
|
+
RateLimit5({ limit: 30, window: "15m", key: "ip" }),
|
|
6707
6954
|
ResMsg6("auth.success.credentialSetupPending"),
|
|
6708
|
-
|
|
6709
|
-
|
|
6710
|
-
|
|
6955
|
+
__metadata42("design:type", Function),
|
|
6956
|
+
__metadata42("design:paramtypes", []),
|
|
6957
|
+
__metadata42("design:returntype", void 0)
|
|
6711
6958
|
], CredentialSetupController.prototype, "status", null);
|
|
6712
|
-
|
|
6713
|
-
|
|
6714
|
-
|
|
6959
|
+
__decorate42([
|
|
6960
|
+
Post8("/change"),
|
|
6961
|
+
RateLimit5({ limit: 5, window: "15m", key: "ip" }),
|
|
6715
6962
|
Validate6(credentialSetupChangeDto),
|
|
6716
6963
|
ResMsg6("auth.success.credentialSetupPasswordReplaced"),
|
|
6717
|
-
|
|
6718
|
-
|
|
6719
|
-
|
|
6720
|
-
|
|
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
|
-
|
|
6723
|
-
|
|
6724
|
-
|
|
6969
|
+
__decorate42([
|
|
6970
|
+
Post8("/cancel"),
|
|
6971
|
+
RateLimit5({ limit: 10, window: "15m", key: "ip" }),
|
|
6725
6972
|
ResMsg6("auth.success.credentialSetupCancelled"),
|
|
6726
|
-
|
|
6727
|
-
|
|
6728
|
-
|
|
6973
|
+
__metadata42("design:type", Function),
|
|
6974
|
+
__metadata42("design:paramtypes", []),
|
|
6975
|
+
__metadata42("design:returntype", void 0)
|
|
6729
6976
|
], CredentialSetupController.prototype, "cancel", null);
|
|
6730
|
-
CredentialSetupController =
|
|
6731
|
-
|
|
6732
|
-
|
|
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:
|
|
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
|
|
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");
|