feat: upload module (#486)
* feat: wip upload module * feat: local storage and serve local images * feat: protect against injections * feat: server local and s3 files * fix: use storage location when serving local files * feat: cross field env validation
This commit is contained in:
@ -1,6 +1,5 @@
|
||||
import { Module } from '@nestjs/common';
|
||||
import { JwtModule } from '@nestjs/jwt';
|
||||
import { ConfigModule, ConfigService } from '@nestjs/config';
|
||||
import { JwtAuthStrategy } from './strategies/jwt.auth.strategy';
|
||||
import { AuthService } from './services/auth.service';
|
||||
import { GoogleAuthController } from './controllers/google-auth.controller';
|
||||
@ -8,25 +7,24 @@ import { GoogleStrategy } from './strategies/google.auth.strategy';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { UserModule } from '../user/user.module';
|
||||
import { VerifyAuthController } from './controllers/verify-auth.controller';
|
||||
|
||||
import { TokenService } from './services/token.service';
|
||||
import { AuthResolver } from './auth.resolver';
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
|
||||
const jwtModule = JwtModule.registerAsync({
|
||||
useFactory: async (configService: ConfigService) => {
|
||||
useFactory: async (environmentService: EnvironmentService) => {
|
||||
return {
|
||||
secret: configService.get<string>('ACCESS_TOKEN_SECRET'),
|
||||
secret: environmentService.getAccessTokenSecret(),
|
||||
signOptions: {
|
||||
expiresIn: configService.get<string>('ACCESS_TOKEN_EXPIRES_IN'),
|
||||
expiresIn: environmentService.getAccessTokenExpiresIn(),
|
||||
},
|
||||
};
|
||||
},
|
||||
imports: [ConfigModule.forRoot({})],
|
||||
inject: [ConfigService],
|
||||
inject: [EnvironmentService],
|
||||
});
|
||||
|
||||
@Module({
|
||||
imports: [jwtModule, ConfigModule.forRoot({}), UserModule],
|
||||
imports: [jwtModule, UserModule],
|
||||
controllers: [GoogleAuthController, VerifyAuthController],
|
||||
providers: [
|
||||
AuthService,
|
||||
|
||||
@ -3,7 +3,7 @@ import { TokenService } from './token.service';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { prismaMock } from 'src/database/client-mock/jest-prisma-singleton';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
|
||||
describe('TokenService', () => {
|
||||
let service: TokenService;
|
||||
@ -17,7 +17,7 @@ describe('TokenService', () => {
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
provide: ConfigService,
|
||||
provide: EnvironmentService,
|
||||
useValue: {},
|
||||
},
|
||||
{
|
||||
|
||||
@ -8,24 +8,24 @@ import {
|
||||
} from '@nestjs/common';
|
||||
import { JwtService } from '@nestjs/jwt';
|
||||
import { JwtPayload } from '../strategies/jwt.auth.strategy';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { assert } from 'src/utils/assert';
|
||||
import { addMilliseconds } from 'date-fns';
|
||||
import ms from 'ms';
|
||||
import { AuthToken } from '../dto/token.entity';
|
||||
import { TokenExpiredError } from 'jsonwebtoken';
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
|
||||
@Injectable()
|
||||
export class TokenService {
|
||||
constructor(
|
||||
private readonly jwtService: JwtService,
|
||||
private readonly configService: ConfigService,
|
||||
private readonly environmentService: EnvironmentService,
|
||||
private readonly prismaService: PrismaService,
|
||||
) {}
|
||||
|
||||
async generateAccessToken(userId: string): Promise<AuthToken> {
|
||||
const expiresIn = this.configService.get<string>('ACCESS_TOKEN_EXPIRES_IN');
|
||||
const expiresIn = this.environmentService.getAccessTokenExpiresIn();
|
||||
assert(expiresIn, '', InternalServerErrorException);
|
||||
const expiresAt = addMilliseconds(new Date().getTime(), ms(expiresIn));
|
||||
|
||||
@ -56,10 +56,8 @@ export class TokenService {
|
||||
}
|
||||
|
||||
async generateRefreshToken(userId: string): Promise<AuthToken> {
|
||||
const secret = this.configService.get('REFRESH_TOKEN_SECRET');
|
||||
const expiresIn = this.configService.get<string>(
|
||||
'REFRESH_TOKEN_EXPIRES_IN',
|
||||
);
|
||||
const secret = this.environmentService.getRefreshTokenSecret();
|
||||
const expiresIn = this.environmentService.getRefreshTokenExpiresIn();
|
||||
assert(expiresIn, '', InternalServerErrorException);
|
||||
const expiresAt = addMilliseconds(new Date().getTime(), ms(expiresIn));
|
||||
|
||||
@ -87,8 +85,8 @@ export class TokenService {
|
||||
}
|
||||
|
||||
async generateLoginToken(email: string): Promise<AuthToken> {
|
||||
const secret = this.configService.get('LOGIN_TOKEN_SECRET');
|
||||
const expiresIn = this.configService.get<string>('LOGIN_TOKEN_EXPIRES_IN');
|
||||
const secret = this.environmentService.getLoginTokenSecret();
|
||||
const expiresIn = this.environmentService.getLoginTokenExpiresIn();
|
||||
assert(expiresIn, '', InternalServerErrorException);
|
||||
const expiresAt = addMilliseconds(new Date().getTime(), ms(expiresIn));
|
||||
const jwtPayload = {
|
||||
@ -105,7 +103,7 @@ export class TokenService {
|
||||
}
|
||||
|
||||
async verifyLoginToken(loginToken: string): Promise<string> {
|
||||
const loginTokenSecret = this.configService.get('LOGIN_TOKEN_SECRET');
|
||||
const loginTokenSecret = this.environmentService.getLoginTokenSecret();
|
||||
|
||||
const payload = await this.verifyJwt(loginToken, loginTokenSecret);
|
||||
|
||||
@ -113,7 +111,7 @@ export class TokenService {
|
||||
}
|
||||
|
||||
async verifyRefreshToken(refreshToken: string) {
|
||||
const secret = this.configService.get('REFRESH_TOKEN_SECRET');
|
||||
const secret = this.environmentService.getRefreshTokenSecret();
|
||||
const jwtPayload = await this.verifyJwt(refreshToken, secret);
|
||||
|
||||
assert(
|
||||
@ -191,9 +189,7 @@ export class TokenService {
|
||||
}
|
||||
|
||||
computeRedirectURI(loginToken: string): string {
|
||||
return `${this.configService.get<string>(
|
||||
'FRONT_AUTH_CALLBACK_URL',
|
||||
)}?loginToken=${loginToken}`;
|
||||
return `${this.environmentService.getFrontAuthCallbackUrl()}?loginToken=${loginToken}`;
|
||||
}
|
||||
|
||||
async verifyJwt(token: string, secret?: string) {
|
||||
|
||||
@ -2,8 +2,8 @@ import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Strategy, VerifyCallback } from 'passport-google-oauth20';
|
||||
|
||||
import { Injectable } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { Request } from 'express';
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
|
||||
export type GoogleRequest = Request & {
|
||||
user: {
|
||||
@ -15,11 +15,11 @@ export type GoogleRequest = Request & {
|
||||
|
||||
@Injectable()
|
||||
export class GoogleStrategy extends PassportStrategy(Strategy, 'google') {
|
||||
constructor(configService: ConfigService) {
|
||||
constructor(environmentService: EnvironmentService) {
|
||||
super({
|
||||
clientID: configService.get<string>('AUTH_GOOGLE_CLIENT_ID'),
|
||||
clientSecret: configService.get<string>('AUTH_GOOGLE_CLIENT_SECRET'),
|
||||
callbackURL: configService.get<string>('AUTH_GOOGLE_CALLBACK_URL'),
|
||||
clientID: environmentService.getAuthGoogleClientId(),
|
||||
clientSecret: environmentService.getAuthGoogleClientSecret(),
|
||||
callbackURL: environmentService.getAuthGoogleCallbackUrl(),
|
||||
scope: ['email', 'profile'],
|
||||
});
|
||||
}
|
||||
|
||||
@ -1,9 +1,9 @@
|
||||
import { Strategy, ExtractJwt } from 'passport-jwt';
|
||||
import { PassportStrategy } from '@nestjs/passport';
|
||||
import { Injectable, UnauthorizedException } from '@nestjs/common';
|
||||
import { ConfigService } from '@nestjs/config';
|
||||
import { PrismaService } from 'src/database/prisma.service';
|
||||
import { User, Workspace } from '@prisma/client';
|
||||
import { EnvironmentService } from 'src/integrations/environment/environment.service';
|
||||
|
||||
export type JwtPayload = { sub: string; workspaceId: string };
|
||||
export type PassportUser = { user: User; workspace: Workspace };
|
||||
@ -11,13 +11,13 @@ export type PassportUser = { user: User; workspace: Workspace };
|
||||
@Injectable()
|
||||
export class JwtAuthStrategy extends PassportStrategy(Strategy, 'jwt') {
|
||||
constructor(
|
||||
private readonly configService: ConfigService,
|
||||
private readonly environmentService: EnvironmentService,
|
||||
private readonly prismaService: PrismaService,
|
||||
) {
|
||||
super({
|
||||
jwtFromRequest: ExtractJwt.fromAuthHeaderAsBearerToken(),
|
||||
ignoreExpiration: false,
|
||||
secretOrKey: configService.get<string>('ACCESS_TOKEN_SECRET'),
|
||||
secretOrKey: environmentService.getAccessTokenSecret(),
|
||||
});
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user