From 08af5147581c408e5331f802cce53779caac007f Mon Sep 17 00:00:00 2001 From: rubikscraft Date: Wed, 7 Sep 2022 09:48:52 +0200 Subject: [PATCH] change way auth keys behave --- backend/src/managers/auth/auth.service.ts | 6 +++--- .../managers/auth/guards/guest.strategy.ts | 6 ++++-- .../src/managers/auth/guards/jwt.strategy.ts | 14 +++++++++++-- .../src/managers/auth/guards/main.guard.ts | 21 +++++++++++++------ frontend/src/app/services/api/user.service.ts | 13 ++---------- shared/src/dto/jwt.dto.ts | 4 ++-- 6 files changed, 38 insertions(+), 26 deletions(-) diff --git a/backend/src/managers/auth/auth.service.ts b/backend/src/managers/auth/auth.service.ts index 0cb9a43..6f86bc3 100644 --- a/backend/src/managers/auth/auth.service.ts +++ b/backend/src/managers/auth/auth.service.ts @@ -1,6 +1,6 @@ import { Injectable, Logger } from '@nestjs/common'; import { JwtService } from '@nestjs/jwt'; -import { JwtDataSchema } from 'picsur-shared/dist/dto/jwt.dto'; +import { JwtData, JwtDataSchema } from 'picsur-shared/dist/dto/jwt.dto'; import { EUser } from 'picsur-shared/dist/entities/user.entity'; import { AsyncFailable, Fail, FT } from 'picsur-shared/dist/types'; @@ -11,8 +11,8 @@ export class AuthManagerService { constructor(private readonly jwtService: JwtService) {} async createToken(user: EUser): AsyncFailable { - const jwtData = { - user, + const jwtData: JwtData = { + uid: user.id, }; // Validate to be sure, this makes client experience better diff --git a/backend/src/managers/auth/guards/guest.strategy.ts b/backend/src/managers/auth/guards/guest.strategy.ts index b3e31bf..a90a56f 100644 --- a/backend/src/managers/auth/guards/guest.strategy.ts +++ b/backend/src/managers/auth/guards/guest.strategy.ts @@ -1,6 +1,8 @@ import { Injectable } from '@nestjs/common'; import { PassportStrategy } from '@nestjs/passport'; import { Strategy } from 'passport-strategy'; +import { EUser } from 'picsur-shared/dist/entities/user.entity'; +import { EUserBackend2EUser } from '../../../models/transformers/user.transformer'; import { GuestService } from '../guest.service'; import { ReqType } from './reqtype'; @@ -26,7 +28,7 @@ export class GuestStrategy extends PassportStrategy( } // Return the guest user created by the guestservice - override async validate(payload: any) { - return await this.guestService.getGuestUser(); + override async validate(payload: any): Promise { + return EUserBackend2EUser(await this.guestService.getGuestUser()); } } diff --git a/backend/src/managers/auth/guards/jwt.strategy.ts b/backend/src/managers/auth/guards/jwt.strategy.ts index f0850cc..1efda19 100644 --- a/backend/src/managers/auth/guards/jwt.strategy.ts +++ b/backend/src/managers/auth/guards/jwt.strategy.ts @@ -3,12 +3,18 @@ import { PassportStrategy } from '@nestjs/passport'; import { ExtractJwt, Strategy as JwtPassportStrategy } from 'passport-jwt'; import { JwtDataSchema } from 'picsur-shared/dist/dto/jwt.dto'; import { EUser } from 'picsur-shared/dist/entities/user.entity'; +import { ThrowIfFailed } from 'picsur-shared/dist/types'; +import { UserDbService } from '../../../collections/user-db/user-db.service'; +import { EUserBackend2EUser } from '../../../models/transformers/user.transformer'; @Injectable() export class JwtStrategy extends PassportStrategy(JwtPassportStrategy, 'jwt') { private readonly logger = new Logger(JwtStrategy.name); - constructor(@Inject('JWT_SECRET') jwtSecret: string) { + constructor( + @Inject('JWT_SECRET') jwtSecret: string, + private readonly usersService: UserDbService, + ) { // This will validate the jwt token itself super({ jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(), @@ -24,7 +30,11 @@ export class JwtStrategy extends PassportStrategy(JwtPassportStrategy, 'jwt') { return false; } + const backendUser = ThrowIfFailed( + await this.usersService.findOne(result.data.uid), + ); + // And return the user - return result.data.user; + return EUserBackend2EUser(backendUser); } } diff --git a/backend/src/managers/auth/guards/main.guard.ts b/backend/src/managers/auth/guards/main.guard.ts index ba7fa34..3b02ccf 100644 --- a/backend/src/managers/auth/guards/main.guard.ts +++ b/backend/src/managers/auth/guards/main.guard.ts @@ -2,7 +2,14 @@ import { ExecutionContext, Injectable, Logger } from '@nestjs/common'; import { Reflector } from '@nestjs/core'; import { AuthGuard } from '@nestjs/passport'; import { EUser, EUserSchema } from 'picsur-shared/dist/entities/user.entity'; -import { Fail, Failable, FT, HasFailed } from 'picsur-shared/dist/types'; +import { + AsyncFailable, + Fail, + Failable, + FT, + HasFailed, + ThrowIfFailed, +} from 'picsur-shared/dist/types'; import { makeUnique } from 'picsur-shared/dist/util/unique'; import { UserDbService } from '../../../collections/user-db/user-db.service'; import { Permissions } from '../../../models/constants/permissions.const'; @@ -34,9 +41,10 @@ export class MainAuthGuard extends AuthGuard(['apikey', 'jwt', 'guest']) { ); } - const user = await this.validateUser( - context.switchToHttp().getRequest().user, - ); + const unsafeUser: EUser = context.switchToHttp().getRequest().user; + + const user = ThrowIfFailed(await this.validateUser(unsafeUser)); + if (!user.id) { throw Fail( FT.Internal, @@ -62,6 +70,7 @@ export class MainAuthGuard extends AuthGuard(['apikey', 'jwt', 'guest']) { } context.switchToHttp().getRequest().userPermissions = userPermissions; + context.switchToHttp().getRequest().user = user; if (permissions.every((permission) => userPermissions.includes(permission))) return true; @@ -100,10 +109,10 @@ export class MainAuthGuard extends AuthGuard(['apikey', 'jwt', 'guest']) { return permissions; } - private async validateUser(user: EUser): Promise { + private async validateUser(user: EUser): AsyncFailable { const result = EUserSchema.safeParse(user); if (!result.success) { - throw Fail( + return Fail( FT.Internal, undefined, `Invalid user object, where it should always be valid: ${result.error}`, diff --git a/frontend/src/app/services/api/user.service.ts b/frontend/src/app/services/api/user.service.ts index 74fa8dc..9a8ab90 100644 --- a/frontend/src/app/services/api/user.service.ts +++ b/frontend/src/app/services/api/user.service.ts @@ -53,15 +53,6 @@ export class UserService { const apikey = await this.key.get(); if (!apikey) return; - const user = await this.extractUser(apikey); - if (HasFailed(user)) { - this.logger.error(user.getReason()); - await this.logout(); - return; - } - - this.userSubject.next(user); - const fetchedUser = await this.fetchUser(); if (HasFailed(fetchedUser)) { this.logger.error(fetchedUser.getReason()); @@ -137,7 +128,7 @@ export class UserService { } // This extracts the available userdata from the jwt token - private async extractUser(token: string): AsyncFailable { + private async extractUserID(token: string): AsyncFailable { let decoded: any; try { decoded = jwt_decode(token); @@ -151,7 +142,7 @@ export class UserService { return Fail(FT.UsrValidation, 'Invalid token data'); } - return result.data.user; + return result.data.uid; } // This actually fetches up to date information from the server diff --git a/shared/src/dto/jwt.dto.ts b/shared/src/dto/jwt.dto.ts index 4bf77eb..18bcc15 100644 --- a/shared/src/dto/jwt.dto.ts +++ b/shared/src/dto/jwt.dto.ts @@ -1,8 +1,8 @@ import { z } from 'zod'; -import { EUserSchema } from '../entities/user.entity'; +import { IsEntityID } from '../validators/entity-id.validator'; export const JwtDataSchema = z.object({ - user: EUserSchema.required(), + uid: IsEntityID(), iat: z.number().int().optional(), exp: z.number().int().optional(), });