Skip to content

Commit

Permalink
fix: using jsonwebtoken to decode token payload instead of atob and j…
Browse files Browse the repository at this point in the history
…son parse token (#572)
  • Loading branch information
goemen authored Jul 9, 2024
1 parent 5b228f1 commit 360e965
Show file tree
Hide file tree
Showing 6 changed files with 116 additions and 88 deletions.
11 changes: 0 additions & 11 deletions admin-frontend/src/store/modules/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,6 @@ function isFollowUpVisit(jwtToken) {
return !!jwtToken;
}

function isExpiredToken(jwtToken) {
const now = Date.now().valueOf() / 1000;
const jwtPayload = jwtToken.split('.')[1];
const payload = JSON.parse(window.atob(jwtPayload));
return payload.exp <= now;
}

export const authStore = defineStore('auth', {
namespaced: true,
state: () => ({
Expand Down Expand Up @@ -90,10 +83,6 @@ export const authStore = defineStore('auth', {
async getJwtToken() {
await this.setError(false);
if (isFollowUpVisit(this.jwtToken)) {
if (isExpiredToken(this.jwtToken)) {
await this.logout();
return;
}

const response = await AuthService.refreshAuthToken(
this.jwtToken,
Expand Down
10 changes: 1 addition & 9 deletions backend/src/admin-app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ function addLoginPassportUse(
//set access and refresh tokens
profile.jwtFrontend = adminAuth.generateFrontendToken();
profile.jwt = accessToken;
profile._json = parseJwt(accessToken);
profile._json = utils.parseJwt(accessToken);
profile.refreshToken = refreshToken;
profile.idToken = idToken;
return done(null, profile);
Expand All @@ -150,14 +150,6 @@ function addLoginPassportUse(
);
}

const parseJwt = (token) => {
try {
return JSON.parse(atob(token.split('.')[1]));
} catch (e) {
return null;
}
};

//initialize our authentication strategy
utils.getOidcDiscovery().then((oicdDiscoveryDocument) => {
//OIDC Strategy is used for authorization
Expand Down
10 changes: 1 addition & 9 deletions backend/src/app.ts
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,7 @@ function addLoginPassportUse(
//set access and refresh tokens
profile.jwtFrontend = publicAuth.generateFrontendToken();
profile.jwt = accessToken;
profile._json = parseJwt(accessToken);
profile._json = utils.parseJwt(accessToken);
profile.refreshToken = refreshToken;
profile.idToken = idToken;
return done(null, profile);
Expand All @@ -170,14 +170,6 @@ function addLoginPassportUse(
);
}

const parseJwt = (token) => {
try {
return JSON.parse(atob(token.split('.')[1]));
} catch (e) {
return null;
}
};

//initialize our authentication strategy
utils.getOidcDiscovery().then((discovery) => {
//OIDC Strategy is used for authorization
Expand Down
147 changes: 101 additions & 46 deletions backend/src/v1/services/utils-service.spec.ts
Original file line number Diff line number Diff line change
@@ -1,66 +1,121 @@
import { utils } from './utils-service';
import { config } from '../../config';
import axios from 'axios';
import { exceptions } from 'winston';
import jwt from 'jsonwebtoken';

jest.mock('axios');

afterEach(() => {
jest.clearAllMocks();
});

describe('postDataToDocGenService (&& postData)', () => {
describe('when the config parameter is omitted', () => {
it('creates a new config', async () => {
const body = 'test';
const url = 'http://localhost';
const corr = '1234asdf';

const configResult = {
headers: {
'x-correlation-id': corr,
'x-api-key': config.get('docGenService:apiKey'),
},
};

const spyPostData = jest.spyOn(axios, 'post').mockResolvedValue({
data: 'test',
describe('utils-service', () => {
beforeEach(() => {
jest.clearAllMocks();
});
describe('postDataToDocGenService (&& postData)', () => {
describe('when the config parameter is omitted', () => {
it('creates a new config', async () => {
const body = 'test';
const url = 'http://localhost';
const corr = '1234asdf';

const configResult = {
headers: {
'x-correlation-id': corr,
'x-api-key': config.get('docGenService:apiKey'),
},
};

const spyPostData = jest.spyOn(axios, 'post').mockResolvedValue({
data: 'test',
});

await utils.postDataToDocGenService(body, url, corr);

expect(spyPostData).toHaveBeenCalledWith(url, body, configResult);
});
});
describe('when the config parameter is provided', () => {
it('adds the required headers', async () => {
const body = 'test';
const url = 'http://localhost';
const corr = '1234asdf';

const configParam = {
headers: {
Accept: 'application/pdf',
},
responseType: 'stream',
};

const configResult = {
headers: {
Accept: 'application/pdf',
'x-correlation-id': corr,
'x-api-key': config.get('docGenService:apiKey'),
},
responseType: 'stream',
};

await utils.postDataToDocGenService(body, url, corr);
const spyPostData = jest.spyOn(axios, 'post').mockResolvedValue({
data: 'test',
});

expect(spyPostData).toHaveBeenCalledWith(url, body, configResult);
await utils.postDataToDocGenService(body, url, corr, configParam);

expect(spyPostData).toHaveBeenCalledWith(url, body, configResult);
});
});
});
describe('when the config parameter is provided', () => {
it('adds the required headers', async () => {
const body = 'test';
const url = 'http://localhost';
const corr = '1234asdf';

const configParam = {
headers: {
Accept: 'application/pdf',
},
responseType: 'stream',
};

const configResult = {
headers: {
Accept: 'application/pdf',
'x-correlation-id': corr,
'x-api-key': config.get('docGenService:apiKey'),
},
responseType: 'stream',
};

const spyPostData = jest.spyOn(axios, 'post').mockResolvedValue({
data: 'test',
});

await utils.postDataToDocGenService(body, url, corr, configParam);
describe('parseJwt', () => {
beforeEach(() => {
jest.clearAllMocks();
});

describe('when the JWT is invalid', () => {
it('throws an error', () => {
jest.spyOn(jwt, 'decode').mockImplementationOnce(() => {
throw new Error('test');
});

expect(spyPostData).toHaveBeenCalledWith(url, body, configResult);
expect(utils.parseJwt('test')).toBe(null);
});
});

describe('when the JWT is valid', () => {
describe('with special characters', () => {
it('parses a JWT successfully', () => {
const expectedPayload = {
sub: '1234567890',
name: 'John Doe',
iat: 1516239022,
display_name: 'Kevin O’Riely',
};

const output = utils.parseJwt('eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJkaXNwbGF5X25hbWUiOiJLZXZpbiBP4oCZUmllbHkifQ.ab6ATknTP8_gksT7mnGV9XdEbE8JatEEeAYD4ipPQMg');
expect(output).toEqual(expectedPayload);
});
});

describe('without special characters', () => {
it('parses a JWT successfully', () => {
const expectedPayload = {
sub: '1234567890',
name: 'John Doe',
iat: 1516239022,
display_name: 'Kevin ORiely',
};

const output = utils.parseJwt(
'eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyLCJkaXNwbGF5X25hbWUiOiJLZXZpbiBPUmllbHkifQ.Q7ZwUBIr5hwFuPKq4twT_nE7J3PVQj6hVukhT0xurrY',
);
expect(output).toEqual(expectedPayload);
});
});

})

});
});
14 changes: 13 additions & 1 deletion backend/src/v1/services/utils-service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import axios from 'axios';
import { NextFunction, Request, Response } from 'express';
import { config } from '../../config';
import { logger as log, logger } from '../../logger';
import jsonwebtoken from 'jsonwebtoken';

const fs = require('fs');
axios.interceptors.response.use((response) => {
const headers = response.headers;
Expand All @@ -23,6 +25,15 @@ axios.interceptors.response.use((response) => {
return response;
});

const parseJwt = (token) => {
try {
return jsonwebtoken.decode(token);
} catch (e) {
logger.error(`Error parsing jwt: ${e}`);
return null;
}
};

let discovery = null;

async function getOidcDiscovery() {
Expand Down Expand Up @@ -108,7 +119,8 @@ const utils = {
asyncHandler: (fn) => (req: Request, res: Response, next: NextFunction) => {
Promise.resolve(fn(req, res, next)).catch(next);
},
delay
delay,
parseJwt
};

export { utils };
12 changes: 0 additions & 12 deletions frontend/src/store/modules/auth.js
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,6 @@ function isFollowUpVisit(jwtToken) {
return !!jwtToken;
}

function isExpiredToken(jwtToken) {
const now = Date.now().valueOf() / 1000;
const jwtPayload = jwtToken.split('.')[1];
const payload = JSON.parse(window.atob(jwtPayload));
return payload.exp <= now;
}

export const authStore = defineStore('auth', {
namespaced: true,
state: () => ({
Expand Down Expand Up @@ -84,11 +77,6 @@ export const authStore = defineStore('auth', {
async getJwtToken() {
await this.setError(false);
if (isFollowUpVisit(this.jwtToken)) {
if (isExpiredToken(this.jwtToken)) {
await this.logout();
return;
}

const response = await AuthService.refreshAuthToken(
this.jwtToken,
localStorage.getItem('correlationID'),
Expand Down

0 comments on commit 360e965

Please sign in to comment.