import { Injectable, UnauthorizedException } from '@nestjs/common'; import { UserEntity } from 'src/users/entities/users.entity'; import { UsersService } from 'src/users/users.service'; import { AuthRefreshService } from './auth.refresh.service'; import { AuthAccessService } from './auth.access.service'; import { UUID } from 'crypto'; import { AuthenticationDto } from './dto/authentication.dto'; import { LoginDto } from './dto/login.dto'; @Injectable() export class AuthService { constructor( private accessTokens: AuthAccessService, private refreshTokens: AuthRefreshService, private users: UsersService, ) { } async login( loginDetails: LoginDto ): Promise { const user = await this.users.findOne(loginDetails); if (!user) { throw new UnauthorizedException(); } if (loginDetails.remember_me) { return this.renew(user); } const access_token = await this.accessTokens.generate(user); return { ...access_token, refresh_token: null, refresh_exp: null, } } async renew( user: UserEntity, ): Promise { const new_refresh_data = await this.refreshTokens.generate(user); const access_token = await this.accessTokens.generate(user); return { ...access_token, refresh_token: new_refresh_data.refresh_token, refresh_exp: new_refresh_data.exp, } } async validate( username: string, password: string, ): Promise { return await this.users.findOne({ user_login: username, password, remember_me: false }); } async verify( accessToken: string, refreshToken: string ): Promise<{ validation: boolean, userId: UUID | null, username: string | null }> { let access: any = null; let refresh: any = null; if (accessToken) { access = await this.accessTokens.verify(accessToken); if (!access.username || !access.sub) { return { validation: false, userId: null, username: null, }; } } if (refreshToken) { refresh = await this.refreshTokens.verify(refreshToken); if (!refresh.username || !refresh.sub) { return { validation: false, userId: null, username: null, }; } } if (accessToken && refreshToken) { if (access.username != refresh.username || access.sub != refresh.sub) { return { validation: false, userId: null, username: null, }; } } if (!accessToken || !access.exp || access.exp * 1000 <= new Date().getTime()) { if (!refreshToken || !refresh.exp || refresh.exp * 1000 <= new Date().getTime()) { // Both expired. return { validation: false, userId: null, username: null, }; } // Refresh token is still active. return { validation: null, userId: refresh.sub, username: refresh.username, }; } // Access still active, at least. return { validation: true, userId: access.sub, username: access.username, }; } async revoke( userId: UUID, refreshToken: string ): Promise { const res = await this.refreshTokens.revoke(userId, refreshToken); return res?.affected === 1; } }