Skip to content

Commit

Permalink
fix: Update jsonwebtoken to v9.0.0 (#2025)
Browse files Browse the repository at this point in the history
* fix: Update jsonwebtoken to v9.0.0

* Fix emulator based integration tests
  • Loading branch information
lahirumaramba authored Dec 22, 2022
1 parent 1acdb67 commit d23b1c5
Show file tree
Hide file tree
Showing 10 changed files with 46 additions and 79 deletions.
62 changes: 12 additions & 50 deletions package-lock.json

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

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -200,7 +200,7 @@
"@firebase/database-compat": "^0.2.6",
"@firebase/database-types": "^0.9.13",
"@types/node": ">=12.12.47",
"jsonwebtoken": "^8.5.1",
"jsonwebtoken": "^9.0.0",
"jwks-rsa": "^2.1.4",
"node-forge": "^1.3.1",
"uuid": "^9.0.0"
Expand Down
2 changes: 1 addition & 1 deletion src/utils/jwt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -244,7 +244,7 @@ export class PublicKeySignatureVerifier implements SignatureVerifier {
export class EmulatorSignatureVerifier implements SignatureVerifier {
public verify(token: string): Promise<void> {
// Signature checks skipped for emulator; no need to fetch public keys.
return verifyJwtSignature(token, '');
return verifyJwtSignature(token, undefined as any, { algorithms:['none'] });
}
}

Expand Down
8 changes: 5 additions & 3 deletions test/integration/auth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1065,12 +1065,14 @@ describe('admin.auth', () => {
audience: projectId,
issuer: 'https://securetoken.google.com/' + projectId,
subject: uid,
});
}, undefined, 'secret');
return getAuth().verifyIdToken(unsignedToken);
});

