feat(api-auth): add totp 2fa enrollment and verify
This commit is contained in:
@@ -3,6 +3,7 @@ AUTH_ACCESS_SECRET="dev-access-secret"
|
||||
AUTH_ACCESS_EXPIRES_IN_SECONDS="900"
|
||||
AUTH_REFRESH_EXPIRES_IN_SECONDS="2592000"
|
||||
AUTH_EMAIL_CODE_TTL_SECONDS="300"
|
||||
AUTH_TOTP_ISSUER="TodoList"
|
||||
OAUTH_GITHUB_CLIENT_ID="github-client-id"
|
||||
OAUTH_GITHUB_CLIENT_SECRET="github-client-secret"
|
||||
OAUTH_GITHUB_CALLBACK_URL="http://localhost:3000/auth/oauth/github/callback"
|
||||
|
||||
@@ -30,9 +30,11 @@
|
||||
"@nestjs/core": "^11.1.18",
|
||||
"@nestjs/jwt": "^11.0.2",
|
||||
"@nestjs/platform-express": "^11.1.18",
|
||||
"@otplib/preset-default": "^12.0.1",
|
||||
"@prisma/client": "^7.6.0",
|
||||
"class-transformer": "^0.5.1",
|
||||
"class-validator": "^0.15.1",
|
||||
"otplib": "^13.4.0",
|
||||
"reflect-metadata": "^0.2.2",
|
||||
"rxjs": "^7.8.2"
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import { AuthService } from "./auth.service";
|
||||
import { EmailLoginDto } from "./dto/email-login.dto";
|
||||
import { RefreshTokenDto } from "./dto/refresh-token.dto";
|
||||
import { SendEmailCodeDto } from "./dto/send-email-code.dto";
|
||||
import { TwoFactorEnrollDto } from "./dto/two-factor-enroll.dto";
|
||||
import { TwoFactorVerifyDto } from "./dto/two-factor-verify.dto";
|
||||
|
||||
@Controller("auth")
|
||||
export class AuthController {
|
||||
@@ -45,6 +47,23 @@ export class AuthController {
|
||||
return this.authService.revokeRefreshToken(body.refreshToken);
|
||||
}
|
||||
|
||||
@Post("2fa/enroll")
|
||||
async enrollTwoFactor(@Body() body: TwoFactorEnrollDto): Promise<{
|
||||
userId: string;
|
||||
secret: string;
|
||||
otpauthUrl: string;
|
||||
enabled: boolean;
|
||||
}> {
|
||||
return this.authService.enrollTwoFactor(body.email);
|
||||
}
|
||||
|
||||
@Post("2fa/verify")
|
||||
async verifyTwoFactor(
|
||||
@Body() body: TwoFactorVerifyDto
|
||||
): Promise<{ success: boolean; enabled: boolean }> {
|
||||
return this.authService.verifyTwoFactor(body.email, body.token);
|
||||
}
|
||||
|
||||
@Get("oauth/github")
|
||||
@UseGuards(AuthGuard("github"))
|
||||
githubLogin(): void {}
|
||||
|
||||
@@ -2,6 +2,7 @@ import { Injectable, UnauthorizedException } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { JwtService } from "@nestjs/jwt";
|
||||
import { randomUUID } from "node:crypto";
|
||||
import { authenticator } from "@otplib/preset-default";
|
||||
|
||||
type EmailCodeEntry = {
|
||||
code: string;
|
||||
@@ -19,6 +20,11 @@ type RefreshTokenEntry = {
|
||||
revokedAt?: number;
|
||||
};
|
||||
|
||||
type TwoFactorEntry = {
|
||||
secret: string;
|
||||
enabled: boolean;
|
||||
};
|
||||
|
||||
type AuthTokenResult = {
|
||||
accessToken: string;
|
||||
tokenType: "Bearer";
|
||||
@@ -34,6 +40,7 @@ export class AuthService {
|
||||
private readonly userStoreByEmail = new Map<string, AuthUser>();
|
||||
private readonly userStoreById = new Map<string, AuthUser>();
|
||||
private readonly refreshTokenStore = new Map<string, RefreshTokenEntry>();
|
||||
private readonly twoFactorStore = new Map<string, TwoFactorEntry>();
|
||||
|
||||
constructor(
|
||||
private readonly configService: ConfigService,
|
||||
@@ -111,6 +118,49 @@ export class AuthService {
|
||||
return { success: true };
|
||||
}
|
||||
|
||||
async enrollTwoFactor(
|
||||
email: string
|
||||
): Promise<{ userId: string; secret: string; otpauthUrl: string; enabled: boolean }> {
|
||||
const user = this.getOrCreateUser(email.toLowerCase());
|
||||
const secret = authenticator.generateSecret();
|
||||
const issuer = this.configService.get<string>("AUTH_TOTP_ISSUER") ?? "TodoList";
|
||||
const otpauthUrl = authenticator.keyuri(user.email, issuer, secret);
|
||||
|
||||
this.twoFactorStore.set(user.id, {
|
||||
secret,
|
||||
enabled: false
|
||||
});
|
||||
|
||||
return {
|
||||
userId: user.id,
|
||||
secret,
|
||||
otpauthUrl,
|
||||
enabled: false
|
||||
};
|
||||
}
|
||||
|
||||
async verifyTwoFactor(
|
||||
email: string,
|
||||
token: string
|
||||
): Promise<{ success: boolean; enabled: boolean }> {
|
||||
const user = this.getOrCreateUser(email.toLowerCase());
|
||||
const entry = this.twoFactorStore.get(user.id);
|
||||
if (!entry) {
|
||||
throw new UnauthorizedException("尚未启用两步验证");
|
||||
}
|
||||
|
||||
const valid = authenticator.check(token, entry.secret);
|
||||
if (!valid) {
|
||||
throw new UnauthorizedException("两步验证码错误");
|
||||
}
|
||||
|
||||
entry.enabled = true;
|
||||
return {
|
||||
success: true,
|
||||
enabled: true
|
||||
};
|
||||
}
|
||||
|
||||
private getOrCreateUser(email: string): AuthUser {
|
||||
const existingUser = this.userStoreByEmail.get(email);
|
||||
if (existingUser) {
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
import { IsEmail } from "class-validator";
|
||||
|
||||
export class TwoFactorEnrollDto {
|
||||
@IsEmail()
|
||||
email!: string;
|
||||
}
|
||||
@@ -0,0 +1,11 @@
|
||||
import { IsEmail, IsString, Length, Matches } from "class-validator";
|
||||
|
||||
export class TwoFactorVerifyDto {
|
||||
@IsEmail()
|
||||
email!: string;
|
||||
|
||||
@IsString()
|
||||
@Length(6, 6)
|
||||
@Matches(/^\d{6}$/)
|
||||
token!: string;
|
||||
}
|
||||
Generated
+295
-116
@@ -43,12 +43,12 @@ importers:
|
||||
"@nestjs/jwt":
|
||||
specifier: ^11.0.2
|
||||
version: 11.0.2(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))
|
||||
"@nestjs/passport":
|
||||
specifier: ^11.0.5
|
||||
version: 11.0.5(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0)
|
||||
"@nestjs/platform-express":
|
||||
specifier: ^11.1.18
|
||||
version: 11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)
|
||||
"@otplib/preset-default":
|
||||
specifier: ^12.0.1
|
||||
version: 12.0.1
|
||||
"@prisma/client":
|
||||
specifier: ^7.6.0
|
||||
version: 7.6.0(prisma@7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3)
|
||||
@@ -58,15 +58,9 @@ importers:
|
||||
class-validator:
|
||||
specifier: ^0.15.1
|
||||
version: 0.15.1
|
||||
passport:
|
||||
specifier: ^0.7.0
|
||||
version: 0.7.0
|
||||
passport-github2:
|
||||
specifier: ^0.1.12
|
||||
version: 0.1.12
|
||||
passport-oauth2:
|
||||
specifier: ^1.8.0
|
||||
version: 1.8.0
|
||||
otplib:
|
||||
specifier: ^13.4.0
|
||||
version: 13.4.0
|
||||
reflect-metadata:
|
||||
specifier: ^0.2.2
|
||||
version: 0.2.2
|
||||
@@ -77,6 +71,12 @@ importers:
|
||||
"@types/node":
|
||||
specifier: ^25.5.2
|
||||
version: 25.5.2
|
||||
"@types/passport-github2":
|
||||
specifier: ^1.2.9
|
||||
version: 1.2.9
|
||||
"@types/passport-oauth2":
|
||||
specifier: ^1.8.0
|
||||
version: 1.8.0
|
||||
dotenv:
|
||||
specifier: ^16.6.1
|
||||
version: 16.6.1
|
||||
@@ -332,15 +332,6 @@ packages:
|
||||
peerDependencies:
|
||||
"@nestjs/common": ^8.0.0 || ^9.0.0 || ^10.0.0 || ^11.0.0
|
||||
|
||||
"@nestjs/passport@11.0.5":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-ulQX6mbjlws92PIM15Naes4F4p2JoxGnIJuUsdXQPT+Oo2sqQmENEZXM7eYuimocfHnKlcfZOuyzbA33LwUlOQ==
|
||||
}
|
||||
peerDependencies:
|
||||
"@nestjs/common": ^10.0.0 || ^11.0.0
|
||||
passport: ^0.5.0 || ^0.6.0 || ^0.7.0
|
||||
|
||||
"@nestjs/platform-express@11.1.18":
|
||||
resolution:
|
||||
{
|
||||
@@ -350,6 +341,13 @@ packages:
|
||||
"@nestjs/common": ^11.0.0
|
||||
"@nestjs/core": ^11.0.0
|
||||
|
||||
"@noble/hashes@2.0.1":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-XlOlEbQcE9fmuXxrVTXCTlG2nlRXa9Rj3rr5Ue/+tX+nmkgbX720YHh0VR3hBF9xDvwnb8D2shVGOwNx+ulArw==
|
||||
}
|
||||
engines: { node: ">= 20.19.0" }
|
||||
|
||||
"@nuxt/opencollective@0.4.1":
|
||||
resolution:
|
||||
{
|
||||
@@ -358,6 +356,69 @@ packages:
|
||||
engines: { node: ^14.18.0 || >=16.10.0, npm: ">=5.10.0" }
|
||||
hasBin: true
|
||||
|
||||
"@otplib/core@12.0.1":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-4sGntwbA/AC+SbPhbsziRiD+jNDdIzsZ3JUyfZwjtKyc/wufl1pnSIaG4Uqx8ymPagujub0o92kgBnB89cuAMA==
|
||||
}
|
||||
|
||||
"@otplib/core@13.4.0":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-JqOGcvZQi2wIkEQo8f3/iAjstavpXy6gouIDMHygjNuH6Q0FjbHOiXMdcE94RwfgDNMABhzwUmvaPsxvgm9NYw==
|
||||
}
|
||||
|
||||
"@otplib/hotp@13.4.0":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-MJjE0x06mn2ptymz5qZmQveb+vWFuaIftqE0b5/TZZqUOK7l97cV8lRTmid5BpAQMwJDNLW6RnYxGeCRiNdekw==
|
||||
}
|
||||
|
||||
"@otplib/plugin-base32-scure@13.4.0":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-/t9YWJmMbB8bF5z8mXrBZc2FXBe8B/3hG5FhWr9K8cFwFhyxScbPysmZe8s1UTzSA6N+s8Uv8aIfCtVXPNjJWw==
|
||||
}
|
||||
|
||||
"@otplib/plugin-crypto-noble@13.4.0":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-KrvE4m7Zv+TT1944HzgqFJWJpKb6AyoxDbvhPStmBqdMlv5Gekb80d66cuFRL08kkPgJ5gXUSb5SFpYeB+bACg==
|
||||
}
|
||||
|
||||
"@otplib/plugin-crypto@12.0.1":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-qPuhN3QrT7ZZLcLCyKOSNhuijUi9G5guMRVrxq63r9YNOxxQjPm59gVxLM+7xGnHnM6cimY57tuKsjK7y9LM1g==
|
||||
}
|
||||
deprecated: Please upgrade to v13 of otplib. Refer to otplib docs for migration paths
|
||||
|
||||
"@otplib/plugin-thirty-two@12.0.1":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-MtT+uqRso909UkbrrYpJ6XFjj9D+x2Py7KjTO9JDPhL0bJUYVu5kFP4TFZW4NFAywrAtFRxOVY261u0qwb93gA==
|
||||
}
|
||||
deprecated: Please upgrade to v13 of otplib. Refer to otplib docs for migration paths
|
||||
|
||||
"@otplib/preset-default@12.0.1":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-xf1v9oOJRyXfluBhMdpOkr+bsE+Irt+0D5uHtvg6x1eosfmHCsCC6ej/m7FXiWqdo0+ZUI6xSKDhJwc8yfiOPQ==
|
||||
}
|
||||
deprecated: Please upgrade to v13 of otplib. Refer to otplib docs for migration paths
|
||||
|
||||
"@otplib/totp@13.4.0":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-dK+vl0f0ekzf6mCENRI9AKS2NJUC7OjI3+X8e7QSnhQ2WM7I+i4PGpb3QxKi5hxjTtwVuoZwXR2CFtXdcRtNdQ==
|
||||
}
|
||||
|
||||
"@otplib/uri@13.4.0":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-x1ozBa5bPbdZCrrTL/HK21qchiK7jYElTu+0ft22abeEhiLYgH1+SIULvOcVk3CK8YwF4kdcidvkq4ciejucJA==
|
||||
}
|
||||
|
||||
"@prisma/client-runtime-utils@7.6.0":
|
||||
resolution:
|
||||
{
|
||||
@@ -555,6 +616,12 @@ packages:
|
||||
"@types/react":
|
||||
optional: true
|
||||
|
||||
"@scure/base@2.0.0":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-3E1kpuZginKkek01ovG8krQ0Z44E3DHPjc5S2rjJw9lZn3KSQOs8S7wqikF/AH7iRanHypj85uGyxk0XAyC37w==
|
||||
}
|
||||
|
||||
"@standard-schema/spec@1.1.0":
|
||||
resolution:
|
||||
{
|
||||
@@ -646,6 +713,18 @@ packages:
|
||||
cpu: [arm64]
|
||||
os: [win32]
|
||||
|
||||
"@types/body-parser@1.19.6":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-HLFeCYgz89uk22N5Qg3dvGvsv46B8GLvKKo1zKG4NybA8U2DiEO3w9lqGg29t/tfLRJpJ6iQxnVw4OnB7MoM9g==
|
||||
}
|
||||
|
||||
"@types/connect@3.4.38":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-K6uROf1LD88uDQqJCktA4yzL1YYAK6NgfsI0v/mTgyPKWsX1CnJ0XPSDhViejru1GcRkLWb8RlzFYJRqGUbaug==
|
||||
}
|
||||
|
||||
"@types/esrecurse@4.3.1":
|
||||
resolution:
|
||||
{
|
||||
@@ -658,6 +737,24 @@ packages:
|
||||
integrity: sha512-dWHzHa2WqEXI/O1E9OjrocMTKJl2mSrEolh1Iomrv6U+JuNwaHXsXx9bLu5gG7BUWFIN0skIQJQ/L1rIex4X6w==
|
||||
}
|
||||
|
||||
"@types/express-serve-static-core@5.1.1":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-v4zIMr/cX7/d2BpAEX3KNKL/JrT1s43s96lLvvdTmza1oEvDudCqK9aF/djc/SWgy8Yh0h30TZx5VpzqFCxk5A==
|
||||
}
|
||||
|
||||
"@types/express@5.0.6":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-sKYVuV7Sv9fbPIt/442koC7+IIwK5olP1KWeD88e/idgoJqDm3JV/YUiPwkoKK92ylff2MGxSz1CSjsXelx0YA==
|
||||
}
|
||||
|
||||
"@types/http-errors@2.0.5":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-r8Tayk8HJnX0FztbZN7oVqGccWgw98T/0neJphO91KkmOzug1KkofZURD4UaD5uH8AqcFLfdPErnBod0u71/qg==
|
||||
}
|
||||
|
||||
"@types/json-schema@7.0.15":
|
||||
resolution:
|
||||
{
|
||||
@@ -682,12 +779,60 @@ packages:
|
||||
integrity: sha512-tO4ZIRKNC+MDWV4qKVZe3Ql/woTnmHDr5JD8UI5hn2pwBrHEwOEMZK7WlNb5RKB6EoJ02gwmQS9OrjuFnZYdpg==
|
||||
}
|
||||
|
||||
"@types/oauth@0.9.6":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-H9TRCVKBNOhZZmyHLqFt9drPM9l+ShWiqqJijU1B8P3DX3ub84NjxDuy+Hjrz+fEca5Kwip3qPMKNyiLgNJtIA==
|
||||
}
|
||||
|
||||
"@types/passport-github2@1.2.9":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-/nMfiPK2E6GKttwBzwj0Wjaot8eHrM57hnWxu52o6becr5/kXlH/4yE2v2rh234WGvSgEEzIII02Nc5oC5xEHA==
|
||||
}
|
||||
|
||||
"@types/passport-oauth2@1.8.0":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-6//z+4orIOy/g3zx17HyQ71GSRK4bs7Sb+zFasRoc2xzlv7ZCJ+vkDBYFci8U6HY+or6Zy7ajf4mz4rK7nsWJQ==
|
||||
}
|
||||
|
||||
"@types/passport@1.0.17":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-aciLyx+wDwT2t2/kJGJR2AEeBz0nJU4WuRX04Wu9Dqc5lSUtwu0WERPHYsLhF9PtseiAMPBGNUOtFjxZ56prsg==
|
||||
}
|
||||
|
||||
"@types/qs@6.15.0":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-JawvT8iBVWpzTrz3EGw9BTQFg3BQNmwERdKE22vlTxawwtbyUSlMppvZYKLZzB5zgACXdXxbD3m1bXaMqP/9ow==
|
||||
}
|
||||
|
||||
"@types/range-parser@1.2.7":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-hKormJbkJqzQGhziax5PItDUTMAM9uE2XXQmM37dyd4hVM+5aVl7oVxMVUiVQn2oCQFN/LKCZdvSM0pFRqbSmQ==
|
||||
}
|
||||
|
||||
"@types/react@19.2.14":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-ilcTH/UniCkMdtexkoCN0bI7pMcJDvmQFPvuPvmEaYA/NSfFTAgdUSLAoVjaRJm7+6PvcM+q1zYOwS4wTYMF9w==
|
||||
}
|
||||
|
||||
"@types/send@1.2.1":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-arsCikDvlU99zl1g69TcAB3mzZPpxgw0UQnaHeC1Nwb015xp8bknZv5rIfri9xTOcMuaVgvabfIRA7PSZVuZIQ==
|
||||
}
|
||||
|
||||
"@types/serve-static@2.2.0":
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-8mam4H1NHLtu7nmtalF7eyBH14QyOASmcxHhSfEoRyr0nP/YdoesEtU+uSRvMe96TW/HPTtkoKqQLl53N7UXMQ==
|
||||
}
|
||||
|
||||
"@types/strip-bom@3.0.0":
|
||||
resolution:
|
||||
{
|
||||
@@ -808,13 +953,6 @@ packages:
|
||||
}
|
||||
engines: { node: 18 || 20 || >=22 }
|
||||
|
||||
base64url@3.0.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-ir1UPr3dkwexU7FdV8qBBbNDRUhMmIekYMFZfi+C/sLNnRESKPl23nB9b2pltqfOQNnGzsDdId90AEtG5tCx4A==
|
||||
}
|
||||
engines: { node: ">=6.0.0" }
|
||||
|
||||
better-result@2.7.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -2030,12 +2168,6 @@ packages:
|
||||
engines: { node: ">=18" }
|
||||
hasBin: true
|
||||
|
||||
oauth@0.10.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-JtFnB+8nxDEXgNyniwz573xxbKSOu3R8D40xQKqcjwJ2CDkYqUDI53o6IuzDJBx60Z8VKCm271+t8iFjakrl8Q==
|
||||
}
|
||||
|
||||
object-assign@4.1.1:
|
||||
resolution:
|
||||
{
|
||||
@@ -2083,6 +2215,12 @@ packages:
|
||||
}
|
||||
engines: { node: ">= 0.8.0" }
|
||||
|
||||
otplib@13.4.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-RUcYcRMCgRWhUE/XabRppXpUwCwaWBNHe5iPXhdvP8wwDGpGpsIf/kxX/ec3zFsOaM1Oq8lEhUqDwk6W7DHkwg==
|
||||
}
|
||||
|
||||
p-limit@3.1.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -2104,34 +2242,6 @@ packages:
|
||||
}
|
||||
engines: { node: ">= 0.8" }
|
||||
|
||||
passport-github2@0.1.12:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-3nPUCc7ttF/3HSP/k9sAXjz3SkGv5Nki84I05kSQPo01Jqq1NzJACgMblCK0fGcv9pKCG/KXU3AJRDGLqHLoIw==
|
||||
}
|
||||
engines: { node: ">= 0.8.0" }
|
||||
|
||||
passport-oauth2@1.8.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-cjsQbOrXIDE4P8nNb3FQRCCmJJ/utnFKEz2NX209f7KOHPoX18gF7gBzBbLLsj2/je4KrgiwLLGjf0lm9rtTBA==
|
||||
}
|
||||
engines: { node: ">= 0.4.0" }
|
||||
|
||||
passport-strategy@1.0.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-CB97UUvDKJde2V0KDWWB3lyf6PC3FaZP7YxZ2G8OAtn9p4HI9j9JLP9qjOGZFvyl8uwNT8qM+hGnz/n16NI7oA==
|
||||
}
|
||||
engines: { node: ">= 0.4.0" }
|
||||
|
||||
passport@0.7.0:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-cPLl+qZpSc+ireUvt+IzqbED1cHHkDoVYMo30jbJIdOOjQ1MQYZBPiNvmi8UM6lJuOpTPXJGZQk0DtC4y61MYQ==
|
||||
}
|
||||
engines: { node: ">= 0.4.0" }
|
||||
|
||||
path-exists@4.0.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -2171,12 +2281,6 @@ packages:
|
||||
integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==
|
||||
}
|
||||
|
||||
pause@0.0.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-KG8UEiEVkR3wGEb4m5yZkVCzigAD+cVEJck2CzYZO37ZGJfctvVptVO192MwrtPhzONn6go8ylnOdMhKqi4nfg==
|
||||
}
|
||||
|
||||
perfect-debounce@1.0.0:
|
||||
resolution:
|
||||
{
|
||||
@@ -2633,6 +2737,13 @@ packages:
|
||||
}
|
||||
engines: { node: ">= 0.4" }
|
||||
|
||||
thirty-two@1.0.2:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-OEI0IWCe+Dw46019YLl6V10Us5bi574EvlJEOcAkB29IzQ/mYD1A6RyNHLjZPiHCmuodxvgF6U+vZO1L15lxVA==
|
||||
}
|
||||
engines: { node: ">=0.2.6" }
|
||||
|
||||
tinyexec@1.0.4:
|
||||
resolution:
|
||||
{
|
||||
@@ -2753,12 +2864,6 @@ packages:
|
||||
engines: { node: ">=14.17" }
|
||||
hasBin: true
|
||||
|
||||
uid2@0.0.4:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-IevTus0SbGwQzYh3+fRsAMTVVPOoIVufzacXcHPmdlle1jUpq7BRL+mw3dgeLanvGZdwwbWhRV6XrcFNdBmjWA==
|
||||
}
|
||||
|
||||
uid@2.0.2:
|
||||
resolution:
|
||||
{
|
||||
@@ -2798,13 +2903,6 @@ packages:
|
||||
integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==
|
||||
}
|
||||
|
||||
utils-merge@1.0.1:
|
||||
resolution:
|
||||
{
|
||||
integrity: sha512-pMZTvIkT1d+TFGvDOqodOclx0QWkkgi6Tdoa8gC8ffGAAqz9pzPTZWAybbsHHoED/ztMtkv/VoYTYyShUn81hA==
|
||||
}
|
||||
engines: { node: ">= 0.4.0" }
|
||||
|
||||
v8-compile-cache-lib@3.0.1:
|
||||
resolution:
|
||||
{
|
||||
@@ -3032,11 +3130,6 @@ snapshots:
|
||||
"@types/jsonwebtoken": 9.0.10
|
||||
jsonwebtoken: 9.0.3
|
||||
|
||||
"@nestjs/passport@11.0.5(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(passport@0.7.0)":
|
||||
dependencies:
|
||||
"@nestjs/common": 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
passport: 0.7.0
|
||||
|
||||
"@nestjs/platform-express@11.1.18(@nestjs/common@11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2))(@nestjs/core@11.1.18)":
|
||||
dependencies:
|
||||
"@nestjs/common": 11.1.18(class-transformer@0.5.1)(class-validator@0.15.1)(reflect-metadata@0.2.2)(rxjs@7.8.2)
|
||||
@@ -3049,10 +3142,56 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
"@noble/hashes@2.0.1": {}
|
||||
|
||||
"@nuxt/opencollective@0.4.1":
|
||||
dependencies:
|
||||
consola: 3.4.2
|
||||
|
||||
"@otplib/core@12.0.1": {}
|
||||
|
||||
"@otplib/core@13.4.0": {}
|
||||
|
||||
"@otplib/hotp@13.4.0":
|
||||
dependencies:
|
||||
"@otplib/core": 13.4.0
|
||||
"@otplib/uri": 13.4.0
|
||||
|
||||
"@otplib/plugin-base32-scure@13.4.0":
|
||||
dependencies:
|
||||
"@otplib/core": 13.4.0
|
||||
"@scure/base": 2.0.0
|
||||
|
||||
"@otplib/plugin-crypto-noble@13.4.0":
|
||||
dependencies:
|
||||
"@noble/hashes": 2.0.1
|
||||
"@otplib/core": 13.4.0
|
||||
|
||||
"@otplib/plugin-crypto@12.0.1":
|
||||
dependencies:
|
||||
"@otplib/core": 12.0.1
|
||||
|
||||
"@otplib/plugin-thirty-two@12.0.1":
|
||||
dependencies:
|
||||
"@otplib/core": 12.0.1
|
||||
thirty-two: 1.0.2
|
||||
|
||||
"@otplib/preset-default@12.0.1":
|
||||
dependencies:
|
||||
"@otplib/core": 12.0.1
|
||||
"@otplib/plugin-crypto": 12.0.1
|
||||
"@otplib/plugin-thirty-two": 12.0.1
|
||||
|
||||
"@otplib/totp@13.4.0":
|
||||
dependencies:
|
||||
"@otplib/core": 13.4.0
|
||||
"@otplib/hotp": 13.4.0
|
||||
"@otplib/uri": 13.4.0
|
||||
|
||||
"@otplib/uri@13.4.0":
|
||||
dependencies:
|
||||
"@otplib/core": 13.4.0
|
||||
|
||||
"@prisma/client-runtime-utils@7.6.0": {}
|
||||
|
||||
"@prisma/client@7.6.0(prisma@7.6.0(@types/react@19.2.14)(react-dom@19.2.4(react@19.2.4))(react@19.2.4)(typescript@5.9.3))(typescript@5.9.3)":
|
||||
@@ -3193,6 +3332,8 @@ snapshots:
|
||||
optionalDependencies:
|
||||
"@types/react": 19.2.14
|
||||
|
||||
"@scure/base@2.0.0": {}
|
||||
|
||||
"@standard-schema/spec@1.1.0": {}
|
||||
|
||||
"@tokenizer/inflate@0.4.1":
|
||||
@@ -3230,10 +3371,34 @@ snapshots:
|
||||
"@turbo/windows-arm64@2.9.3":
|
||||
optional: true
|
||||
|
||||
"@types/body-parser@1.19.6":
|
||||
dependencies:
|
||||
"@types/connect": 3.4.38
|
||||
"@types/node": 25.5.2
|
||||
|
||||
"@types/connect@3.4.38":
|
||||
dependencies:
|
||||
"@types/node": 25.5.2
|
||||
|
||||
"@types/esrecurse@4.3.1": {}
|
||||
|
||||
"@types/estree@1.0.8": {}
|
||||
|
||||
"@types/express-serve-static-core@5.1.1":
|
||||
dependencies:
|
||||
"@types/node": 25.5.2
|
||||
"@types/qs": 6.15.0
|
||||
"@types/range-parser": 1.2.7
|
||||
"@types/send": 1.2.1
|
||||
|
||||
"@types/express@5.0.6":
|
||||
dependencies:
|
||||
"@types/body-parser": 1.19.6
|
||||
"@types/express-serve-static-core": 5.1.1
|
||||
"@types/serve-static": 2.2.0
|
||||
|
||||
"@types/http-errors@2.0.5": {}
|
||||
|
||||
"@types/json-schema@7.0.15": {}
|
||||
|
||||
"@types/jsonwebtoken@9.0.10":
|
||||
@@ -3247,10 +3412,43 @@ snapshots:
|
||||
dependencies:
|
||||
undici-types: 7.18.2
|
||||
|
||||
"@types/oauth@0.9.6":
|
||||
dependencies:
|
||||
"@types/node": 25.5.2
|
||||
|
||||
"@types/passport-github2@1.2.9":
|
||||
dependencies:
|
||||
"@types/express": 5.0.6
|
||||
"@types/passport": 1.0.17
|
||||
"@types/passport-oauth2": 1.8.0
|
||||
|
||||
"@types/passport-oauth2@1.8.0":
|
||||
dependencies:
|
||||
"@types/express": 5.0.6
|
||||
"@types/oauth": 0.9.6
|
||||
"@types/passport": 1.0.17
|
||||
|
||||
"@types/passport@1.0.17":
|
||||
dependencies:
|
||||
"@types/express": 5.0.6
|
||||
|
||||
"@types/qs@6.15.0": {}
|
||||
|
||||
"@types/range-parser@1.2.7": {}
|
||||
|
||||
"@types/react@19.2.14":
|
||||
dependencies:
|
||||
csstype: 3.2.3
|
||||
|
||||
"@types/send@1.2.1":
|
||||
dependencies:
|
||||
"@types/node": 25.5.2
|
||||
|
||||
"@types/serve-static@2.2.0":
|
||||
dependencies:
|
||||
"@types/http-errors": 2.0.5
|
||||
"@types/node": 25.5.2
|
||||
|
||||
"@types/strip-bom@3.0.0": {}
|
||||
|
||||
"@types/strip-json-comments@0.0.30": {}
|
||||
@@ -3309,8 +3507,6 @@ snapshots:
|
||||
|
||||
balanced-match@4.0.4: {}
|
||||
|
||||
base64url@3.0.1: {}
|
||||
|
||||
better-result@2.7.0:
|
||||
dependencies:
|
||||
"@clack/prompts": 0.11.0
|
||||
@@ -4007,8 +4203,6 @@ snapshots:
|
||||
pathe: 2.0.3
|
||||
tinyexec: 1.0.4
|
||||
|
||||
oauth@0.10.2: {}
|
||||
|
||||
object-assign@4.1.1: {}
|
||||
|
||||
object-inspect@1.13.4: {}
|
||||
@@ -4036,6 +4230,15 @@ snapshots:
|
||||
type-check: 0.4.0
|
||||
word-wrap: 1.2.5
|
||||
|
||||
otplib@13.4.0:
|
||||
dependencies:
|
||||
"@otplib/core": 13.4.0
|
||||
"@otplib/hotp": 13.4.0
|
||||
"@otplib/plugin-base32-scure": 13.4.0
|
||||
"@otplib/plugin-crypto-noble": 13.4.0
|
||||
"@otplib/totp": 13.4.0
|
||||
"@otplib/uri": 13.4.0
|
||||
|
||||
p-limit@3.1.0:
|
||||
dependencies:
|
||||
yocto-queue: 0.1.0
|
||||
@@ -4046,26 +4249,6 @@ snapshots:
|
||||
|
||||
parseurl@1.3.3: {}
|
||||
|
||||
passport-github2@0.1.12:
|
||||
dependencies:
|
||||
passport-oauth2: 1.8.0
|
||||
|
||||
passport-oauth2@1.8.0:
|
||||
dependencies:
|
||||
base64url: 3.0.1
|
||||
oauth: 0.10.2
|
||||
passport-strategy: 1.0.0
|
||||
uid2: 0.0.4
|
||||
utils-merge: 1.0.1
|
||||
|
||||
passport-strategy@1.0.0: {}
|
||||
|
||||
passport@0.7.0:
|
||||
dependencies:
|
||||
passport-strategy: 1.0.0
|
||||
pause: 0.0.1
|
||||
utils-merge: 1.0.1
|
||||
|
||||
path-exists@4.0.0: {}
|
||||
|
||||
path-is-absolute@1.0.1: {}
|
||||
@@ -4078,8 +4261,6 @@ snapshots:
|
||||
|
||||
pathe@2.0.3: {}
|
||||
|
||||
pause@0.0.1: {}
|
||||
|
||||
perfect-debounce@1.0.0: {}
|
||||
|
||||
picocolors@1.1.1: {}
|
||||
@@ -4341,6 +4522,8 @@ snapshots:
|
||||
|
||||
supports-preserve-symlinks-flag@1.0.0: {}
|
||||
|
||||
thirty-two@1.0.2: {}
|
||||
|
||||
tinyexec@1.0.4: {}
|
||||
|
||||
to-regex-range@5.0.1:
|
||||
@@ -4430,8 +4613,6 @@ snapshots:
|
||||
|
||||
typescript@5.9.3: {}
|
||||
|
||||
uid2@0.0.4: {}
|
||||
|
||||
uid@2.0.2:
|
||||
dependencies:
|
||||
"@lukeed/csprng": 1.1.0
|
||||
@@ -4448,8 +4629,6 @@ snapshots:
|
||||
|
||||
util-deprecate@1.0.2: {}
|
||||
|
||||
utils-merge@1.0.1: {}
|
||||
|
||||
v8-compile-cache-lib@3.0.1: {}
|
||||
|
||||
valibot@1.2.0(typescript@5.9.3):
|
||||
|
||||
Reference in New Issue
Block a user