Skip to content

Commit

Permalink
add local strategy (#9)
Browse files Browse the repository at this point in the history
  • Loading branch information
ashmortar authored Aug 9, 2024
1 parent 4185250 commit fb253dc
Show file tree
Hide file tree
Showing 18 changed files with 4,218 additions and 405 deletions.
365 changes: 361 additions & 4 deletions package-lock.json

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@
"@opentelemetry/sdk-node": "0.52.1",
"@prisma/client": "5.17.0",
"@quixo3/prisma-session-store": "3.1.13",
"bcrypt": "5.1.1",
"class-transformer": "0.5.1",
"class-validator": "0.14.1",
"cookie-parser": "1.4.6",
Expand Down Expand Up @@ -81,6 +82,7 @@
"@nestjs/testing": "10.3.10",
"@swc/cli": "0.4.0",
"@swc/core": "1.7.0",
"@types/bcrypt": "5.0.2",
"@types/cookie-parser": "1.4.7",
"@types/express": "4.17.21",
"@types/jest": "29.5.12",
Expand Down
3,391 changes: 3,390 additions & 1 deletion public/styles.css

Large diffs are not rendered by default.

2 changes: 2 additions & 0 deletions src/app.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import i18n_opts from './config/i18n';
import { LoggerModule } from 'nestjs-pino';
import pinoOpts from './config/pino';
import { OpenTelemetryModule } from 'nestjs-otel';
import { CredentialsService } from './credentials/credentials.service';

@Module({
controllers: [AppController],
Expand All @@ -38,5 +39,6 @@ import { OpenTelemetryModule } from 'nestjs-otel';
UsersModule,
ValidationModule,
],
providers: [CredentialsService],
})
export class AppModule {}
2 changes: 2 additions & 0 deletions src/auth/auth.controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import { prismaMock } from '../../prisma/singleton';
import { I18nModule } from 'nestjs-i18n';
import i18n_opts from '@core/config/i18n';
import opts from '@core/config/app';
import { CredentialsService } from '@core/credentials/credentials.service';

