feat: implement long-lived sessions

This commit is contained in:
Julian Tölle 2020-09-05 23:35:53 +02:00
parent d0705afca8
commit 44f7e26270
35 changed files with 739 additions and 190 deletions

View file

@ -0,0 +1,27 @@
import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { PassportStrategy } from "@nestjs/passport";
import { ExtractJwt, Strategy } from "passport-jwt";
import { AuthService } from "../auth.service";
import { AuthStrategy } from "./strategies.enum";
@Injectable()
export class AccessTokenStrategy extends PassportStrategy(
Strategy,
AuthStrategy.AccessToken
) {
constructor(
private readonly authService: AuthService,
config: ConfigService
) {
super({
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
ignoreExpiration: false,
secretOrKey: config.get<string>("JWT_SECRET"),
});
}
async validate(payload: { sub: string }) {
return this.authService.findUser(payload.sub);
}
}

View file

@ -0,0 +1,48 @@
import {
Injectable,
UnauthorizedException,
ForbiddenException,
} from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { PassportStrategy } from "@nestjs/passport";
import { JwtFromRequestFunction, Strategy } from "passport-jwt";
import { AuthService } from "../auth.service";
import { COOKIE_REFRESH_TOKEN } from "../constants";
import { AuthStrategy } from "./strategies.enum";
import { AuthSession } from "../auth-session.entity";
const extractJwtFromCookie: JwtFromRequestFunction = (req) => {
const token = req.cookies[COOKIE_REFRESH_TOKEN] || null;
return token;
};
@Injectable()
export class RefreshTokenStrategy extends PassportStrategy(
Strategy,
AuthStrategy.RefreshToken
) {
constructor(
private readonly authService: AuthService,
config: ConfigService
) {
super({
jwtFromRequest: extractJwtFromCookie,
ignoreExpiration: false,
secretOrKey: config.get<string>("JWT_SECRET"),
});
}
async validate(payload: { jti: string }): Promise<AuthSession> {
const session = await this.authService.findSession(payload.jti);
if (!session) {
throw new UnauthorizedException("SessionNotFound");
}
if (session.revokedAt) {
throw new ForbiddenException("SessionIsRevoked");
}
return session;
}
}

View file

@ -0,0 +1,38 @@
import { Injectable } from "@nestjs/common";
import { ConfigService } from "@nestjs/config";
import { PassportStrategy } from "@nestjs/passport";
import { Strategy } from "passport-spotify";
import { AuthService } from "../auth.service";
import { AuthStrategy } from "./strategies.enum";
@Injectable()
export class SpotifyStrategy extends PassportStrategy(
Strategy,
AuthStrategy.Spotify
) {
constructor(
private readonly authService: AuthService,
config: ConfigService
) {
super({
clientID: config.get<string>("SPOTIFY_CLIENT_ID"),
clientSecret: config.get<string>("SPOTIFY_CLIENT_SECRET"),
callbackURL: `${config.get<string>(
"APP_URL"
)}/api/v1/auth/spotify/callback`,
scope: [
"user-read-private",
"user-read-email",
"user-read-recently-played",
],
});
}
async validate(accessToken: string, refreshToken: string, profile: any) {
return await this.authService.spotifyLogin({
accessToken,
refreshToken,
profile,
});
}
}

View file

@ -0,0 +1,8 @@
export enum AuthStrategy {
// Internal
AccessToken = "access_token",
RefreshToken = "refresh_token",
// Auth Provider
Spotify = "spotify",
}