create-forgeon 0.3.16 → 0.3.17
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/package.json +1 -1
- package/src/cli/add-options.test.mjs +5 -2
- package/src/cli/options.test.mjs +1 -0
- package/src/cli/prompt-select.test.mjs +1 -0
- package/src/core/docs.test.mjs +1 -0
- package/src/core/scaffold.test.mjs +1 -0
- package/src/core/validate.test.mjs +1 -0
- package/src/modules/accounts.mjs +416 -0
- package/src/modules/dependencies.test.mjs +71 -29
- package/src/modules/executor.mjs +3 -2
- package/src/modules/executor.test.mjs +512 -477
- package/src/modules/files-access.mjs +9 -7
- package/src/modules/files-image.mjs +9 -7
- package/src/modules/files-local.mjs +15 -6
- package/src/modules/files-quotas.mjs +8 -6
- package/src/modules/files-s3.mjs +17 -6
- package/src/modules/files.mjs +21 -21
- package/src/modules/idempotency.test.mjs +13 -7
- package/src/modules/probes.test.mjs +4 -2
- package/src/modules/queue.mjs +9 -6
- package/src/modules/rate-limit.mjs +14 -10
- package/src/modules/rbac.mjs +12 -11
- package/src/modules/registry.mjs +22 -35
- package/src/modules/scheduler.mjs +9 -6
- package/src/modules/shared/files-runtime-wiring.mjs +81 -0
- package/src/modules/shared/patch-utils.mjs +29 -1
- package/src/modules/sync-integrations.mjs +102 -422
- package/src/modules/sync-integrations.test.mjs +32 -111
- package/src/run-add-module.test.mjs +1 -0
- package/templates/base/scripts/forgeon-sync-integrations.mjs +65 -325
- package/templates/module-fragments/{jwt-auth → accounts}/00_title.md +2 -1
- package/templates/module-fragments/{jwt-auth → accounts}/10_overview.md +5 -5
- package/templates/module-fragments/accounts/20_scope.md +29 -0
- package/templates/module-fragments/accounts/90_status_implemented.md +8 -0
- package/templates/module-fragments/accounts/90_status_planned.md +7 -0
- package/templates/module-fragments/rbac/30_what_it_adds.md +3 -2
- package/templates/module-fragments/rbac/40_how_it_works.md +2 -1
- package/templates/module-fragments/rbac/50_how_to_use.md +2 -1
- package/templates/module-fragments/swagger/20_scope.md +2 -1
- package/templates/module-presets/accounts/apps/api/prisma/migrations/0002_accounts_core/migration.sql +97 -0
- package/templates/module-presets/accounts/apps/api/src/accounts/forgeon-accounts-db-prisma.module.ts +17 -0
- package/templates/module-presets/accounts/apps/api/src/accounts/prisma-accounts-persistence.store.ts +332 -0
- package/templates/module-presets/{jwt-auth/packages/auth-api → accounts/packages/accounts-api}/package.json +5 -5
- package/templates/module-presets/accounts/packages/accounts-api/src/accounts-email.port.ts +13 -0
- package/templates/module-presets/accounts/packages/accounts-api/src/accounts-persistence.port.ts +67 -0
- package/templates/module-presets/accounts/packages/accounts-api/src/accounts-rbac.port.ts +14 -0
- package/templates/module-presets/{jwt-auth/packages/auth-api → accounts/packages/accounts-api}/src/auth-config.loader.ts +7 -7
- package/templates/module-presets/{jwt-auth/packages/auth-api → accounts/packages/accounts-api}/src/auth-config.service.ts +7 -7
- package/templates/module-presets/accounts/packages/accounts-api/src/auth-core.service.ts +318 -0
- package/templates/module-presets/{jwt-auth/packages/auth-api → accounts/packages/accounts-api}/src/auth-env.schema.ts +4 -4
- package/templates/module-presets/accounts/packages/accounts-api/src/auth-jwt.service.ts +58 -0
- package/templates/module-presets/accounts/packages/accounts-api/src/auth-password.service.ts +21 -0
- package/templates/module-presets/accounts/packages/accounts-api/src/auth.controller.ts +93 -0
- package/templates/module-presets/accounts/packages/accounts-api/src/auth.service.ts +48 -0
- package/templates/module-presets/accounts/packages/accounts-api/src/auth.types.ts +17 -0
- package/templates/module-presets/accounts/packages/accounts-api/src/dto/change-password.dto.ts +13 -0
- package/templates/module-presets/accounts/packages/accounts-api/src/dto/confirm-password-reset.dto.ts +12 -0
- package/templates/module-presets/accounts/packages/accounts-api/src/dto/index.ts +10 -0
- package/templates/module-presets/{jwt-auth/packages/auth-api → accounts/packages/accounts-api}/src/dto/login.dto.ts +1 -1
- package/templates/module-presets/{jwt-auth/packages/auth-api → accounts/packages/accounts-api}/src/dto/refresh.dto.ts +1 -1
- package/templates/module-presets/accounts/packages/accounts-api/src/dto/register.dto.ts +23 -0
- package/templates/module-presets/accounts/packages/accounts-api/src/dto/request-password-reset.dto.ts +7 -0
- package/templates/module-presets/accounts/packages/accounts-api/src/dto/update-user-profile.dto.ts +16 -0
- package/templates/module-presets/accounts/packages/accounts-api/src/dto/update-user-settings.dto.ts +16 -0
- package/templates/module-presets/accounts/packages/accounts-api/src/dto/update-user.dto.ts +8 -0
- package/templates/module-presets/accounts/packages/accounts-api/src/dto/verify-email.dto.ts +8 -0
- package/templates/module-presets/accounts/packages/accounts-api/src/forgeon-accounts.module.ts +82 -0
- package/templates/module-presets/accounts/packages/accounts-api/src/index.ts +24 -0
- package/templates/module-presets/{jwt-auth/packages/auth-api → accounts/packages/accounts-api}/src/jwt.strategy.ts +3 -3
- package/templates/module-presets/accounts/packages/accounts-api/src/owner-access.guard.ts +39 -0
- package/templates/module-presets/accounts/packages/accounts-api/src/users-config.ts +13 -0
- package/templates/module-presets/accounts/packages/accounts-api/src/users.controller.ts +65 -0
- package/templates/module-presets/accounts/packages/accounts-api/src/users.service.ts +87 -0
- package/templates/module-presets/accounts/packages/accounts-api/src/users.types.ts +65 -0
- package/templates/module-presets/{jwt-auth/packages/auth-contracts → accounts/packages/accounts-contracts}/package.json +1 -1
- package/templates/module-presets/accounts/packages/accounts-contracts/src/index.ts +119 -0
- package/templates/module-presets/files/apps/api/src/files/forgeon-files-db-prisma.module.ts +17 -0
- package/templates/module-presets/files/apps/api/src/files/prisma-files-persistence.store.ts +164 -0
- package/templates/module-presets/files/packages/files/package.json +1 -2
- package/templates/module-presets/files/packages/files/src/files.ports.ts +107 -0
- package/templates/module-presets/files/packages/files/src/files.service.ts +81 -395
- package/templates/module-presets/files/packages/files/src/forgeon-files.module.ts +126 -2
- package/templates/module-presets/files/packages/files/src/index.ts +2 -1
- package/templates/module-presets/files-local/packages/files-local/src/forgeon-files-local-storage.module.ts +18 -0
- package/templates/module-presets/files-local/packages/files-local/src/index.ts +2 -0
- package/templates/module-presets/files-local/packages/files-local/src/local-files-storage.adapter.ts +53 -0
- package/templates/module-presets/files-s3/packages/files-s3/src/forgeon-files-s3-storage.module.ts +18 -0
- package/templates/module-presets/files-s3/packages/files-s3/src/index.ts +2 -0
- package/templates/module-presets/files-s3/packages/files-s3/src/s3-files-storage.adapter.ts +130 -0
- package/src/modules/jwt-auth.mjs +0 -271
- package/templates/module-fragments/jwt-auth/20_scope.md +0 -19
- package/templates/module-fragments/jwt-auth/90_status_implemented.md +0 -8
- package/templates/module-fragments/jwt-auth/90_status_planned.md +0 -3
- package/templates/module-presets/jwt-auth/apps/api/prisma/migrations/0002_auth_refresh_token_hash/migration.sql +0 -3
- package/templates/module-presets/jwt-auth/apps/api/src/auth/prisma-auth-refresh-token.store.ts +0 -36
- package/templates/module-presets/jwt-auth/packages/auth-api/src/auth-refresh-token.store.ts +0 -23
- package/templates/module-presets/jwt-auth/packages/auth-api/src/auth.controller.ts +0 -71
- package/templates/module-presets/jwt-auth/packages/auth-api/src/auth.service.ts +0 -175
- package/templates/module-presets/jwt-auth/packages/auth-api/src/auth.types.ts +0 -6
- package/templates/module-presets/jwt-auth/packages/auth-api/src/dto/index.ts +0 -2
- package/templates/module-presets/jwt-auth/packages/auth-api/src/forgeon-auth.module.ts +0 -47
- package/templates/module-presets/jwt-auth/packages/auth-api/src/index.ts +0 -12
- package/templates/module-presets/jwt-auth/packages/auth-contracts/src/index.ts +0 -47
- /package/templates/module-presets/{jwt-auth/packages/auth-api/src/jwt-auth.guard.ts → accounts/packages/accounts-api/src/access-token.guard.ts} +0 -0
- /package/templates/module-presets/{jwt-auth/packages/auth-api → accounts/packages/accounts-api}/src/auth-config.module.ts +0 -0
- /package/templates/module-presets/{jwt-auth/packages/auth-api → accounts/packages/accounts-api}/tsconfig.json +0 -0
- /package/templates/module-presets/{jwt-auth/packages/auth-contracts → accounts/packages/accounts-contracts}/tsconfig.json +0 -0
package/templates/module-presets/jwt-auth/apps/api/src/auth/prisma-auth-refresh-token.store.ts
DELETED
|
@@ -1,36 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
AuthRefreshTokenStore,
|
|
3
|
-
} from '@forgeon/auth-api';
|
|
4
|
-
import { PrismaService } from '@forgeon/db-prisma';
|
|
5
|
-
import { Injectable } from '@nestjs/common';
|
|
6
|
-
|
|
7
|
-
@Injectable()
|
|
8
|
-
export class PrismaAuthRefreshTokenStore implements AuthRefreshTokenStore {
|
|
9
|
-
readonly kind = 'prisma';
|
|
10
|
-
|
|
11
|
-
constructor(private readonly prisma: PrismaService) {}
|
|
12
|
-
|
|
13
|
-
async saveRefreshTokenHash(subject: string, hash: string): Promise<void> {
|
|
14
|
-
await this.prisma.user.upsert({
|
|
15
|
-
where: { email: subject },
|
|
16
|
-
create: { email: subject, refreshTokenHash: hash },
|
|
17
|
-
update: { refreshTokenHash: hash },
|
|
18
|
-
select: { id: true },
|
|
19
|
-
});
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async getRefreshTokenHash(subject: string): Promise<string | null> {
|
|
23
|
-
const user = await this.prisma.user.findUnique({
|
|
24
|
-
where: { email: subject },
|
|
25
|
-
select: { refreshTokenHash: true },
|
|
26
|
-
});
|
|
27
|
-
return user?.refreshTokenHash ?? null;
|
|
28
|
-
}
|
|
29
|
-
|
|
30
|
-
async removeRefreshTokenHash(subject: string): Promise<void> {
|
|
31
|
-
await this.prisma.user.updateMany({
|
|
32
|
-
where: { email: subject },
|
|
33
|
-
data: { refreshTokenHash: null },
|
|
34
|
-
});
|
|
35
|
-
}
|
|
36
|
-
}
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { Injectable } from '@nestjs/common';
|
|
2
|
-
|
|
3
|
-
export const AUTH_REFRESH_TOKEN_STORE = Symbol('AUTH_REFRESH_TOKEN_STORE');
|
|
4
|
-
|
|
5
|
-
export interface AuthRefreshTokenStore {
|
|
6
|
-
readonly kind: string;
|
|
7
|
-
saveRefreshTokenHash(subject: string, hash: string): Promise<void>;
|
|
8
|
-
getRefreshTokenHash(subject: string): Promise<string | null>;
|
|
9
|
-
removeRefreshTokenHash(subject: string): Promise<void>;
|
|
10
|
-
}
|
|
11
|
-
|
|
12
|
-
@Injectable()
|
|
13
|
-
export class NoopAuthRefreshTokenStore implements AuthRefreshTokenStore {
|
|
14
|
-
readonly kind = 'none';
|
|
15
|
-
|
|
16
|
-
async saveRefreshTokenHash(): Promise<void> {}
|
|
17
|
-
|
|
18
|
-
async getRefreshTokenHash(): Promise<string | null> {
|
|
19
|
-
return null;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
async removeRefreshTokenHash(): Promise<void> {}
|
|
23
|
-
}
|
|
@@ -1,71 +0,0 @@
|
|
|
1
|
-
import type { AuthUser } from '@forgeon/auth-contracts';
|
|
2
|
-
import {
|
|
3
|
-
Body,
|
|
4
|
-
Controller,
|
|
5
|
-
Get,
|
|
6
|
-
Post,
|
|
7
|
-
Req,
|
|
8
|
-
UseGuards,
|
|
9
|
-
} from '@nestjs/common';
|
|
10
|
-
import { AuthService } from './auth.service';
|
|
11
|
-
import { LoginDto, RefreshDto } from './dto';
|
|
12
|
-
import { JwtAuthGuard } from './jwt-auth.guard';
|
|
13
|
-
import { AuthJwtPayload } from './auth.types';
|
|
14
|
-
|
|
15
|
-
type RequestWithUser = { user?: AuthJwtPayload };
|
|
16
|
-
|
|
17
|
-
@Controller('auth')
|
|
18
|
-
export class AuthController {
|
|
19
|
-
constructor(private readonly authService: AuthService) {}
|
|
20
|
-
|
|
21
|
-
@Post('login')
|
|
22
|
-
login(@Body() body: LoginDto) {
|
|
23
|
-
return this.authService.login(body);
|
|
24
|
-
}
|
|
25
|
-
|
|
26
|
-
@Post('refresh')
|
|
27
|
-
refresh(@Body() body: RefreshDto) {
|
|
28
|
-
return this.authService.refresh(body);
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
@UseGuards(JwtAuthGuard)
|
|
32
|
-
@Post('logout')
|
|
33
|
-
async logout(@Req() request: RequestWithUser) {
|
|
34
|
-
const user = this.getRequestUser(request);
|
|
35
|
-
await this.authService.logout(user);
|
|
36
|
-
return {
|
|
37
|
-
status: 'ok',
|
|
38
|
-
tokenStore: this.authService.getTokenStoreKind(),
|
|
39
|
-
};
|
|
40
|
-
}
|
|
41
|
-
|
|
42
|
-
@UseGuards(JwtAuthGuard)
|
|
43
|
-
@Get('me')
|
|
44
|
-
me(@Req() request: RequestWithUser) {
|
|
45
|
-
const user = this.getRequestUser(request);
|
|
46
|
-
return {
|
|
47
|
-
user: this.toAuthUser(user),
|
|
48
|
-
tokenStore: this.authService.getTokenStoreKind(),
|
|
49
|
-
};
|
|
50
|
-
}
|
|
51
|
-
|
|
52
|
-
private getRequestUser(request: RequestWithUser): AuthJwtPayload {
|
|
53
|
-
const user = request.user;
|
|
54
|
-
if (!user || typeof user.sub !== 'string' || typeof user.email !== 'string') {
|
|
55
|
-
return {
|
|
56
|
-
sub: 'unknown',
|
|
57
|
-
email: 'unknown@invalid.local',
|
|
58
|
-
roles: ['user'],
|
|
59
|
-
};
|
|
60
|
-
}
|
|
61
|
-
return user;
|
|
62
|
-
}
|
|
63
|
-
|
|
64
|
-
private toAuthUser(payload: AuthJwtPayload): AuthUser {
|
|
65
|
-
return {
|
|
66
|
-
sub: payload.sub,
|
|
67
|
-
email: payload.email,
|
|
68
|
-
roles: Array.isArray(payload.roles) ? payload.roles : ['user'],
|
|
69
|
-
};
|
|
70
|
-
}
|
|
71
|
-
}
|
|
@@ -1,175 +0,0 @@
|
|
|
1
|
-
import type {
|
|
2
|
-
AuthErrorCode,
|
|
3
|
-
AuthUser,
|
|
4
|
-
LoginResponse,
|
|
5
|
-
RefreshResponse,
|
|
6
|
-
TokenPair,
|
|
7
|
-
} from '@forgeon/auth-contracts';
|
|
8
|
-
import { Inject, Injectable, UnauthorizedException } from '@nestjs/common';
|
|
9
|
-
import { JwtService } from '@nestjs/jwt';
|
|
10
|
-
import type { JwtSignOptions } from '@nestjs/jwt';
|
|
11
|
-
import { compare, hash } from 'bcryptjs';
|
|
12
|
-
import {
|
|
13
|
-
AUTH_REFRESH_TOKEN_STORE,
|
|
14
|
-
AuthRefreshTokenStore,
|
|
15
|
-
} from './auth-refresh-token.store';
|
|
16
|
-
import { AuthConfigService } from './auth-config.service';
|
|
17
|
-
import { LoginDto, RefreshDto } from './dto';
|
|
18
|
-
import { AuthJwtPayload } from './auth.types';
|
|
19
|
-
|
|
20
|
-
type JwtExpiresIn = NonNullable<JwtSignOptions['expiresIn']>;
|
|
21
|
-
|
|
22
|
-
const AUTH_ERROR_CODES: Record<
|
|
23
|
-
'invalidCredentials' | 'tokenExpired' | 'refreshInvalid',
|
|
24
|
-
AuthErrorCode
|
|
25
|
-
> = {
|
|
26
|
-
invalidCredentials: 'AUTH_INVALID_CREDENTIALS',
|
|
27
|
-
tokenExpired: 'AUTH_TOKEN_EXPIRED',
|
|
28
|
-
refreshInvalid: 'AUTH_REFRESH_INVALID',
|
|
29
|
-
};
|
|
30
|
-
|
|
31
|
-
@Injectable()
|
|
32
|
-
export class AuthService {
|
|
33
|
-
constructor(
|
|
34
|
-
private readonly jwtService: JwtService,
|
|
35
|
-
private readonly configService: AuthConfigService,
|
|
36
|
-
@Inject(AUTH_REFRESH_TOKEN_STORE)
|
|
37
|
-
private readonly refreshTokenStore: AuthRefreshTokenStore,
|
|
38
|
-
) {}
|
|
39
|
-
|
|
40
|
-
getTokenStoreKind(): string {
|
|
41
|
-
return this.refreshTokenStore.kind;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
async login(input: LoginDto): Promise<LoginResponse> {
|
|
45
|
-
const email = input.email.trim().toLowerCase();
|
|
46
|
-
if (email !== this.configService.demoEmail || input.password !== this.configService.demoPassword) {
|
|
47
|
-
throw new UnauthorizedException({
|
|
48
|
-
message: 'Invalid credentials',
|
|
49
|
-
details: { code: AUTH_ERROR_CODES.invalidCredentials },
|
|
50
|
-
});
|
|
51
|
-
}
|
|
52
|
-
|
|
53
|
-
const user: AuthUser = {
|
|
54
|
-
sub: email,
|
|
55
|
-
email,
|
|
56
|
-
roles: ['user'],
|
|
57
|
-
};
|
|
58
|
-
|
|
59
|
-
const tokens = await this.issueTokens(user);
|
|
60
|
-
return {
|
|
61
|
-
...tokens,
|
|
62
|
-
user,
|
|
63
|
-
tokenStore: this.refreshTokenStore.kind,
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
|
|
67
|
-
async refresh(input: RefreshDto): Promise<RefreshResponse> {
|
|
68
|
-
let payload: AuthJwtPayload;
|
|
69
|
-
try {
|
|
70
|
-
payload = await this.jwtService.verifyAsync<AuthJwtPayload>(input.refreshToken, {
|
|
71
|
-
secret: this.configService.refreshSecret,
|
|
72
|
-
});
|
|
73
|
-
} catch (error) {
|
|
74
|
-
const code =
|
|
75
|
-
error instanceof Error && error.name === 'TokenExpiredError'
|
|
76
|
-
? AUTH_ERROR_CODES.tokenExpired
|
|
77
|
-
: AUTH_ERROR_CODES.refreshInvalid;
|
|
78
|
-
|
|
79
|
-
throw new UnauthorizedException({
|
|
80
|
-
message: 'Refresh token is invalid or expired',
|
|
81
|
-
details: { code },
|
|
82
|
-
});
|
|
83
|
-
}
|
|
84
|
-
|
|
85
|
-
if (this.refreshTokenStore.kind !== 'none') {
|
|
86
|
-
const storedHash = await this.refreshTokenStore.getRefreshTokenHash(payload.sub);
|
|
87
|
-
if (!storedHash) {
|
|
88
|
-
throw new UnauthorizedException({
|
|
89
|
-
message: 'Refresh token is invalid or expired',
|
|
90
|
-
details: { code: AUTH_ERROR_CODES.refreshInvalid },
|
|
91
|
-
});
|
|
92
|
-
}
|
|
93
|
-
|
|
94
|
-
const matched = await compare(input.refreshToken, storedHash);
|
|
95
|
-
if (!matched) {
|
|
96
|
-
throw new UnauthorizedException({
|
|
97
|
-
message: 'Refresh token is invalid or expired',
|
|
98
|
-
details: { code: AUTH_ERROR_CODES.refreshInvalid },
|
|
99
|
-
});
|
|
100
|
-
}
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
const user = this.toAuthUser(payload);
|
|
104
|
-
const tokens = await this.issueTokens(user);
|
|
105
|
-
return {
|
|
106
|
-
...tokens,
|
|
107
|
-
user,
|
|
108
|
-
tokenStore: this.refreshTokenStore.kind,
|
|
109
|
-
};
|
|
110
|
-
}
|
|
111
|
-
|
|
112
|
-
async logout(user: AuthJwtPayload): Promise<void> {
|
|
113
|
-
if (this.refreshTokenStore.kind === 'none') {
|
|
114
|
-
return;
|
|
115
|
-
}
|
|
116
|
-
await this.refreshTokenStore.removeRefreshTokenHash(user.sub);
|
|
117
|
-
}
|
|
118
|
-
|
|
119
|
-
getProbeStatus() {
|
|
120
|
-
return {
|
|
121
|
-
status: 'ok',
|
|
122
|
-
feature: 'jwt-auth',
|
|
123
|
-
tokenStore: this.refreshTokenStore.kind,
|
|
124
|
-
demoEmail: this.configService.demoEmail,
|
|
125
|
-
};
|
|
126
|
-
}
|
|
127
|
-
|
|
128
|
-
private async issueTokens(user: AuthUser): Promise<TokenPair> {
|
|
129
|
-
const payload: AuthJwtPayload = {
|
|
130
|
-
sub: user.sub,
|
|
131
|
-
email: user.email,
|
|
132
|
-
roles: user.roles,
|
|
133
|
-
};
|
|
134
|
-
|
|
135
|
-
const [accessToken, refreshToken] = await Promise.all([
|
|
136
|
-
this.jwtService.signAsync(payload, {
|
|
137
|
-
secret: this.configService.accessSecret,
|
|
138
|
-
expiresIn: this.toJwtExpiresIn(this.configService.accessExpiresIn),
|
|
139
|
-
}),
|
|
140
|
-
this.jwtService.signAsync(payload, {
|
|
141
|
-
secret: this.configService.refreshSecret,
|
|
142
|
-
expiresIn: this.toJwtExpiresIn(this.configService.refreshExpiresIn),
|
|
143
|
-
}),
|
|
144
|
-
]);
|
|
145
|
-
|
|
146
|
-
if (this.refreshTokenStore.kind !== 'none') {
|
|
147
|
-
const tokenHash = await hash(refreshToken, this.configService.bcryptRounds);
|
|
148
|
-
await this.refreshTokenStore.saveRefreshTokenHash(user.sub, tokenHash);
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
return {
|
|
152
|
-
accessToken,
|
|
153
|
-
refreshToken,
|
|
154
|
-
tokenType: 'Bearer',
|
|
155
|
-
accessTtl: this.configService.accessExpiresIn,
|
|
156
|
-
refreshTtl: this.configService.refreshExpiresIn,
|
|
157
|
-
};
|
|
158
|
-
}
|
|
159
|
-
|
|
160
|
-
private toAuthUser(payload: AuthJwtPayload): AuthUser {
|
|
161
|
-
return {
|
|
162
|
-
sub: payload.sub,
|
|
163
|
-
email: payload.email,
|
|
164
|
-
roles: Array.isArray(payload.roles) ? payload.roles : ['user'],
|
|
165
|
-
};
|
|
166
|
-
}
|
|
167
|
-
|
|
168
|
-
private toJwtExpiresIn(value: string): JwtExpiresIn {
|
|
169
|
-
const trimmed = value.trim();
|
|
170
|
-
if (/^\d+$/.test(trimmed)) {
|
|
171
|
-
return Number(trimmed);
|
|
172
|
-
}
|
|
173
|
-
return trimmed as JwtExpiresIn;
|
|
174
|
-
}
|
|
175
|
-
}
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
import {
|
|
2
|
-
DynamicModule,
|
|
3
|
-
Module,
|
|
4
|
-
ModuleMetadata,
|
|
5
|
-
Provider,
|
|
6
|
-
} from '@nestjs/common';
|
|
7
|
-
import { JwtModule } from '@nestjs/jwt';
|
|
8
|
-
import { PassportModule } from '@nestjs/passport';
|
|
9
|
-
import {
|
|
10
|
-
AUTH_REFRESH_TOKEN_STORE,
|
|
11
|
-
NoopAuthRefreshTokenStore,
|
|
12
|
-
} from './auth-refresh-token.store';
|
|
13
|
-
import { AuthConfigModule } from './auth-config.module';
|
|
14
|
-
import { AuthController } from './auth.controller';
|
|
15
|
-
import { AuthService } from './auth.service';
|
|
16
|
-
import { JwtAuthGuard } from './jwt-auth.guard';
|
|
17
|
-
import { JwtStrategy } from './jwt.strategy';
|
|
18
|
-
|
|
19
|
-
export interface ForgeonAuthModuleOptions {
|
|
20
|
-
imports?: ModuleMetadata['imports'];
|
|
21
|
-
refreshTokenStoreProvider?: Provider;
|
|
22
|
-
}
|
|
23
|
-
|
|
24
|
-
@Module({})
|
|
25
|
-
export class ForgeonAuthModule {
|
|
26
|
-
static register(options: ForgeonAuthModuleOptions = {}): DynamicModule {
|
|
27
|
-
const refreshTokenStoreProvider =
|
|
28
|
-
options.refreshTokenStoreProvider ??
|
|
29
|
-
({
|
|
30
|
-
provide: AUTH_REFRESH_TOKEN_STORE,
|
|
31
|
-
useClass: NoopAuthRefreshTokenStore,
|
|
32
|
-
} satisfies Provider);
|
|
33
|
-
|
|
34
|
-
return {
|
|
35
|
-
module: ForgeonAuthModule,
|
|
36
|
-
imports: [
|
|
37
|
-
AuthConfigModule,
|
|
38
|
-
PassportModule.register({ defaultStrategy: 'jwt' }),
|
|
39
|
-
JwtModule.register({}),
|
|
40
|
-
...(options.imports ?? []),
|
|
41
|
-
],
|
|
42
|
-
controllers: [AuthController],
|
|
43
|
-
providers: [AuthService, JwtStrategy, JwtAuthGuard, refreshTokenStoreProvider],
|
|
44
|
-
exports: [AuthConfigModule, AuthService, JwtAuthGuard, AUTH_REFRESH_TOKEN_STORE],
|
|
45
|
-
};
|
|
46
|
-
}
|
|
47
|
-
}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
export * from './auth-env.schema';
|
|
2
|
-
export * from './auth-config.loader';
|
|
3
|
-
export * from './auth-config.module';
|
|
4
|
-
export * from './auth-config.service';
|
|
5
|
-
export * from './auth-refresh-token.store';
|
|
6
|
-
export * from './auth.controller';
|
|
7
|
-
export * from './auth.service';
|
|
8
|
-
export * from './auth.types';
|
|
9
|
-
export * from './dto';
|
|
10
|
-
export * from './forgeon-auth.module';
|
|
11
|
-
export * from './jwt-auth.guard';
|
|
12
|
-
export * from './jwt.strategy';
|
|
@@ -1,47 +0,0 @@
|
|
|
1
|
-
export const AUTH_API_ROUTES = {
|
|
2
|
-
login: '/api/auth/login',
|
|
3
|
-
refresh: '/api/auth/refresh',
|
|
4
|
-
logout: '/api/auth/logout',
|
|
5
|
-
me: '/api/auth/me',
|
|
6
|
-
} as const;
|
|
7
|
-
|
|
8
|
-
export const AUTH_ERROR_CODES = {
|
|
9
|
-
invalidCredentials: 'AUTH_INVALID_CREDENTIALS',
|
|
10
|
-
tokenExpired: 'AUTH_TOKEN_EXPIRED',
|
|
11
|
-
refreshInvalid: 'AUTH_REFRESH_INVALID',
|
|
12
|
-
} as const;
|
|
13
|
-
|
|
14
|
-
export type AuthErrorCode = (typeof AUTH_ERROR_CODES)[keyof typeof AUTH_ERROR_CODES];
|
|
15
|
-
|
|
16
|
-
export interface AuthUser {
|
|
17
|
-
sub: string;
|
|
18
|
-
email: string;
|
|
19
|
-
roles: string[];
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
export interface LoginRequest {
|
|
23
|
-
email: string;
|
|
24
|
-
password: string;
|
|
25
|
-
}
|
|
26
|
-
|
|
27
|
-
export interface RefreshRequest {
|
|
28
|
-
refreshToken: string;
|
|
29
|
-
}
|
|
30
|
-
|
|
31
|
-
export interface TokenPair {
|
|
32
|
-
accessToken: string;
|
|
33
|
-
refreshToken: string;
|
|
34
|
-
tokenType: 'Bearer';
|
|
35
|
-
accessTtl: string;
|
|
36
|
-
refreshTtl: string;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
export interface LoginResponse extends TokenPair {
|
|
40
|
-
user: AuthUser;
|
|
41
|
-
tokenStore: string;
|
|
42
|
-
}
|
|
43
|
-
|
|
44
|
-
export interface RefreshResponse extends TokenPair {
|
|
45
|
-
user: AuthUser;
|
|
46
|
-
tokenStore: string;
|
|
47
|
-
}
|
|
File without changes
|
|
File without changes
|
|
File without changes
|