feat: First-Start Create Admin Modal
This commit is contained in:
@@ -136,11 +136,31 @@ export class AuthService implements OnModuleInit {
|
||||
* token so the caller can set the HttpOnly cookie.
|
||||
*/
|
||||
private async mintSession(manager: EntityManager, user: UserEntity): Promise<LoginResponse> {
|
||||
const accessToken = await this.signAccess(user);
|
||||
const refreshToken = this.generateRefreshToken();
|
||||
const tokenHash = this.hashToken(refreshToken);
|
||||
return AuthService.mintSessionWith(
|
||||
manager,
|
||||
this.jwt,
|
||||
this.config,
|
||||
this.refreshTtlSeconds,
|
||||
user,
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Stateless mint helper used by other modules (e.g. setup) that need to
|
||||
* create a session without depending on the full AuthModule injection tree.
|
||||
*/
|
||||
static async mintSessionWith(
|
||||
manager: EntityManager,
|
||||
jwt: JwtService,
|
||||
config: ConfigService,
|
||||
refreshTtlSeconds: number,
|
||||
user: UserEntity,
|
||||
): Promise<LoginResponse> {
|
||||
const accessToken = await jwt.signAsync({ sub: user.id, username: user.username, role: user.role });
|
||||
const refreshToken = AuthService.generateRefreshTokenStatic();
|
||||
const tokenHash = AuthService.hashTokenStatic(refreshToken);
|
||||
const now = new Date();
|
||||
const expires = new Date(now.getTime() + this.refreshTtlSeconds * 1000);
|
||||
const expires = new Date(now.getTime() + refreshTtlSeconds * 1000);
|
||||
await manager.insert(RefreshTokenEntity, {
|
||||
id: uuid(),
|
||||
userId: user.id,
|
||||
@@ -152,11 +172,46 @@ export class AuthService implements OnModuleInit {
|
||||
return {
|
||||
accessToken,
|
||||
refreshToken,
|
||||
expiresIn: this.accessTtlSeconds(),
|
||||
expiresIn: AuthService.accessTtlSecondsStatic(config),
|
||||
user: { id: user.id, username: user.username, role: user.role },
|
||||
};
|
||||
}
|
||||
|
||||
private static generateRefreshTokenStatic(): string {
|
||||
return crypto.randomBytes(32).toString('base64url');
|
||||
}
|
||||
|
||||
private static hashTokenStatic(token: string): string {
|
||||
return crypto.createHash('sha256').update(token).digest('hex');
|
||||
}
|
||||
|
||||
private static accessTtlSecondsStatic(config: ConfigService): number {
|
||||
const ttl = config.get<string>('JWT_ACCESS_TTL', '15m');
|
||||
return AuthService.parseTtlStatic(ttl);
|
||||
}
|
||||
|
||||
private static parseTtlStatic(ttl: string): number {
|
||||
const m = /^(\d+)([smhd])$/.exec(ttl.trim());
|
||||
if (!m) return 900;
|
||||
const n = parseInt(m[1], 10);
|
||||
const unit = m[2];
|
||||
const mult = unit === 's' ? 1 : unit === 'm' ? 60 : unit === 'h' ? 3600 : 86400;
|
||||
return n * mult;
|
||||
}
|
||||
|
||||
/**
|
||||
* Public access to the static session mint for cross-module reuse.
|
||||
* The instance-based `mintSession(manager, user)` is preserved for
|
||||
* existing callers and delegates here.
|
||||
*/
|
||||
static async createSession(
|
||||
manager: EntityManager,
|
||||
deps: { jwt: JwtService; config: ConfigService; refreshTtlSeconds: number },
|
||||
user: UserEntity,
|
||||
): Promise<LoginResponse> {
|
||||
return AuthService.mintSessionWith(manager, deps.jwt, deps.config, deps.refreshTtlSeconds, user);
|
||||
}
|
||||
|
||||
private async signAccess(user: UserEntity): Promise<string> {
|
||||
return this.jwt.signAsync({ sub: user.id, username: user.username, role: user.role });
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user