it('verifyIdToken() fails when called with a token with wrong project', () => {
const unsignedToken = mocks.generateIdToken({ algorithm: 'none', audience: 'nosuch' });
const unsignedToken = mocks.generateIdToken(
{ algorithm: 'none', audience: 'nosuch' },
undefined, 'secret');
return getAuth().verifyIdToken(unsignedToken)
.should.eventually.be.rejected.and.have.property('code', 'auth/argument-error');
});
Expand All @@ -1081,7 +1083,7 @@ describe('admin.auth', () => {
audience: projectId,
issuer: 'https://securetoken.google.com/' + projectId,
subject: 'nosuch',
});
}, undefined, 'secret');
return getAuth().verifyIdToken(unsignedToken)
.should.eventually.be.rejected.and.have.property('code', 'auth/user-not-found');
});
Expand Down
10 changes: 6 additions & 4 deletions test/resources/mocks.ts
Original file line number Diff line number Diff line change
Expand Up @@ -206,9 +206,10 @@ export const jwksKeyPair = {
*
* @param {object} overrides Overrides for the generated token's attributes.
* @param {object} claims Extra claims to add to the token.
* @param {string} secret Custom key to sign the token with.
* @return {string} A mocked Firebase ID token with any provided overrides included.
*/
export function generateIdToken(overrides?: object, claims?: object): string {
export function generateIdToken(overrides?: object, claims?: object, secret?: string): string {
const options = _.assign({
audience: projectId,
expiresIn: ONE_HOUR_IN_SECONDS,
Expand All @@ -225,17 +226,18 @@ export function generateIdToken(overrides?: object, claims?: object): string {
...claims,
};

return jwt.sign(payload, certificateObject.private_key, options);
return jwt.sign(payload, secret ?? certificateObject.private_key, options);
}

/**
* Generates a mocked Auth Blocking token.
*
* @param overrides Overrides for the generated token's attributes.
* @param claims Extra claims to add to the token.
* @param {string} secret Custom key to sign the token with.
* @return A mocked Auth Blocking token with any provided overrides included.
*/
export function generateAuthBlockingToken(overrides?: object, claims?: object): string {
export function generateAuthBlockingToken(overrides?: object, claims?: object, secret?: string): string {
const options = _.assign({
audience: `https://us-central1-${projectId}.cloudfunctions.net/functionName`,
expiresIn: TEN_MINUTES_IN_SECONDS,
Expand All @@ -252,7 +254,7 @@ export function generateAuthBlockingToken(overrides?: object, claims?: object):
...claims,
};

return jwt.sign(payload, certificateObject.private_key, options);
return jwt.sign(payload, secret ?? certificateObject.private_key, options);
}

/**
Expand Down
2 changes: 1 addition & 1 deletion test/unit/app-check/token-verifier.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ describe('AppCheckTokenVerifier', () => {

it('should be rejected given an App Check token with an incorrect algorithm', () => {
const mockAppCheckToken = mocks.generateAppCheckToken({
algorithm: 'HS256',
algorithm: 'PS256',
});
return tokenVerifier.verifyToken(mockAppCheckToken)
.should.eventually.be.rejectedWith('The provided App Check token has incorrect algorithm');
Expand Down
9 changes: 5 additions & 4 deletions test/unit/auth/auth.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3895,7 +3895,7 @@ AUTH_CONFIGS.forEach((testConfig) => {
expect(decoded).to.have.property('payload').that.has.property('uid', 'uid1');

// Make sure this doesn't throw
jwt.verify(token, '', { algorithms: ['none'] });
jwt.verify(token, undefined as any, { algorithms: ['none'] });
});

it('verifyIdToken() should reject revoked ID tokens', () => {
Expand All @@ -3909,10 +3909,11 @@ AUTH_CONFIGS.forEach((testConfig) => {
const unsignedToken = mocks.generateIdToken({
algorithm: 'none',
subject: uid,
header: {},
}, {
iat: oneSecBeforeValidSince,
auth_time: oneSecBeforeValidSince,
});
}, 'secret');

// verifyIdToken should force checking revocation in emulator mode,
// even if checkRevoked=false.
Expand Down Expand Up @@ -3942,7 +3943,7 @@ AUTH_CONFIGS.forEach((testConfig) => {
}, {
iat: oneSecBeforeValidSince,
auth_time: oneSecBeforeValidSince,
});
}, 'secret');

// verifySessionCookie should force checking revocation in emulator
// mode, even if checkRevoked=false.
Expand All @@ -3960,7 +3961,7 @@ AUTH_CONFIGS.forEach((testConfig) => {
it('verifyIdToken() rejects an unsigned token if auth emulator is unreachable', async () => {
const unsignedToken = mocks.generateIdToken({
algorithm: 'none'
});
}, undefined, 'secret');

const errorMessage = 'Error while making request: connect ECONNREFUSED 127.0.0.1. Error code: ECONNREFUSED';
const getUserStub = sinon.stub(testConfig.Auth.prototype, 'getUser').rejects(new Error(errorMessage));
Expand Down
2 changes: 1 addition & 1 deletion test/unit/auth/token-generator.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -130,7 +130,7 @@ describe('FirebaseTokenGenerator', () => {

// Check that verify doesn't throw
// Note: the types for jsonwebtoken are wrong so we have to disguise the 'null'
jwt.verify(token, '', { algorithms: ['none'] });
jwt.verify(token, undefined as any, { algorithms: ['none'] });

// Decode and check all three segments
const { header, payload, signature } = jwt.decode(token, { complete: true }) as { [key: string]: any };
Expand Down
16 changes: 8 additions & 8 deletions test/unit/auth/token-verifier.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -281,7 +281,7 @@ describe('FirebaseTokenVerifier', () => {

it('should be rejected given a Firebase JWT token with an incorrect algorithm', () => {
const mockIdToken = mocks.generateIdToken({
algorithm: 'HS256',
algorithm: 'PS256',
});
return tokenVerifier.verifyJWT(mockIdToken)
.should.eventually.be.rejectedWith('Firebase ID token has incorrect algorithm');
Expand Down Expand Up @@ -494,7 +494,7 @@ describe('FirebaseTokenVerifier', () => {
const mockIdToken = mocks.generateIdToken({
algorithm: 'none',
header: {}
});
}, undefined, 'secret');

const isEmulator = true;
const decoded = await emulatorVerifier.verifyJWT(mockIdToken, isEmulator);
Expand All @@ -515,14 +515,14 @@ describe('FirebaseTokenVerifier', () => {

const idTokenNoAlg = mocks.generateIdToken({
algorithm: 'none',
});
}, undefined, 'secret');
await tokenVerifier.verifyJWT(idTokenNoAlg)
.should.eventually.be.rejectedWith('Firebase ID token has incorrect algorithm.');

const idTokenNoHeader = mocks.generateIdToken({
algorithm: 'none',
header: {}
});
}, undefined, 'secret');
await tokenVerifier.verifyJWT(idTokenNoHeader)
.should.eventually.be.rejectedWith('Firebase ID token has no "kid" claim.');
});
Expand Down Expand Up @@ -589,7 +589,7 @@ describe('FirebaseTokenVerifier', () => {

it('should be rejected given a Auth Blocking JWT token with an incorrect algorithm', () => {
const mockAuthBlockingToken = mocks.generateAuthBlockingToken({
algorithm: 'HS256',
algorithm: 'PS256',
});
return authBlockingTokenVerifier._verifyAuthBlockingToken(mockAuthBlockingToken, false, undefined)
.should.eventually.be.rejectedWith('Firebase Auth Blocking token has incorrect algorithm');
Expand Down Expand Up @@ -748,7 +748,7 @@ describe('FirebaseTokenVerifier', () => {
const mockAuthBlockingToken = mocks.generateAuthBlockingToken({
algorithm: 'none',
header: {}
});
}, undefined, 'secret');

const isEmulator = true;
const decoded = await emulatorVerifier._verifyAuthBlockingToken(mockAuthBlockingToken, isEmulator, undefined);
Expand All @@ -769,14 +769,14 @@ describe('FirebaseTokenVerifier', () => {

const idTokenNoAlg = mocks.generateAuthBlockingToken({
algorithm: 'none',
});
}, undefined, 'secret');
await authBlockingTokenVerifier._verifyAuthBlockingToken(idTokenNoAlg, false, undefined)
.should.eventually.be.rejectedWith('Firebase Auth Blocking token has incorrect algorithm.');

const idTokenNoHeader = mocks.generateAuthBlockingToken({
algorithm: 'none',
header: {}
});
}, undefined, 'secret');
await authBlockingTokenVerifier._verifyAuthBlockingToken(idTokenNoHeader, false, undefined)
.should.eventually.be.rejectedWith('Firebase Auth Blocking token has no "kid" claim.');
});
Expand Down
12 changes: 6 additions & 6 deletions test/unit/utils/jwt.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -199,7 +199,7 @@ describe('decodeJwt', () => {
const mockIdToken = mocks.generateIdToken({
algorithm: 'none',
header: {}
});
}, undefined, 'secret');

return decodeJwt(mockIdToken)
.should.eventually.be.fulfilled.and.deep.equal(DECODED_UNSIGNED_TOKEN);
Expand Down Expand Up @@ -247,9 +247,9 @@ describe('verifyJwtSignature', () => {
const mockIdToken = mocks.generateIdToken({
algorithm: 'none',
header: {}
});
}, undefined, 'secret');

return verifyJwtSignature(mockIdToken, '')
return verifyJwtSignature(mockIdToken, undefined as any, { algorithms: ['none'] })
.should.eventually.be.fulfilled;
});

Expand Down Expand Up @@ -448,7 +448,7 @@ describe('PublicKeySignatureVerifier', () => {
.resolves(VALID_PUBLIC_KEYS_RESPONSE);
stubs.push(keyFetcherStub);
const mockIdToken = mocks.generateIdToken({
algorithm: 'HS256',
algorithm: 'RS384',
});

return verifier.verify(mockIdToken).should.eventually.be
Expand Down Expand Up @@ -485,11 +485,11 @@ describe('EmulatorSignatureVerifier', () => {
const emulatorVerifier = new EmulatorSignatureVerifier();

describe('verify', () => {
it('should be fullfilled given a valid unsigned (emulator) token', () => {
it('should be fulfilled given a valid unsigned (emulator) token', () => {
const mockIdToken = mocks.generateIdToken({
algorithm: 'none',
header: {}
});
}, undefined, 'secret');

return emulatorVerifier.verify(mockIdToken).should.eventually.be.fulfilled;
});
Expand Down

0 comments on commit d23b1c5

Please sign in to comment.