describe('AuthController', () => {
let module: TestingModule;
Expand All @@ -27,6 +28,7 @@ describe('AuthController', () => {
providers: [
AuthService,
UsersService,
CredentialsService,
{ provide: PrismaService, useValue: prismaMock },
],
controllers: [AuthController],
Expand Down
13 changes: 9 additions & 4 deletions src/auth/auth.controller.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { Body, Controller } from '@nestjs/common';
import { Body, Controller, Logger } from '@nestjs/common';

import { AuthService } from './auth.service';

import { ConfigService } from '@nestjs/config';
import { Config } from '@core/config/app';
import { CurrentSession } from './current-session.decorator';
import { SessionWithUserPii } from '@core/users/users.service';

import { UserAvatar } from './components/user-avatar';
import { AuthLinks } from './components/auth-links';
import { Base } from '@core/base/base.controller';
Expand All @@ -17,11 +17,14 @@ import * as P from '@core/auth/pages';
import { SignInDto } from './schemas/sign-in';
import { EmailDto } from '@core/validation/schemas';
import { MainContent } from '@core/components';
import { SessionWithUserPii } from '@core/users/users.service';

const PREFIX = 'auth' as const;
const prefix = `/${PREFIX}` as const;

@Controller(PREFIX)
export class AuthController extends Base {
logger = new Logger(AuthController.name);
constructor(
private readonly configService: ConfigService<Config>,
private readonly authService: AuthService,
Expand Down Expand Up @@ -66,7 +69,8 @@ export class AuthController extends Base {
description: 'sign in',
})
async sigInPost(@Body() signInDto: SignInDto) {
console.log(signInDto);
const user = await this.authService.localSignIn(signInDto);
this.logger.log(user);
}

@Route({
Expand All @@ -89,7 +93,8 @@ export class AuthController extends Base {
description: 'register',
})
async registerPost(@Body() signInDto: SignInDto) {
console.log(signInDto);
const user = await this.authService.localRegister(signInDto);
this.logger.log(user);
}

@Route({
Expand Down
6 changes: 4 additions & 2 deletions src/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@ import { AuthService } from './auth.service';
import { UsersModule } from '@core/users/users.module';
import { JwtModule } from '@nestjs/jwt';
import { GoogleController } from './auth.google.controller';
import { CredentialsService } from '@core/credentials/credentials.service';
import { PrismaModule } from 'nestjs-prisma';

@Module({
imports: [JwtModule, UsersModule],
imports: [JwtModule, UsersModule, PrismaModule],
controllers: [AuthController, GoogleController],
providers: [AuthService],
providers: [AuthService, CredentialsService],
})
export class AuthModule {}
2 changes: 2 additions & 0 deletions src/auth/auth.service.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { JwtModule } from '@nestjs/jwt';
import { UsersService } from '@core/users/users.service';
import { PrismaService } from 'nestjs-prisma';
import { prismaMock } from '../../prisma/singleton';
import { CredentialsService } from '@core/credentials/credentials.service';

describe('AuthService', () => {
let module: TestingModule;
Expand All @@ -15,6 +16,7 @@ describe('AuthService', () => {
providers: [
AuthService,
UsersService,
CredentialsService,
{ provide: PrismaService, useValue: prismaMock },
],
}).compile();
Expand Down
34 changes: 33 additions & 1 deletion src/auth/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,19 @@ import { JwtService } from '@nestjs/jwt';
import { SessionWithUserPii, UsersService } from '@core/users/users.service';
import { ValidGoogleOauthData } from './google-oauth.strategy';
import { JwtPayload } from './jwt.strategy';
import bcrypt from 'bcrypt';
import {
CredentialsService,
CredentialWithUserPii,
} from '@core/credentials/credentials.service';
import { SignInDto } from './schemas/sign-in';

@Injectable()
export class AuthService {
constructor(
private jwtService: JwtService,
private readonly usersService: UsersService,
private readonly credentialsService: CredentialsService,
) {}

generateJwt(payload: JwtPayload) {
Expand All @@ -20,6 +27,31 @@ export class AuthService {
throw new BadRequestException('Invalid user data');
}

return this.usersService.upsertOauthCredentialUser(data);
return this.credentialsService.upsertOauthCredentialUser(data);
}

async localSignIn({
email,
password,
}: SignInDto): Promise<CredentialWithUserPii> {
const credential =
await this.credentialsService.findLocalUserByEmail(email);
const valid = await bcrypt.compare(password, credential?.value || '');
if (!valid || !credential) {
throw new BadRequestException('Invalid email or password');
}
return credential;
}

async localRegister({
email,
password,
}: SignInDto): Promise<CredentialWithUserPii> {
const existing = await this.credentialsService.findLocalUserByEmail(email);
if (existing) {
throw new BadRequestException('User already exists');
}

return this.credentialsService.createLocalUser(email, password);
}
}
3 changes: 2 additions & 1 deletion src/auth/components/user-avatar.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import { Link } from '@core/components';
import { PiiType } from '@core/credentials/credentials.service';
import { Translations } from '@core/i18n/i18n.utils';
import { PiiType, SessionWithUserPii } from '@core/users/users.service';
import { SessionWithUserPii } from '@core/users/users.service';

export type UserAvatarProps = Translations & {
session: SessionWithUserPii;
Expand Down
3 changes: 2 additions & 1 deletion src/auth/google-oauth.strategy.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { Config } from '@core/config/app';
import { PiiType } from '@core/users/users.service';
import { PiiType } from '@core/credentials/credentials.service';

import { Injectable } from '@nestjs/common';
import { ConfigService } from '@nestjs/config';
import { PassportStrategy } from '@nestjs/passport';
Expand Down
19 changes: 19 additions & 0 deletions src/auth/local.strategy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
import { Strategy } from 'passport-local';
import { PassportStrategy } from '@nestjs/passport';
import { Injectable, UnauthorizedException } from '@nestjs/common';
import { AuthService } from './auth.service';

@Injectable()
export class LocalStrategy extends PassportStrategy(Strategy) {
constructor(private authService: AuthService) {
super();
}

async validate(email: string, password: string): Promise<any> {
const user = await this.authService.localSignIn({ email, password });
if (!user) {
throw new UnauthorizedException();
}
return user;
}
}
Loading

0 comments on commit fb253dc

Please sign in to comment.