Skip to content

Commit

Permalink
🔐 Fresh Access: Added endpoint for generating access token from refre…
Browse files Browse the repository at this point in the history
…sh token + tweaks
  • Loading branch information
deepakgohil9 committed May 29, 2024
1 parent 38e5c5e commit 5dff37d
Show file tree
Hide file tree
Showing 9 changed files with 158 additions and 55 deletions.
11 changes: 11 additions & 0 deletions .env.example
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
# MongoDB
MONGO_URI = <your_mongo_uri>

# JWT
JWT_ACCESS_SECRET = <your_access_secret>
JWT_ACCESS_EXPIRES_IN = <your_access_expires_in>
JWT_REFRESH_SECRET = <your_refresh_secret>
JWT_REFRESH_EXPIRES_IN = <your_refresh_expires_in>

# CORS
CORS_ORIGIN = <your_cors_origin>
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,7 @@ web_modules/
.env.production.local
.env.local
.env*
!.env.example

# parcel-bundler cache (https://parceljs.org/)
.cache
Expand Down Expand Up @@ -134,4 +135,4 @@ dist
.yarn/unplugged
.yarn/build-state.yml
.yarn/install-state.gz
.pnp.*
.pnp.*
118 changes: 80 additions & 38 deletions package-lock.json

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

8 changes: 7 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "blazex",
"version": "1.0.2",
"version": "1.0.3",
"description": "Ignite your projects with lightning speed! Say goodbye to setup headaches and hello to rapid development.",
"main": "app.ts",
"bin": {
Expand All @@ -22,20 +22,26 @@
},
"dependencies": {
"bcryptjs": "^2.4.3",
"cookie-parser": "^1.4.6",
"cors": "^2.8.5",
"dotenv": "^16.4.5",
"express": "^4.19.2",
"express-mongo-sanitize": "^2.2.0",
"helmet": "^7.1.0",
"jsonwebtoken": "^9.0.2",
"mongoose": "^8.4.0",
"ms": "^2.1.3",
"ora": "^8.0.1",
"xss-shield": "^1.0.1",
"zod": "^3.23.8"
},
"devDependencies": {
"@types/bcryptjs": "^2.4.6",
"@types/cookie-parser": "^1.4.7",
"@types/cors": "^2.8.17",
"@types/express": "^4.17.21",
"@types/jsonwebtoken": "^9.0.6",
"@types/ms": "^0.7.34",
"@typescript-eslint/eslint-plugin": "^6.21.0",
"eslint": "^8.57.0",
"eslint-config-standard-with-typescript": "^43.0.1",
Expand Down
4 changes: 4 additions & 0 deletions src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import express, { type Request, type Response } from 'express'
import dotenv from 'dotenv'
import helmet from 'helmet'
import xss from 'xss-shield'
import cookieParser from 'cookie-parser'
import cors from 'cors'
dotenv.config()

import connect from './databases/mongo.database'
Expand All @@ -15,7 +17,9 @@ import authRoute from './routes/auth.route'
const PORT = process.env.PORT ?? 3000
const app = express()

app.use(cors({ origin: process.env.CORS_ORIGIN ?? '*', credentials: true }))
app.use(helmet())
app.use(cookieParser())
app.use(express.json())
app.use(xss.xssShield())
app.use(sanitizer)
Expand Down
43 changes: 33 additions & 10 deletions src/controllers/auth.controller.ts
Original file line number Diff line number Diff line change
@@ -1,28 +1,31 @@
import { type Request, type Response, type NextFunction } from 'express'
import { type Response, type NextFunction } from 'express'
import ms from 'ms'
import { HttpException, httpErrors } from '../utils/HttpException'
import ApiResponse from '../utils/ApiResponse'
import asyncHandler from '../utils/asyncHandler'
import { generateTokens, type JwtTokens } from '../utils/jwt.utils'
import asyncHandler, { type Req } from '../utils/asyncHandler'
import { generateTokens, generateAccessToken, verifyToken } from '../utils/jwt.utils'

import { type AuthSchemaType } from '../schemas/auth.schema'
import * as authService from '../services/user.service'
import { type AuthSchemaType, type IssueAccessTokenSchemaType } from '../schemas/auth.schema'
import * as userService from '../services/user.service'
import { type UserDocument } from '../models/user.model'

const refreshExpiresIn = ms(process.env.JWT_REFRESH_EXPIRES_IN ?? '30d')

export const register = asyncHandler(async (
req: Request<AuthSchemaType['params'], Record<string, unknown>, AuthSchemaType['body'], AuthSchemaType['query']>,
req: Req<AuthSchemaType>,
res: Response,
next: NextFunction) => {
const user = await authService.create({ signin_method: 'email', ...req.body })
const user = await userService.create({ signin_method: 'email', ...req.body })
const data = user.toObject<UserDocument>()
delete data.password
res.status(201).send(new ApiResponse<UserDocument>('User created successfully', data))
})

export const signin = asyncHandler(async (
req: Request<AuthSchemaType['params'], Record<string, unknown>, AuthSchemaType['body'], AuthSchemaType['query']>,
req: Req<AuthSchemaType>,
res: Response,
next: NextFunction) => {
const user = await authService.findOne({ email: req.body.email }, { signin_method: 1, email: 1, password: 1 }, { lean: false })
const user = await userService.findOne({ email: req.body.email }, { signin_method: 1, email: 1, password: 1 }, { lean: false })

if (user === null) {
throw new HttpException(httpErrors.NOT_FOUND, 'User not found')
Expand All @@ -39,5 +42,25 @@ export const signin = asyncHandler(async (
}

const tokens = generateTokens({ id: user._id })
res.status(200).send(new ApiResponse<JwtTokens>('User signed in successfully', tokens))
res.cookie('refreshToken', tokens.refreshToken, { httpOnly: true, sameSite: 'strict', maxAge: refreshExpiresIn })
res.status(200).send(new ApiResponse<{ accessToken: string }>('User signed in successfully', { accessToken: tokens.accessToken }))
})

export const issueAccessToken = asyncHandler(async (
req: Req<IssueAccessTokenSchemaType>,
res: Response,
next: NextFunction) => {
const refreshToken = req.cookies?.refreshToken

if (refreshToken === undefined || refreshToken === null) {
throw new HttpException(httpErrors.UNAUTHORIZED, 'Access denied')
}

const payload = verifyToken(refreshToken as string, 'refresh')
if (payload === null) {
throw new HttpException(httpErrors.UNAUTHORIZED, 'Invalid token')
}

const accessToken = generateAccessToken({ id: payload.id })
res.status(200).send(new ApiResponse<{ accessToken: string }>('Access token generated successfully', { accessToken }))
})
5 changes: 3 additions & 2 deletions src/routes/auth.route.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@ import * as schema from '../schemas/auth.schema'

const router = express.Router()

router.post('/signup', validate(schema.AuthSchema), controller.register)
router.post('/signin', validate(schema.AuthSchema), controller.signin)
router.post('/signup', validate(schema.authSchema), controller.register)
router.post('/signin', validate(schema.authSchema), controller.signin)
router.get('/issue-access-token', validate(schema.issueAccessTokenSchema), controller.issueAccessToken)

export default router
Loading

0 comments on commit 5dff37d

Please sign in to comment.