Skip to content

Commit

Permalink
feat(oauth): add github oauth
Browse files Browse the repository at this point in the history
* need to work on todo's
  • Loading branch information
HarshPatel5940 authored and rajdip-b committed Jan 28, 2024
1 parent a92311e commit 5b930a1
Show file tree
Hide file tree
Showing 7 changed files with 129 additions and 19 deletions.
5 changes: 5 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,11 @@ DATABASE_URL=postgresql://postgres:<your-project-password>@db.<your-project-name
SUPABASE_API_URL=https://<your-project-name>.supabase.co
SUPABASE_ANON_KEY=

GITHUB_CLIENT_ID=
GITHUB_CLIENT_SECRET=
GITHUB_CALLBACK_URL=


SMTP_HOST=
SMTP_PORT=
SMTP_EMAIL_ADDRESS=
Expand Down
3 changes: 2 additions & 1 deletion apps/api/src/auth/auth.module.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { AuthService } from './service/auth.service'
import { AuthController } from './controller/auth.controller'
import { JwtModule } from '@nestjs/jwt'
import { UserModule } from '../user/user.module'
import { GithubStrategy } from './auth.stratergy'

@Module({
imports: [
Expand All @@ -17,7 +18,7 @@ import { UserModule } from '../user/user.module'
}),
UserModule
],
providers: [AuthService],
providers: [AuthService, GithubStrategy],
controllers: [AuthController]
})
export class AuthModule {}
20 changes: 20 additions & 0 deletions apps/api/src/auth/auth.stratergy.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
import { Injectable } from '@nestjs/common'
import { ConfigService } from '@nestjs/config'
import { PassportStrategy } from '@nestjs/passport'
import { Profile, Strategy } from 'passport-github2'

@Injectable()
export class GithubStrategy extends PassportStrategy(Strategy, 'github') {
constructor(configService: ConfigService) {
super({
clientID: configService.get<string>('GITHUB_CLIENT_ID'),
clientSecret: configService.get<string>('GITHUB_CLIENT_SECRET'),
callbackURL: configService.get<string>('GITHUB_CALLBACK_URL'),
scope: ['public_profile', 'user:email']
})
}

async validate(accessToken: string, _refreshToken: string, profile: Profile) {
return profile
}
}
39 changes: 38 additions & 1 deletion apps/api/src/auth/controller/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,8 +1,18 @@
import { Controller, HttpStatus, Param, Post, Query } from '@nestjs/common'
import {
Controller,
Get,
HttpStatus,
Param,
Post,
Query,
Req,
UseGuards
} from '@nestjs/common'
import { AuthService } from '../service/auth.service'
import { UserAuthenticatedResponse } from '../auth.types'
import { Public } from '../../decorators/public.decorator'
import { ApiOperation, ApiParam, ApiResponse, ApiTags } from '@nestjs/swagger'
import { AuthGuard } from '@nestjs/passport'

@ApiTags('Auth Controller')
@Controller('auth')
Expand Down Expand Up @@ -71,4 +81,31 @@ export class AuthController {
): Promise<UserAuthenticatedResponse> {
return await this.authService.validateOtp(email, otp)
}

@Public()
@Get('github')
@UseGuards(AuthGuard('github'))
@ApiOperation({
summary: 'Github OAuth',
description:
'This endpoint validates Github OAuth. If the OAuth is valid, it returns a valid token along with the user details'
})
// TODO: add params, response and function return types here
async githubOAuthLogin() {}

@Public()
@Get('github/callback')
@UseGuards(AuthGuard('github'))
@ApiOperation({
summary: 'Github OAuth Callback',
description:
'This endpoint validates Github OAuth. If the OAuth is valid, it returns a valid token along with the user details'
})
// TODO: add params, response and function return types here
async githubOAuthCallback(@Req() req) {
const user = req.user
const email = user.emails[0].value
console.log(email)
return await this.authService.handleGithubOAuth(email)
}
}
36 changes: 25 additions & 11 deletions apps/api/src/auth/service/auth.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,19 +29,12 @@ export class AuthService {
this.logger = new Logger(AuthService.name)
}

async sendOtp(email: string): Promise<void> {
if (!email || !email.includes('@')) {
this.logger.error(`Invalid email address: ${email}`)
throw new HttpException(
'Please enter a valid email address',
HttpStatus.BAD_REQUEST
)
}

private async createUserIfNotExists(email: string) {
let user = await this.findUserByEmail(email)
// We need to create the user if it doesn't exist yet
if (!(await this.findUserByEmail(email))) {
if (!user) {
// Create the user
const user = await this.prisma.user.create({
user = await this.prisma.user.create({
data: {
email
}
Expand All @@ -62,7 +55,18 @@ export class AuthService {
}
})
}
return user
}

async sendOtp(email: string): Promise<void> {
if (!email || !email.includes('@')) {
this.logger.error(`Invalid email address: ${email}`)
throw new HttpException(
'Please enter a valid email address',
HttpStatus.BAD_REQUEST
)
}
await this.createUserIfNotExists(email)
const otp = await this.prisma.otp.create({
data: {
code: randomUUID().slice(0, 6).toUpperCase(),
Expand Down Expand Up @@ -126,6 +130,16 @@ export class AuthService {
}
}

async handleGithubOAuth(email: string) {
// We need to create the user if it doesn't exist yet
const user = await this.createUserIfNotExists(email)

return {
...user,
token: await this.jwt.signAsync({ id: user.id })
}
}

@Cron(CronExpression.EVERY_HOUR)
async cleanUpExpiredOtps() {
try {
Expand Down
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -113,6 +113,8 @@
"moment": "^2.30.1",
"next": "14.0.4",
"nodemailer": "^6.9.8",
"passport": "^0.7.0",
"passport-github2": "^0.1.12",
"passport-jwt": "^4.0.1",
"react": "18.2.0",
"react-dom": "18.2.0",
Expand Down
43 changes: 37 additions & 6 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

0 comments on commit 5b930a1

Please sign in to comment.