mirror of
https://github.com/apricote/Listory.git
synced 2026-02-07 02:07:03 +00:00
feat: implement long-lived sessions
This commit is contained in:
parent
d0705afca8
commit
44f7e26270
35 changed files with 739 additions and 190 deletions
|
|
@ -1,19 +1,27 @@
|
|||
import { Injectable, ForbiddenException } from "@nestjs/common";
|
||||
import { ForbiddenException, Injectable } from "@nestjs/common";
|
||||
import { ConfigService } from "@nestjs/config";
|
||||
import { JwtService } from "@nestjs/jwt";
|
||||
import { User } from "../users/user.entity";
|
||||
import { UsersService } from "../users/users.service";
|
||||
import { AuthSession } from "./auth-session.entity";
|
||||
import { AuthSessionRepository } from "./auth-session.repository";
|
||||
import { LoginDto } from "./dto/login.dto";
|
||||
|
||||
@Injectable()
|
||||
export class AuthService {
|
||||
private readonly userFilter: null | string;
|
||||
private readonly sessionExpirationTime: string;
|
||||
|
||||
constructor(
|
||||
private readonly config: ConfigService,
|
||||
private readonly usersService: UsersService,
|
||||
private readonly jwtService: JwtService
|
||||
private readonly jwtService: JwtService,
|
||||
private readonly authSessionRepository: AuthSessionRepository
|
||||
) {
|
||||
this.userFilter = this.config.get<string>("SPOTIFY_USER_FILTER");
|
||||
this.sessionExpirationTime = this.config.get<string>(
|
||||
"SESSION_EXPIRATION_TIME"
|
||||
);
|
||||
}
|
||||
|
||||
async spotifyLogin({
|
||||
|
|
@ -38,6 +46,67 @@ export class AuthService {
|
|||
return user;
|
||||
}
|
||||
|
||||
async createSession(
|
||||
user: User
|
||||
): Promise<{
|
||||
session: AuthSession;
|
||||
refreshToken: string;
|
||||
}> {
|
||||
const session = this.authSessionRepository.create();
|
||||
|
||||
session.user = user;
|
||||
await this.authSessionRepository.save(session);
|
||||
|
||||
const [{ refreshToken }] = await Promise.all([
|
||||
this.createRefreshToken(session),
|
||||
this.createAccessToken(session),
|
||||
]);
|
||||
|
||||
return { session, refreshToken };
|
||||
}
|
||||
|
||||
/**
|
||||
* createRefreshToken should only be used while creating a new session.
|
||||
* @param session
|
||||
*/
|
||||
private async createRefreshToken(
|
||||
session: AuthSession
|
||||
): Promise<{ refreshToken }> {
|
||||
const payload = {
|
||||
sub: session.user.id,
|
||||
name: session.user.displayName,
|
||||
};
|
||||
|
||||
const token = await this.jwtService.signAsync(payload, {
|
||||
jwtid: session.id,
|
||||
// jwtService uses the shorter access token time as a default
|
||||
expiresIn: this.sessionExpirationTime,
|
||||
});
|
||||
|
||||
return { refreshToken: token };
|
||||
}
|
||||
|
||||
async createAccessToken(session: AuthSession): Promise<{ accessToken }> {
|
||||
if (session.revokedAt) {
|
||||
throw new ForbiddenException("SessionIsRevoked");
|
||||
}
|
||||
|
||||
const payload = {
|
||||
sub: session.user.id,
|
||||
name: session.user.displayName,
|
||||
picture: session.user.photo,
|
||||
};
|
||||
|
||||
const token = await this.jwtService.signAsync(payload);
|
||||
|
||||
return { accessToken: token };
|
||||
}
|
||||
|
||||
/**
|
||||
* Switch to createAccessToken
|
||||
* @deprecated
|
||||
* @param user
|
||||
*/
|
||||
async createToken(user: User): Promise<{ accessToken }> {
|
||||
const payload = {
|
||||
sub: user.id,
|
||||
|
|
@ -50,6 +119,10 @@ export class AuthService {
|
|||
return { accessToken: token };
|
||||
}
|
||||
|
||||
async findSession(id: string): Promise<AuthSession> {
|
||||
return this.authSessionRepository.findOne(id);
|
||||
}
|
||||
|
||||
async findUser(id: string): Promise<User> {
|
||||
return this.usersService.findById(id);
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue