132 lines
3.3 KiB
TypeScript
132 lines
3.3 KiB
TypeScript
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<AuthenticationDto | null> {
|
|
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<AuthenticationDto | null> {
|
|
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<UserEntity | null> {
|
|
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<boolean> {
|
|
const res = await this.refreshTokens.revoke(userId, refreshToken);
|
|
return res?.affected === 1;
|
|
}
|
|
}
|