-
Notifications
You must be signed in to change notification settings - Fork 1
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #109 from autonomys/feat/custom-jwt
Implement custom JWT system
- Loading branch information
Showing
28 changed files
with
1,206 additions
and
375 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
JWT_SECRET=secret |
Binary file not shown.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,126 @@ | ||
import { | ||
CustomAccessTokenPayload, | ||
CustomRefreshTokenPayload, | ||
} from '../../../src/models/users/jwt' | ||
import { CustomJWTAuth } from '../../../src/services/authManager/providers/custom' | ||
import jwt from 'jsonwebtoken' | ||
import { AuthManager } from '../../../src/services/authManager' | ||
import { closeDatabase, getDatabase } from '../../../src/drivers/pg' | ||
import { dbMigration } from '../../utils/dbMigrate' | ||
|
||
describe('JWT', () => { | ||
const provider = 'custom' | ||
const userId = '123' | ||
|
||
let refreshTokenId: string | ||
let accessTokenString: string | ||
let refreshTokenString: string | ||
|
||
beforeAll(async () => { | ||
await getDatabase() | ||
await dbMigration.up() | ||
}) | ||
|
||
afterAll(async () => { | ||
await closeDatabase() | ||
await dbMigration.down() | ||
}) | ||
|
||
it('should generate a JWT token', async () => { | ||
const token = await CustomJWTAuth.createSessionTokens({ | ||
id: userId, | ||
provider, | ||
}) | ||
|
||
const refreshTokenDecoded = jwt.decode(token.refreshToken) | ||
if (typeof refreshTokenDecoded === 'string' || !refreshTokenDecoded) { | ||
throw new Error('Invalid refresh token') | ||
} | ||
const refreshTokenPayload = refreshTokenDecoded as CustomRefreshTokenPayload | ||
|
||
expect(refreshTokenPayload.oauthUserId).toBe(userId) | ||
expect(refreshTokenPayload.isRefreshToken).toBe(true) | ||
expect(refreshTokenPayload.id).toBeDefined() | ||
|
||
refreshTokenId = refreshTokenPayload.id | ||
|
||
const decoded = jwt.decode(token.accessToken) | ||
expect(decoded).toBeDefined() | ||
if (typeof decoded === 'string' || !decoded) { | ||
throw new Error('Invalid access token') | ||
} | ||
const accessTokenPayload = decoded as CustomAccessTokenPayload | ||
|
||
expect(accessTokenPayload.oauthProvider).toBe(provider) | ||
expect(accessTokenPayload.oauthUserId).toBe(userId) | ||
expect(accessTokenPayload.isRefreshToken).toBe(false) | ||
expect(accessTokenPayload.refreshTokenId).toBe(refreshTokenId) | ||
|
||
accessTokenString = token.accessToken | ||
refreshTokenString = token.refreshToken | ||
}) | ||
|
||
it('should be able to authenticate with the access token', async () => { | ||
const user = await AuthManager.getUserFromAccessToken( | ||
'custom-jwt', | ||
accessTokenString, | ||
) | ||
|
||
expect(user).toBeDefined() | ||
expect(user?.id).toBe(userId) | ||
expect(user?.provider).toBe(provider) | ||
}) | ||
|
||
it('should be able to refresh with the refresh token', async () => { | ||
const newAccessToken = | ||
await CustomJWTAuth.refreshAccessToken(refreshTokenString) | ||
|
||
if (!newAccessToken) { | ||
expect(newAccessToken).not.toBeNull() | ||
return | ||
} | ||
|
||
const decoded = jwt.decode(newAccessToken) | ||
expect(decoded).toBeDefined() | ||
if (typeof decoded === 'string' || !decoded) { | ||
throw new Error('Invalid access token') | ||
} | ||
|
||
const accessTokenPayload = decoded as CustomAccessTokenPayload | ||
|
||
expect(accessTokenPayload.refreshTokenId).toBe(refreshTokenId) | ||
expect(accessTokenPayload.isRefreshToken).toBe(false) | ||
expect(accessTokenPayload.oauthUserId).toBe(userId) | ||
expect(accessTokenPayload.oauthProvider).toBe(provider) | ||
|
||
accessTokenString = newAccessToken | ||
}) | ||
|
||
it('should not be able to refresh with the access token', async () => { | ||
expect(CustomJWTAuth.refreshAccessToken(accessTokenString)).rejects.toThrow( | ||
'Invalid refresh token', | ||
) | ||
}) | ||
|
||
it('should be able to invalidate the refresh token', async () => { | ||
expect(() => | ||
CustomJWTAuth.invalidateRefreshToken(refreshTokenString), | ||
).not.toThrow() | ||
}) | ||
|
||
it('should not be able to generate an access token after invalidating the refresh token', async () => { | ||
await CustomJWTAuth.invalidateRefreshToken(refreshTokenString) | ||
|
||
await expect( | ||
CustomJWTAuth.refreshAccessToken(refreshTokenString), | ||
).rejects.toThrow('Invalid refresh token') | ||
}) | ||
|
||
it('should not be able to authenticate with the access token after invalidating the refresh token', async () => { | ||
await CustomJWTAuth.invalidateRefreshToken(refreshTokenString) | ||
|
||
await expect( | ||
AuthManager.getUserFromAccessToken('custom-jwt', accessTokenString), | ||
).rejects.toThrow('Invalid access token') | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,57 @@ | ||
'use strict' | ||
|
||
var dbm | ||
var type | ||
var seed | ||
var fs = require('fs') | ||
var path = require('path') | ||
var Promise | ||
|
||
/** | ||
* We receive the dbmigrate dependency from dbmigrate initially. | ||
* This enables us to not have to rely on NODE_PATH. | ||
*/ | ||
exports.setup = function (options, seedLink) { | ||
dbm = options.dbmigrate | ||
type = dbm.dataType | ||
seed = seedLink | ||
Promise = options.Promise | ||
} | ||
|
||
exports.up = function (db) { | ||
var filePath = path.join( | ||
__dirname, | ||
'sqls', | ||
'20241125160059-jwt-token-registry-up.sql', | ||
) | ||
return new Promise(function (resolve, reject) { | ||
fs.readFile(filePath, { encoding: 'utf-8' }, function (err, data) { | ||
if (err) return reject(err) | ||
|
||
resolve(data) | ||
}) | ||
}).then(function (data) { | ||
return db.runSql(data) | ||
}) | ||
} | ||
|
||
exports.down = function (db) { | ||
var filePath = path.join( | ||
__dirname, | ||
'sqls', | ||
'20241125160059-jwt-token-registry-down.sql', | ||
) | ||
return new Promise(function (resolve, reject) { | ||
fs.readFile(filePath, { encoding: 'utf-8' }, function (err, data) { | ||
if (err) return reject(err) | ||
|
||
resolve(data) | ||
}) | ||
}).then(function (data) { | ||
return db.runSql(data) | ||
}) | ||
} | ||
|
||
exports._meta = { | ||
version: 1, | ||
} |
1 change: 1 addition & 0 deletions
1
backend/migrations/sqls/20241125160059-jwt-token-registry-down.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1 @@ | ||
DROP TABLE IF EXISTS jwt_token_registry; |
4 changes: 4 additions & 0 deletions
4
backend/migrations/sqls/20241125160059-jwt-token-registry-up.sql
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,4 @@ | ||
CREATE TABLE jwt_token_registry ( | ||
id TEXT PRIMARY KEY, | ||
createdAt TIMESTAMP DEFAULT CURRENT_TIMESTAMP | ||
); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.