Skip to content

Commit

Permalink
Simplify \iss\ claim validation logic
Browse files Browse the repository at this point in the history
Add test coverage
  • Loading branch information
Blacksmoke16 committed Nov 8, 2023
1 parent 2bb1fb1 commit bf1ffd6
Show file tree
Hide file tree
Showing 3 changed files with 69 additions and 3 deletions.
59 changes: 59 additions & 0 deletions src/script/spec/esi_client.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,65 @@ describe('EsiClient', () => {
});
});

describe('.parseToken', () => {
beforeEach(() => {
global.PropertiesService.getScriptProperties = jest.fn().mockReturnValueOnce(
createMock<GoogleAppsScript.Properties.Properties>({
getProperty(): string {
return 'CLIENT_ID';
},
}),
);
});

it('with valid token', () => {
global.Utilities.newBlob = jest.fn().mockReturnValueOnce(
createMock<GoogleAppsScript.Base.Blob>({
getDataAsString(): string {
return '{"aud": ["CLIENT_ID", "EVE Online"], "azp": "CLIENT_ID", "iss": "login.eveonline.com"}';
},
}),
);

expect(ESIClient.parseToken('HEADER.BODY.SIG')).toEqual({
aud: ['CLIENT_ID', 'EVE Online'],
azp: 'CLIENT_ID',
iss: 'login.eveonline.com',
});
expect(global.Utilities.base64DecodeWebSafe).toHaveBeenCalledWith('BODY');
});

it('with new issuer', () => {
global.Utilities.newBlob = jest.fn().mockReturnValueOnce(
createMock<GoogleAppsScript.Base.Blob>({
getDataAsString(): string {
return '{"aud": ["CLIENT_ID", "EVE Online"], "azp": "CLIENT_ID", "iss": "https://login.eveonline.com"}';
},
}),
);

expect(ESIClient.parseToken('HEADER.BODY.SIG')).toEqual({
aud: ['CLIENT_ID', 'EVE Online'],
azp: 'CLIENT_ID',
iss: 'https://login.eveonline.com',
});
expect(global.Utilities.base64DecodeWebSafe).toHaveBeenCalledWith('BODY');
});

it('with invalid issuer', () => {
global.Utilities.newBlob = jest.fn().mockReturnValueOnce(
createMock<GoogleAppsScript.Base.Blob>({
getDataAsString(): string {
return '{"aud": ["CLIENT_ID", "EVE Online"], "azp": "CLIENT_ID", "iss": "login.eveonline.net"}';
},
}),
);

expect(() => ESIClient.parseToken('HEADER.BODY.SIG')).toThrow('Access token validation error: invalid issuer');
expect(global.Utilities.base64DecodeWebSafe).toHaveBeenCalledWith('BODY');
});
});

describe('#setFunction', () => {
beforeEach(() => {
endpointProvider.hasEndpoint = jest.fn().mockReturnValueOnce(false);
Expand Down
5 changes: 5 additions & 0 deletions src/script/spec/index.ts
Original file line number Diff line number Diff line change
@@ -1 +1,6 @@
import 'jest-ts-auto-mock';
import { createMock } from 'ts-auto-mock';

// Mock AppScript types
global.Utilities = createMock<GoogleAppsScript.Utilities.Utilities>();
global.PropertiesService = createMock<GoogleAppsScript.Properties.PropertiesService>();
8 changes: 5 additions & 3 deletions src/script/src/esi_client.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,8 +17,10 @@ interface IHTTPClient {
class ESIClient {
private static readonly BASE_URL = 'https://esi.evetech.net';
private static readonly AUDIENCE = 'EVE Online';
private static readonly ISSUER = 'login.eveonline.com';
private static readonly ISSUER_URL_SCHEMA = 'https://login.eveonline.com';
private static readonly ISSUERS = [
'login.eveonline.com',
'https://login.eveonline.com'
];

public static addQueryParam(path: string, paramName: string, paramValue: any): string {
path += path.includes('?') ? '&' : '?';
Expand All @@ -30,7 +32,7 @@ class ESIClient {
const jwtToken: IAccessTokenData = JSON.parse(Utilities.newBlob(Utilities.base64DecodeWebSafe(access_token.split('.')[1])).getDataAsString());
const clientId: string = getScriptProperties_().getProperty('CLIENT_ID')!;

if (jwtToken.iss !== ESIClient.ISSUER && jwtToken.iss !== ESIClient.ISSUER_URL_SCHEMA) throw 'Access token validation error: invalid issuer';
if (!ESIClient.ISSUERS.includes(jwtToken.iss)) throw 'Access token validation error: invalid issuer';
if (jwtToken.aud[0] !== clientId || jwtToken.aud[1] !== ESIClient.AUDIENCE) throw 'Access token validation error: invalid audience';
if (jwtToken.azp !== clientId) throw 'Access token validation error: invalid authorized party';
return jwtToken;
Expand Down

0 comments on commit bf1ffd6

Please sign in to comment.