rl-core-api 0.12.0 → 0.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/core/config/env.schema.d.ts +1 -0
- package/dist/core/config/env.schema.js +5 -0
- package/dist/features/auth/domain/auth.service.d.ts +16 -1
- package/dist/features/auth/domain/auth.service.js +42 -6
- package/dist/features/auth/domain/token.service.d.ts +16 -1
- package/dist/features/auth/domain/token.service.js +71 -0
- package/dist/features/auth/infra/schema/authToken.schema.d.ts +2 -1
- package/dist/features/auth/infra/schema/authToken.schema.js +1 -0
- package/dist/features/auth/presentation/auth.controller.d.ts +10 -2
- package/dist/features/auth/presentation/auth.controller.js +58 -4
- package/dist/features/auth/presentation/commands/deviceLogin.command.d.ts +3 -0
- package/dist/features/auth/presentation/commands/deviceLogin.command.js +23 -0
- package/dist/features/auth/presentation/commands/refreshToken.command.d.ts +3 -0
- package/dist/features/auth/presentation/commands/refreshToken.command.js +13 -1
- package/dist/features/auth/presentation/responses/sessionUser.response.d.ts +15 -0
- package/dist/features/auth/presentation/responses/sessionUser.response.js +58 -1
- package/package.json +1 -1
|
@@ -31,6 +31,7 @@ export declare const envSchema: z.ZodObject<{
|
|
|
31
31
|
JWT_PENDING_EXPIRES: z.ZodDefault<z.ZodString>;
|
|
32
32
|
JWT_REFRESH_EXPIRES_MINUTES: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
33
33
|
JWT_REFRESH_MOBILE_EXPIRES_MINUTES: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
34
|
+
DEVICE_TRUST_EXPIRES_MINUTES: z.ZodDefault<z.ZodCoercedNumber<unknown>>;
|
|
34
35
|
TOTP_ENC_KEY: z.ZodString;
|
|
35
36
|
COOKIE_SECURE: z.ZodDefault<z.ZodPipe<z.ZodEnum<{
|
|
36
37
|
true: "true";
|
|
@@ -42,6 +42,11 @@ exports.envSchema = zod_1.z
|
|
|
42
42
|
.int()
|
|
43
43
|
.positive()
|
|
44
44
|
.default(43_200),
|
|
45
|
+
DEVICE_TRUST_EXPIRES_MINUTES: zod_1.z.coerce
|
|
46
|
+
.number()
|
|
47
|
+
.int()
|
|
48
|
+
.positive()
|
|
49
|
+
.default(259_200),
|
|
45
50
|
TOTP_ENC_KEY: zod_1.z
|
|
46
51
|
.string()
|
|
47
52
|
.length(64, "TOTP_ENC_KEY deve ter 64 chars hex (32 bytes)"),
|
|
@@ -28,6 +28,18 @@ export interface LoginSession {
|
|
|
28
28
|
}
|
|
29
29
|
export interface VerifiedSession extends LoginSession {
|
|
30
30
|
backupCodes?: string[];
|
|
31
|
+
deviceToken?: string;
|
|
32
|
+
}
|
|
33
|
+
export interface DeviceSession extends LoginSession {
|
|
34
|
+
deviceToken: string;
|
|
35
|
+
}
|
|
36
|
+
export interface TrustedDevice {
|
|
37
|
+
id: string;
|
|
38
|
+
family: string;
|
|
39
|
+
createdAt: Date;
|
|
40
|
+
expiresAt: Date;
|
|
41
|
+
ipAddress?: string | null;
|
|
42
|
+
userAgent?: string | null;
|
|
31
43
|
}
|
|
32
44
|
export interface RefreshedTokens {
|
|
33
45
|
accessToken: string;
|
|
@@ -56,7 +68,10 @@ export declare class AuthService {
|
|
|
56
68
|
verifyTwoFactor(dto: VerifyTwoFactorCommand, ctx: RequestCtx): Promise<VerifiedSession>;
|
|
57
69
|
regenerateBackupCodes(userId: string, code: string): Promise<string[]>;
|
|
58
70
|
refresh(rawToken: string | undefined, ctx: RequestCtx, client?: ClientType): Promise<RefreshedTokens>;
|
|
59
|
-
|
|
71
|
+
loginWithDevice(rawToken: string | undefined, ctx: RequestCtx): Promise<DeviceSession>;
|
|
72
|
+
listTrustedDevices(userId: string): Promise<TrustedDevice[]>;
|
|
73
|
+
forgetTrustedDevice(userId: string, family: string): Promise<void>;
|
|
74
|
+
logout(rawRefreshToken?: string, rawDeviceToken?: string): Promise<void>;
|
|
60
75
|
forgotPassword(email: string): Promise<void>;
|
|
61
76
|
inspectPasswordToken(token: string): Promise<PasswordTokenCheck>;
|
|
62
77
|
resetPassword(token: string, newPassword: string): Promise<void>;
|
|
@@ -151,8 +151,12 @@ let AuthService = class AuthService {
|
|
|
151
151
|
user.backupCodes = remainingBackup;
|
|
152
152
|
}
|
|
153
153
|
await this.users.save(user);
|
|
154
|
-
const
|
|
155
|
-
|
|
154
|
+
const client = dto.client ?? clientType_enum_1.ClientType.WEB;
|
|
155
|
+
const tokens = await this.finalizeLogin(user, ctx, client);
|
|
156
|
+
const deviceToken = client === clientType_enum_1.ClientType.MOBILE
|
|
157
|
+
? await this.tokens.issueDeviceToken(user.id, ctx)
|
|
158
|
+
: undefined;
|
|
159
|
+
return { ...tokens, backupCodes, deviceToken };
|
|
156
160
|
}
|
|
157
161
|
async regenerateBackupCodes(userId, code) {
|
|
158
162
|
const user = await this.users.findById(userId);
|
|
@@ -183,11 +187,43 @@ let AuthService = class AuthService {
|
|
|
183
187
|
const accessToken = await this.signAccessToken(user);
|
|
184
188
|
return { accessToken, refreshToken: newRefresh };
|
|
185
189
|
}
|
|
186
|
-
async
|
|
187
|
-
if (!
|
|
188
|
-
|
|
190
|
+
async loginWithDevice(rawToken, ctx) {
|
|
191
|
+
if (!rawToken) {
|
|
192
|
+
throw new common_1.UnauthorizedException("Aparelho não reconhecido");
|
|
193
|
+
}
|
|
194
|
+
const { userId, rawToken: renewed } = await this.tokens.rotateDeviceToken(rawToken, ctx);
|
|
195
|
+
const user = await this.users.findById(userId);
|
|
196
|
+
if (!user.isActive) {
|
|
197
|
+
throw accountInactive();
|
|
198
|
+
}
|
|
199
|
+
if (user.isLocked(this.env.get("LOGIN_MAX_ATTEMPTS"))) {
|
|
200
|
+
throw accountLocked();
|
|
201
|
+
}
|
|
202
|
+
(0, auditContext_1.setAuditUser)(user.id);
|
|
203
|
+
const session = await this.finalizeLogin(user, ctx, clientType_enum_1.ClientType.MOBILE);
|
|
204
|
+
return { ...session, deviceToken: renewed };
|
|
205
|
+
}
|
|
206
|
+
async listTrustedDevices(userId) {
|
|
207
|
+
const devices = await this.tokens.listTrustedDevices(userId);
|
|
208
|
+
return devices.map((device) => ({
|
|
209
|
+
id: device.id,
|
|
210
|
+
family: device.family ?? device.id,
|
|
211
|
+
createdAt: device.createdAt,
|
|
212
|
+
expiresAt: device.expiresAt,
|
|
213
|
+
ipAddress: device.ipAddress,
|
|
214
|
+
userAgent: device.userAgent,
|
|
215
|
+
}));
|
|
216
|
+
}
|
|
217
|
+
async forgetTrustedDevice(userId, family) {
|
|
218
|
+
await this.tokens.revokeTrustedDevice(userId, family);
|
|
219
|
+
}
|
|
220
|
+
async logout(rawRefreshToken, rawDeviceToken) {
|
|
221
|
+
if (rawRefreshToken) {
|
|
222
|
+
await this.tokens.revokeByRawToken(rawRefreshToken);
|
|
223
|
+
}
|
|
224
|
+
if (rawDeviceToken) {
|
|
225
|
+
await this.tokens.revokeDeviceByRawToken(rawDeviceToken);
|
|
189
226
|
}
|
|
190
|
-
await this.tokens.revokeByRawToken(rawRefreshToken);
|
|
191
227
|
}
|
|
192
228
|
async forgotPassword(email) {
|
|
193
229
|
const user = await this.users.findByEmail(email);
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { EnvService } from "../../../core/config/env.service";
|
|
2
2
|
import { AuthTokenRepository } from "../infra/repositories/authToken.repository";
|
|
3
|
-
import { TokenType } from "../infra/schema/authToken.schema";
|
|
3
|
+
import { AuthToken, TokenType } from "../infra/schema/authToken.schema";
|
|
4
4
|
import { ClientType } from "../presentation/commands/clientType.enum";
|
|
5
5
|
export interface RotationResult {
|
|
6
6
|
userId: string;
|
|
@@ -42,6 +42,21 @@ export declare class TokenService {
|
|
|
42
42
|
ipAddress?: string;
|
|
43
43
|
userAgent?: string;
|
|
44
44
|
}, client?: ClientType): Promise<RotationResult>;
|
|
45
|
+
issueDeviceToken(userId: string, ctx?: {
|
|
46
|
+
ipAddress?: string;
|
|
47
|
+
userAgent?: string;
|
|
48
|
+
}, family?: string): Promise<string>;
|
|
49
|
+
rotateDeviceToken(rawToken: string, ctx?: {
|
|
50
|
+
ipAddress?: string;
|
|
51
|
+
userAgent?: string;
|
|
52
|
+
}): Promise<{
|
|
53
|
+
userId: string;
|
|
54
|
+
rawToken: string;
|
|
55
|
+
}>;
|
|
56
|
+
listTrustedDevices(userId: string): Promise<AuthToken[]>;
|
|
57
|
+
revokeAllDevices(userId: string): Promise<void>;
|
|
58
|
+
revokeTrustedDevice(userId: string, family: string): Promise<void>;
|
|
59
|
+
revokeDeviceByRawToken(rawToken: string): Promise<void>;
|
|
45
60
|
revokeFamily(family: string): Promise<void>;
|
|
46
61
|
revokeAllRefresh(userId: string): Promise<void>;
|
|
47
62
|
revokeByRawToken(rawToken: string): Promise<void>;
|
|
@@ -92,6 +92,77 @@ let TokenService = TokenService_1 = class TokenService {
|
|
|
92
92
|
const { rawToken: newRaw, family } = await this.issueRefreshToken(stored.userId, stored.family ?? undefined, ctx, client);
|
|
93
93
|
return { userId: stored.userId, rawToken: newRaw, family };
|
|
94
94
|
}
|
|
95
|
+
async issueDeviceToken(userId, ctx, family) {
|
|
96
|
+
if (!family) {
|
|
97
|
+
await this.revokeAllDevices(userId);
|
|
98
|
+
}
|
|
99
|
+
const rawToken = (0, hash_util_1.randomToken)(48);
|
|
100
|
+
await this.persist({
|
|
101
|
+
userId,
|
|
102
|
+
rawToken,
|
|
103
|
+
type: authToken_schema_1.TokenType.DEVICE_TRUST,
|
|
104
|
+
family: family ?? (0, uuid_1.v7)(),
|
|
105
|
+
expiresAt: this.minutesFromNow(this.env.get("DEVICE_TRUST_EXPIRES_MINUTES")),
|
|
106
|
+
ipAddress: ctx?.ipAddress,
|
|
107
|
+
userAgent: ctx?.userAgent,
|
|
108
|
+
});
|
|
109
|
+
return rawToken;
|
|
110
|
+
}
|
|
111
|
+
async rotateDeviceToken(rawToken, ctx) {
|
|
112
|
+
const stored = await this.repo.findOne({
|
|
113
|
+
where: { tokenHash: (0, hash_util_1.sha256)(rawToken), type: authToken_schema_1.TokenType.DEVICE_TRUST },
|
|
114
|
+
});
|
|
115
|
+
if (!stored) {
|
|
116
|
+
throw new common_1.UnauthorizedException("Aparelho não reconhecido");
|
|
117
|
+
}
|
|
118
|
+
if (stored.used || stored.revoked) {
|
|
119
|
+
this.logger.warn(`Reuso de vínculo de aparelho detectado (família ${stored.family})`);
|
|
120
|
+
if (stored.family) {
|
|
121
|
+
await this.revokeFamily(stored.family);
|
|
122
|
+
}
|
|
123
|
+
throw new common_1.UnauthorizedException("Aparelho revogado por segurança");
|
|
124
|
+
}
|
|
125
|
+
if (stored.isExpired()) {
|
|
126
|
+
throw new common_1.UnauthorizedException("Vínculo do aparelho expirado");
|
|
127
|
+
}
|
|
128
|
+
stored.used = true;
|
|
129
|
+
await this.repo.save(stored);
|
|
130
|
+
const renewed = await this.issueDeviceToken(stored.userId, ctx, stored.family ?? undefined);
|
|
131
|
+
return { userId: stored.userId, rawToken: renewed };
|
|
132
|
+
}
|
|
133
|
+
async listTrustedDevices(userId) {
|
|
134
|
+
const tokens = await this.repo.find({
|
|
135
|
+
where: { userId, type: authToken_schema_1.TokenType.DEVICE_TRUST, revoked: false },
|
|
136
|
+
order: { createdAt: "DESC" },
|
|
137
|
+
});
|
|
138
|
+
const latestByFamily = new Map();
|
|
139
|
+
for (const token of tokens) {
|
|
140
|
+
const key = token.family ?? token.id;
|
|
141
|
+
if (!latestByFamily.has(key) && !token.isExpired()) {
|
|
142
|
+
latestByFamily.set(key, token);
|
|
143
|
+
}
|
|
144
|
+
}
|
|
145
|
+
return [...latestByFamily.values()];
|
|
146
|
+
}
|
|
147
|
+
async revokeAllDevices(userId) {
|
|
148
|
+
await this.repo.update({ userId, type: authToken_schema_1.TokenType.DEVICE_TRUST, revoked: false }, { revoked: true });
|
|
149
|
+
}
|
|
150
|
+
async revokeTrustedDevice(userId, family) {
|
|
151
|
+
await this.repo.update({ userId, family, type: authToken_schema_1.TokenType.DEVICE_TRUST }, { revoked: true });
|
|
152
|
+
}
|
|
153
|
+
async revokeDeviceByRawToken(rawToken) {
|
|
154
|
+
const stored = await this.repo.findOne({
|
|
155
|
+
where: { tokenHash: (0, hash_util_1.sha256)(rawToken), type: authToken_schema_1.TokenType.DEVICE_TRUST },
|
|
156
|
+
});
|
|
157
|
+
if (!stored) {
|
|
158
|
+
return;
|
|
159
|
+
}
|
|
160
|
+
if (stored.family) {
|
|
161
|
+
await this.revokeFamily(stored.family);
|
|
162
|
+
return;
|
|
163
|
+
}
|
|
164
|
+
await this.repo.update({ id: stored.id }, { revoked: true });
|
|
165
|
+
}
|
|
95
166
|
async revokeFamily(family) {
|
|
96
167
|
await this.repo.update({ family }, { revoked: true });
|
|
97
168
|
}
|
|
@@ -19,6 +19,7 @@ var TokenType;
|
|
|
19
19
|
TokenType[TokenType["TWO_FACTOR_EMAIL"] = 2] = "TWO_FACTOR_EMAIL";
|
|
20
20
|
TokenType[TokenType["REFRESH_TOKEN"] = 3] = "REFRESH_TOKEN";
|
|
21
21
|
TokenType[TokenType["TOTP_USED"] = 4] = "TOTP_USED";
|
|
22
|
+
TokenType[TokenType["DEVICE_TRUST"] = 5] = "DEVICE_TRUST";
|
|
22
23
|
})(TokenType || (exports.TokenType = TokenType = {}));
|
|
23
24
|
let AuthToken = class AuthToken extends base_schema_1.BaseEntity {
|
|
24
25
|
isExpired() {
|
|
@@ -5,16 +5,19 @@ import { AuthService } from "../domain/auth.service";
|
|
|
5
5
|
import { PasswordTokenCheck } from "../domain/auth.service";
|
|
6
6
|
import { SessionUser } from "../domain/auth.service";
|
|
7
7
|
import { LoginChallenge } from "../domain/auth.service";
|
|
8
|
+
import { DeviceSession, TrustedDevice } from "../domain/auth.service";
|
|
8
9
|
import { TotpSetup } from "../domain/twoFactor.service";
|
|
10
|
+
import { DeviceLoginCommand } from "./commands/deviceLogin.command";
|
|
9
11
|
import { LoginCommand } from "./commands/login.command";
|
|
10
12
|
import { ForgotPasswordCommand, ResetPasswordCommand } from "./commands/password.command";
|
|
11
|
-
import { RefreshTokenCommand } from "./commands/refreshToken.command";
|
|
13
|
+
import { LogoutCommand, RefreshTokenCommand } from "./commands/refreshToken.command";
|
|
12
14
|
import { PendingTokenCommand, RegenerateBackupCommand, VerifyTwoFactorCommand } from "./commands/twoFactor.command";
|
|
13
15
|
export interface VerifyResponse {
|
|
14
16
|
user: SessionUser;
|
|
15
17
|
backupCodes?: string[];
|
|
16
18
|
accessToken?: string;
|
|
17
19
|
refreshToken?: string;
|
|
20
|
+
deviceToken?: string;
|
|
18
21
|
}
|
|
19
22
|
export interface RefreshResponse {
|
|
20
23
|
ok: boolean;
|
|
@@ -32,7 +35,12 @@ export declare class AuthController {
|
|
|
32
35
|
}>;
|
|
33
36
|
verify(dto: VerifyTwoFactorCommand, req: Request, res: Response): Promise<VerifyResponse>;
|
|
34
37
|
refresh(dto: RefreshTokenCommand, req: Request, res: Response): Promise<RefreshResponse>;
|
|
35
|
-
logout(dto:
|
|
38
|
+
logout(dto: LogoutCommand, req: Request, res: Response): Promise<{
|
|
39
|
+
ok: boolean;
|
|
40
|
+
}>;
|
|
41
|
+
deviceLogin(dto: DeviceLoginCommand, req: Request): Promise<DeviceSession>;
|
|
42
|
+
devices(user: AuthenticatedUser): Promise<TrustedDevice[]>;
|
|
43
|
+
forgetDevice(user: AuthenticatedUser, family: string): Promise<{
|
|
36
44
|
ok: boolean;
|
|
37
45
|
}>;
|
|
38
46
|
profile(user: AuthenticatedUser): AuthenticatedUser;
|
|
@@ -26,6 +26,7 @@ const ip_util_1 = require("../../../core/utils/ip.util");
|
|
|
26
26
|
const auditable_decorator_1 = require("../../audit/presentation/decorators/auditable.decorator");
|
|
27
27
|
const auth_service_1 = require("../domain/auth.service");
|
|
28
28
|
const clientType_enum_1 = require("./commands/clientType.enum");
|
|
29
|
+
const deviceLogin_command_1 = require("./commands/deviceLogin.command");
|
|
29
30
|
const login_command_1 = require("./commands/login.command");
|
|
30
31
|
const password_command_1 = require("./commands/password.command");
|
|
31
32
|
const refreshToken_command_1 = require("./commands/refreshToken.command");
|
|
@@ -52,10 +53,10 @@ let AuthController = class AuthController {
|
|
|
52
53
|
return this.auth.requestEmailCode(dto.pendingToken);
|
|
53
54
|
}
|
|
54
55
|
async verify(dto, req, res) {
|
|
55
|
-
const { accessToken, refreshToken, user, backupCodes } = await this.auth.verifyTwoFactor(dto, ctxFrom(req));
|
|
56
|
+
const { accessToken, refreshToken, user, backupCodes, deviceToken } = await this.auth.verifyTwoFactor(dto, ctxFrom(req));
|
|
56
57
|
(0, requestLog_context_1.setRequestLogUser)(req, user.id);
|
|
57
58
|
if (dto.client === clientType_enum_1.ClientType.MOBILE) {
|
|
58
|
-
return { user, backupCodes, accessToken, refreshToken };
|
|
59
|
+
return { user, backupCodes, accessToken, refreshToken, deviceToken };
|
|
59
60
|
}
|
|
60
61
|
(0, cookie_util_1.setAuthCookies)(res, this.env, { accessToken, refreshToken });
|
|
61
62
|
return { user, backupCodes };
|
|
@@ -73,10 +74,22 @@ let AuthController = class AuthController {
|
|
|
73
74
|
}
|
|
74
75
|
async logout(dto, req, res) {
|
|
75
76
|
const cookies = req.cookies;
|
|
76
|
-
await this.auth.logout(dto.refreshToken ?? cookies?.[cookie_util_1.REFRESH_COOKIE]);
|
|
77
|
+
await this.auth.logout(dto.refreshToken ?? cookies?.[cookie_util_1.REFRESH_COOKIE], dto.deviceToken);
|
|
77
78
|
(0, cookie_util_1.clearAuthCookies)(res, this.env);
|
|
78
79
|
return { ok: true };
|
|
79
80
|
}
|
|
81
|
+
async deviceLogin(dto, req) {
|
|
82
|
+
const session = await this.auth.loginWithDevice(dto.deviceToken, ctxFrom(req));
|
|
83
|
+
(0, requestLog_context_1.setRequestLogUser)(req, session.user.id);
|
|
84
|
+
return session;
|
|
85
|
+
}
|
|
86
|
+
devices(user) {
|
|
87
|
+
return this.auth.listTrustedDevices(user.id);
|
|
88
|
+
}
|
|
89
|
+
async forgetDevice(user, family) {
|
|
90
|
+
await this.auth.forgetTrustedDevice(user.id, family);
|
|
91
|
+
return { ok: true };
|
|
92
|
+
}
|
|
80
93
|
profile(user) {
|
|
81
94
|
return user;
|
|
82
95
|
}
|
|
@@ -171,9 +184,50 @@ __decorate([
|
|
|
171
184
|
__param(1, (0, common_1.Req)()),
|
|
172
185
|
__param(2, (0, common_1.Res)({ passthrough: true })),
|
|
173
186
|
__metadata("design:type", Function),
|
|
174
|
-
__metadata("design:paramtypes", [refreshToken_command_1.
|
|
187
|
+
__metadata("design:paramtypes", [refreshToken_command_1.LogoutCommand, Object, Object]),
|
|
175
188
|
__metadata("design:returntype", Promise)
|
|
176
189
|
], AuthController.prototype, "logout", null);
|
|
190
|
+
__decorate([
|
|
191
|
+
(0, public_decorator_1.Public)(),
|
|
192
|
+
(0, throttler_1.Throttle)({ default: { limit: 5, ttl: 60_000 } }),
|
|
193
|
+
(0, auditable_decorator_1.Auditable)("Login pelo aparelho confiável"),
|
|
194
|
+
(0, common_1.Post)("device/login"),
|
|
195
|
+
(0, swagger_1.ApiOperation)({
|
|
196
|
+
summary: "Login pelo aparelho confiável: sem senha e sem 2FA",
|
|
197
|
+
description: "O app apresenta o vínculo criado num login completo, depois de destravá-lo com a biometria. O vínculo rotaciona a cada uso.",
|
|
198
|
+
}),
|
|
199
|
+
(0, swagger_1.ApiOkResponse)({ type: sessionUser_response_1.DeviceLoginResponse }),
|
|
200
|
+
__param(0, (0, common_1.Body)()),
|
|
201
|
+
__param(1, (0, common_1.Req)()),
|
|
202
|
+
__metadata("design:type", Function),
|
|
203
|
+
__metadata("design:paramtypes", [deviceLogin_command_1.DeviceLoginCommand, Object]),
|
|
204
|
+
__metadata("design:returntype", Promise)
|
|
205
|
+
], AuthController.prototype, "deviceLogin", null);
|
|
206
|
+
__decorate([
|
|
207
|
+
(0, common_1.UseGuards)(jwtAuth_guard_1.JwtAuthGuard),
|
|
208
|
+
(0, swagger_1.ApiBearerAuth)(),
|
|
209
|
+
(0, common_1.Get)("devices"),
|
|
210
|
+
(0, swagger_1.ApiOperation)({ summary: "Aparelhos confiáveis da conta" }),
|
|
211
|
+
(0, swagger_1.ApiOkResponse)({ type: sessionUser_response_1.TrustedDeviceResponse, isArray: true }),
|
|
212
|
+
__param(0, (0, currentUser_decorator_1.CurrentUser)()),
|
|
213
|
+
__metadata("design:type", Function),
|
|
214
|
+
__metadata("design:paramtypes", [Object]),
|
|
215
|
+
__metadata("design:returntype", Promise)
|
|
216
|
+
], AuthController.prototype, "devices", null);
|
|
217
|
+
__decorate([
|
|
218
|
+
(0, common_1.UseGuards)(jwtAuth_guard_1.JwtAuthGuard),
|
|
219
|
+
(0, swagger_1.ApiBearerAuth)(),
|
|
220
|
+
(0, auditable_decorator_1.Auditable)("Esquecer aparelho confiável"),
|
|
221
|
+
(0, common_1.Delete)("devices/:family"),
|
|
222
|
+
(0, swagger_1.ApiOperation)({
|
|
223
|
+
summary: "Esquece um aparelho: o próximo acesso dele exige senha + 2FA",
|
|
224
|
+
}),
|
|
225
|
+
__param(0, (0, currentUser_decorator_1.CurrentUser)()),
|
|
226
|
+
__param(1, (0, common_1.Param)("family")),
|
|
227
|
+
__metadata("design:type", Function),
|
|
228
|
+
__metadata("design:paramtypes", [Object, String]),
|
|
229
|
+
__metadata("design:returntype", Promise)
|
|
230
|
+
], AuthController.prototype, "forgetDevice", null);
|
|
177
231
|
__decorate([
|
|
178
232
|
(0, common_1.UseGuards)(jwtAuth_guard_1.JwtAuthGuard),
|
|
179
233
|
(0, swagger_1.ApiBearerAuth)(),
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
|
|
3
|
+
var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
|
|
4
|
+
if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
|
|
5
|
+
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;
|
|
6
|
+
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7
|
+
};
|
|
8
|
+
var __metadata = (this && this.__metadata) || function (k, v) {
|
|
9
|
+
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
|
+
};
|
|
11
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
+
exports.DeviceLoginCommand = void 0;
|
|
13
|
+
const swagger_1 = require("@nestjs/swagger");
|
|
14
|
+
const class_validator_1 = require("class-validator");
|
|
15
|
+
class DeviceLoginCommand {
|
|
16
|
+
}
|
|
17
|
+
exports.DeviceLoginCommand = DeviceLoginCommand;
|
|
18
|
+
__decorate([
|
|
19
|
+
(0, swagger_1.ApiProperty)({ description: "Vínculo do aparelho, guardado no Keychain" }),
|
|
20
|
+
(0, class_validator_1.IsString)(),
|
|
21
|
+
(0, class_validator_1.MinLength)(32),
|
|
22
|
+
__metadata("design:type", String)
|
|
23
|
+
], DeviceLoginCommand.prototype, "deviceToken", void 0);
|
|
@@ -9,7 +9,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
9
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.RefreshTokenCommand = void 0;
|
|
12
|
+
exports.LogoutCommand = exports.RefreshTokenCommand = void 0;
|
|
13
13
|
const swagger_1 = require("@nestjs/swagger");
|
|
14
14
|
const class_validator_1 = require("class-validator");
|
|
15
15
|
class RefreshTokenCommand {
|
|
@@ -24,3 +24,15 @@ __decorate([
|
|
|
24
24
|
(0, class_validator_1.IsString)(),
|
|
25
25
|
__metadata("design:type", String)
|
|
26
26
|
], RefreshTokenCommand.prototype, "refreshToken", void 0);
|
|
27
|
+
class LogoutCommand extends RefreshTokenCommand {
|
|
28
|
+
}
|
|
29
|
+
exports.LogoutCommand = LogoutCommand;
|
|
30
|
+
__decorate([
|
|
31
|
+
(0, swagger_1.ApiProperty)({
|
|
32
|
+
required: false,
|
|
33
|
+
description: "Enviar apenas para esquecer o aparelho junto com a saída",
|
|
34
|
+
}),
|
|
35
|
+
(0, class_validator_1.IsOptional)(),
|
|
36
|
+
(0, class_validator_1.IsString)(),
|
|
37
|
+
__metadata("design:type", String)
|
|
38
|
+
], LogoutCommand.prototype, "deviceToken", void 0);
|
|
@@ -16,4 +16,19 @@ export declare class VerifyTwoFactorResponse {
|
|
|
16
16
|
backupCodes?: string[];
|
|
17
17
|
accessToken?: string;
|
|
18
18
|
refreshToken?: string;
|
|
19
|
+
deviceToken?: string;
|
|
20
|
+
}
|
|
21
|
+
export declare class DeviceLoginResponse {
|
|
22
|
+
user: SessionUserResponse;
|
|
23
|
+
accessToken: string;
|
|
24
|
+
refreshToken: string;
|
|
25
|
+
deviceToken: string;
|
|
26
|
+
}
|
|
27
|
+
export declare class TrustedDeviceResponse {
|
|
28
|
+
id: string;
|
|
29
|
+
family: string;
|
|
30
|
+
createdAt: Date;
|
|
31
|
+
expiresAt: Date;
|
|
32
|
+
ipAddress?: string | null;
|
|
33
|
+
userAgent?: string | null;
|
|
19
34
|
}
|
|
@@ -9,7 +9,7 @@ var __metadata = (this && this.__metadata) || function (k, v) {
|
|
|
9
9
|
if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
|
|
10
10
|
};
|
|
11
11
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
12
|
-
exports.VerifyTwoFactorResponse = exports.SessionUserResponse = void 0;
|
|
12
|
+
exports.TrustedDeviceResponse = exports.DeviceLoginResponse = exports.VerifyTwoFactorResponse = exports.SessionUserResponse = void 0;
|
|
13
13
|
const swagger_1 = require("@nestjs/swagger");
|
|
14
14
|
class SessionUserResponse {
|
|
15
15
|
}
|
|
@@ -88,3 +88,60 @@ __decorate([
|
|
|
88
88
|
}),
|
|
89
89
|
__metadata("design:type", String)
|
|
90
90
|
], VerifyTwoFactorResponse.prototype, "refreshToken", void 0);
|
|
91
|
+
__decorate([
|
|
92
|
+
(0, swagger_1.ApiProperty)({
|
|
93
|
+
type: String,
|
|
94
|
+
required: false,
|
|
95
|
+
description: "Vínculo do aparelho (client=mobile). Guardar no Keychain: é o que abre os próximos acessos sem senha e sem 2FA",
|
|
96
|
+
}),
|
|
97
|
+
__metadata("design:type", String)
|
|
98
|
+
], VerifyTwoFactorResponse.prototype, "deviceToken", void 0);
|
|
99
|
+
class DeviceLoginResponse {
|
|
100
|
+
}
|
|
101
|
+
exports.DeviceLoginResponse = DeviceLoginResponse;
|
|
102
|
+
__decorate([
|
|
103
|
+
(0, swagger_1.ApiProperty)({ type: SessionUserResponse }),
|
|
104
|
+
__metadata("design:type", SessionUserResponse)
|
|
105
|
+
], DeviceLoginResponse.prototype, "user", void 0);
|
|
106
|
+
__decorate([
|
|
107
|
+
(0, swagger_1.ApiProperty)(),
|
|
108
|
+
__metadata("design:type", String)
|
|
109
|
+
], DeviceLoginResponse.prototype, "accessToken", void 0);
|
|
110
|
+
__decorate([
|
|
111
|
+
(0, swagger_1.ApiProperty)(),
|
|
112
|
+
__metadata("design:type", String)
|
|
113
|
+
], DeviceLoginResponse.prototype, "refreshToken", void 0);
|
|
114
|
+
__decorate([
|
|
115
|
+
(0, swagger_1.ApiProperty)({ description: "O vínculo rotaciona a cada uso: guarde este" }),
|
|
116
|
+
__metadata("design:type", String)
|
|
117
|
+
], DeviceLoginResponse.prototype, "deviceToken", void 0);
|
|
118
|
+
class TrustedDeviceResponse {
|
|
119
|
+
}
|
|
120
|
+
exports.TrustedDeviceResponse = TrustedDeviceResponse;
|
|
121
|
+
__decorate([
|
|
122
|
+
(0, swagger_1.ApiProperty)({ format: "uuid" }),
|
|
123
|
+
__metadata("design:type", String)
|
|
124
|
+
], TrustedDeviceResponse.prototype, "id", void 0);
|
|
125
|
+
__decorate([
|
|
126
|
+
(0, swagger_1.ApiProperty)({
|
|
127
|
+
format: "uuid",
|
|
128
|
+
description: "Identifica o aparelho ao longo das rotações — é o que se revoga",
|
|
129
|
+
}),
|
|
130
|
+
__metadata("design:type", String)
|
|
131
|
+
], TrustedDeviceResponse.prototype, "family", void 0);
|
|
132
|
+
__decorate([
|
|
133
|
+
(0, swagger_1.ApiProperty)(),
|
|
134
|
+
__metadata("design:type", Date)
|
|
135
|
+
], TrustedDeviceResponse.prototype, "createdAt", void 0);
|
|
136
|
+
__decorate([
|
|
137
|
+
(0, swagger_1.ApiProperty)(),
|
|
138
|
+
__metadata("design:type", Date)
|
|
139
|
+
], TrustedDeviceResponse.prototype, "expiresAt", void 0);
|
|
140
|
+
__decorate([
|
|
141
|
+
(0, swagger_1.ApiProperty)({ required: false, nullable: true }),
|
|
142
|
+
__metadata("design:type", Object)
|
|
143
|
+
], TrustedDeviceResponse.prototype, "ipAddress", void 0);
|
|
144
|
+
__decorate([
|
|
145
|
+
(0, swagger_1.ApiProperty)({ required: false, nullable: true }),
|
|
146
|
+
__metadata("design:type", Object)
|
|
147
|
+
], TrustedDeviceResponse.prototype, "userAgent", void 0);
|
package/package.json
CHANGED