Skip to content

Commit 0e553e5

Browse files
author
Frantz Kati
committed
fix(auth): add JWT refresh tokens to rest api
Add refresh tokens and delete refresh tokens endpoints to rest api from the auth package
1 parent 034f575 commit 0e553e5

File tree

9 files changed

+106
-14
lines changed

9 files changed

+106
-14
lines changed

examples/blog/app.js

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@ module.exports = tensei()
3232
.teams()
3333
.apiPath('auth')
3434
.rolesAndPermissions()
35+
.jwt({
36+
expiresIn: 60,
37+
refreshTokenExpiresIn: 60 * 2,
38+
})
3539
.social('github', {
3640
key: process.env.GITHUB_KEY,
3741
secret: process.env.GITHUB_SECRET,

packages/auth/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,9 @@
44
"main": "./build/index.js",
55
"license": "MIT",
66
"files": [
7-
"build/"
7+
"build/",
8+
"auth.d.ts",
9+
"auth.config.d.ts"
810
],
911
"types": "./build/index.d.ts",
1012
"devDependencies": {

packages/auth/src/index.ts

Lines changed: 29 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import Bcrypt from 'bcryptjs'
44
import Jwt from 'jsonwebtoken'
55
import Randomstring from 'randomstring'
66
import { validateAll } from 'indicative/validator'
7-
import { Request, Response, NextFunction } from 'express'
7+
import { Request, Response, NextFunction, response } from 'express'
88
import {
99
plugin,
1010
resource,
@@ -808,8 +808,10 @@ class Auth {
808808
route(`Get authenticated ${name}`)
809809
.path(this.getApiPath('me'))
810810
.get()
811-
.handle(async (request, response) =>
812-
response.formatter.ok(request.user)
811+
.handle(async ({ user }, { formatter: { ok, unauthorized } }) =>
812+
user && ! user.public ? ok(user) : unauthorized({
813+
message: 'Unauthorized.'
814+
})
813815
),
814816
route(`Resend Verification email`)
815817
.path(this.getApiPath('verification/resend'))
@@ -834,6 +836,28 @@ class Auth {
834836
response.formatter.ok(
835837
await this.socialAuth(request as any, 'register')
836838
)
839+
),
840+
route('Refresh Token')
841+
.path(this.getApiPath('refresh-token'))
842+
.post()
843+
.handle(async (request, { formatter: { ok, unauthorized } }) => {
844+
try {
845+
return ok(
846+
await this.handleRefreshTokens(request as any)
847+
)
848+
} catch (error) {
849+
return unauthorized({
850+
message: error.message || 'Invalid refresh token.'
851+
})
852+
}
853+
}),
854+
route('Remove refresh Token')
855+
.path(this.getApiPath('refresh-token'))
856+
.delete()
857+
.handle(async (request, response) =>
858+
response.formatter.ok(
859+
await this.removeRefreshTokens(request as any)
860+
)
837861
)
838862
]
839863
}
@@ -978,8 +1002,9 @@ class Auth {
9781002
) as JwtPayload
9791003
} catch (error) {}
9801004

981-
if (!tokenPayload || !tokenPayload.refresh)
1005+
if (!tokenPayload || !tokenPayload.refresh) {
9821006
throw ctx.authenticationError('Invalid refresh token.')
1007+
}
9831008

9841009
const user: any = await ctx.manager.findOne(
9851010
this.resources.user.data.pascalCaseName,

packages/common/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,8 @@
55
"license": "MIT",
66
"types": "./typings/index.d.ts",
77
"files": [
8-
"build/"
8+
"build/",
9+
"typings/"
910
],
1011
"devDependencies": {
1112
"@babel/core": "^7.11.4",

packages/common/typings/config.d.ts

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -218,11 +218,15 @@ declare module '@tensei/common/config' {
218218
TContext = GraphQLPluginContext,
219219
TArgs = any
220220
> = IMiddleware<TSource, TContext, TArgs>
221-
type MiddlewareGenerator = (
221+
type MiddlewareGenerator<
222+
TSource = any,
223+
TContext = GraphQLPluginContext,
224+
TArgs = any
225+
> = (
222226
graphQlQueries: GraphQlQueryContract[],
223227
typeDefs: ITypedef[],
224228
schema: GraphQLSchema
225-
) => IMiddleware
229+
) => IMiddleware<TSource, TContext, TArgs>
226230
export interface Config {
227231
databaseClient: any
228232
schemas: any

packages/core/Tensei.ts

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -363,6 +363,8 @@ export class Tensei implements TenseiContract {
363363

364364
this.app.use(CookieParser())
365365

366+
this.app.disable('x-powered-by')
367+
366368
const rootStorage = (this.storageConfig.disks?.local?.config as any)
367369
.root
368370
const publicPath = (this.storageConfig.disks?.local?.config as any)

packages/core/package.json

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,8 @@
66
"license": "MIT",
77
"files": [
88
"build/",
9-
"typings/"
9+
"typings/",
10+
"core.d.ts"
1011
],
1112
"devDependencies": {
1213
"@types/bcryptjs": "^2.4.2",

packages/rest/rest.d.ts

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Request } from 'express'
2+
3+
declare global {
4+
namespace Express {
5+
export interface Request {
6+
authenticationError: (message?: string) => unknown
7+
forbiddenError: (message?: string) => unknown
8+
validationError: (message?: string) => unknown
9+
userInputError: (message?: string) => unknown
10+
}
11+
}
12+
}

packages/rest/src/index.ts

Lines changed: 46 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { Request, Response, request } from 'express'
1+
import { Request, Response } from 'express'
22
import qs from 'qs'
33
import {
44
FindOptions,
@@ -15,7 +15,7 @@ class Rest {
1515
return `/${apiPath}/${path}`
1616
}
1717

18-
public getPageMetaFromFindOptions(
18+
private getPageMetaFromFindOptions(
1919
total: number,
2020
findOptions: FindOptions<any>
2121
) {
@@ -31,7 +31,7 @@ class Rest {
3131
}
3232
}
3333

34-
public parseQueryToFindOptions(query: any, resource: ResourceContract) {
34+
private parseQueryToFindOptions(query: any, resource: ResourceContract) {
3535
let findOptions: FindOptions<any> = {}
3636

3737
if (query.page && query.page !== '-1') {
@@ -69,7 +69,7 @@ class Rest {
6969
return findOptions
7070
}
7171

72-
public parseQueryToWhereOptions(query: any) {
72+
private parseQueryToWhereOptions(query: any) {
7373
let whereOptions: FilterQuery<any> = {}
7474

7575
if (query.where) {
@@ -101,7 +101,7 @@ class Rest {
101101
return whereOptions
102102
}
103103

104-
extendRoutes(
104+
private extendRoutes(
105105
resources: ResourceContract[],
106106
getApiPath: (path: string) => string
107107
) {
@@ -299,6 +299,46 @@ class Rest {
299299
.afterDatabaseSetup(
300300
async ({ extendRoutes, resources, apiPath, app }) => {
301301
app.use(responseEnhancer())
302+
303+
app.use((request, response, next) => {
304+
// @ts-ignore
305+
request.req = request
306+
307+
return next()
308+
})
309+
310+
app.use((request, response, next) => {
311+
request.authenticationError = (
312+
message: string = 'Unauthenticated.'
313+
) => ({
314+
status: 401,
315+
message
316+
})
317+
318+
request.forbiddenError = (
319+
message: string = 'Forbidden.'
320+
) => ({
321+
status: 400,
322+
message
323+
})
324+
325+
request.validationError = (
326+
message: string = 'Validation failed.'
327+
) => ({
328+
status: 422,
329+
message
330+
})
331+
332+
request.userInputError = (
333+
message: string = 'Validation failed.'
334+
) => ({
335+
status: 422,
336+
message
337+
})
338+
339+
return next()
340+
})
341+
302342
extendRoutes(
303343
this.extendRoutes(resources, (path: string) =>
304344
this.getApiPath(apiPath, path)
@@ -317,6 +357,7 @@ class Rest {
317357
}
318358
)
319359
})
360+
320361
routes.forEach(route => {
321362
;(app as any)[route.config.type.toLowerCase()](
322363
route.config.path,

0 commit comments

Comments
 (0)