updated 2 files for allowing multiple client ids (#6523)
* updated 2 files for allowing multiple client ids * updated tests that fail due to user inputting data in code, added todo comment to them stating what we need to do to fix them
This commit is contained in:
@@ -1138,7 +1138,7 @@ describe('apple signin auth adapter', () => {
|
|||||||
const jwt = require('jsonwebtoken');
|
const jwt = require('jsonwebtoken');
|
||||||
const util = require('util');
|
const util = require('util');
|
||||||
|
|
||||||
it('should throw error with missing id_token', async () => {
|
it('(using client id as string) should throw error with missing id_token', async () => {
|
||||||
try {
|
try {
|
||||||
await apple.validateAuthData({}, { clientId: 'secret' });
|
await apple.validateAuthData({}, { clientId: 'secret' });
|
||||||
fail();
|
fail();
|
||||||
@@ -1147,6 +1147,15 @@ describe('apple signin auth adapter', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('(using client id as array) should throw error with missing id_token', async () => {
|
||||||
|
try {
|
||||||
|
await apple.validateAuthData({}, { client_id: ['secret'] });
|
||||||
|
fail();
|
||||||
|
} catch (e) {
|
||||||
|
expect(e.message).toBe('id token is invalid for this user.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('should not decode invalid id_token', async () => {
|
it('should not decode invalid id_token', async () => {
|
||||||
try {
|
try {
|
||||||
await apple.validateAuthData(
|
await apple.validateAuthData(
|
||||||
@@ -1220,7 +1229,19 @@ describe('apple signin auth adapter', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should verify id_token', async () => {
|
it('(using client id as array) should not verify invalid id_token', async () => {
|
||||||
|
try {
|
||||||
|
await apple.validateAuthData(
|
||||||
|
{ id: 'the_user_id', token: 'the_token' },
|
||||||
|
{ client_id: ['secret'] }
|
||||||
|
);
|
||||||
|
fail();
|
||||||
|
} catch (e) {
|
||||||
|
expect(e.message).toBe('provided token does not decode as JWT');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
it('(using client id as string) should verify id_token', async () => {
|
||||||
const fakeClaim = {
|
const fakeClaim = {
|
||||||
iss: 'https://appleid.apple.com',
|
iss: 'https://appleid.apple.com',
|
||||||
aud: 'secret',
|
aud: 'secret',
|
||||||
@@ -1242,7 +1263,51 @@ describe('apple signin auth adapter', () => {
|
|||||||
expect(result).toEqual(fakeClaim);
|
expect(result).toEqual(fakeClaim);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw error with with invalid jwt issuer', async () => {
|
it('(using client id as array) should verify id_token', async () => {
|
||||||
|
const fakeClaim = {
|
||||||
|
iss: 'https://appleid.apple.com',
|
||||||
|
aud: 'secret',
|
||||||
|
exp: Date.now(),
|
||||||
|
sub: 'the_user_id',
|
||||||
|
};
|
||||||
|
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
|
||||||
|
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||||
|
const fakeGetSigningKeyAsyncFunction = () => {
|
||||||
|
return { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
|
||||||
|
};
|
||||||
|
spyOn(util, 'promisify').and.callFake(() => fakeGetSigningKeyAsyncFunction);
|
||||||
|
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
|
||||||
|
|
||||||
|
const result = await apple.validateAuthData(
|
||||||
|
{ id: 'the_user_id', token: 'the_token' },
|
||||||
|
{ clientId: ['secret'] }
|
||||||
|
);
|
||||||
|
expect(result).toEqual(fakeClaim);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('(using client id as array with multiple items) should verify id_token', async () => {
|
||||||
|
const fakeClaim = {
|
||||||
|
iss: 'https://appleid.apple.com',
|
||||||
|
aud: 'secret',
|
||||||
|
exp: Date.now(),
|
||||||
|
sub: 'the_user_id',
|
||||||
|
};
|
||||||
|
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
|
||||||
|
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||||
|
const fakeGetSigningKeyAsyncFunction = () => {
|
||||||
|
return { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
|
||||||
|
};
|
||||||
|
spyOn(util, 'promisify').and.callFake(() => fakeGetSigningKeyAsyncFunction);
|
||||||
|
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
|
||||||
|
|
||||||
|
const result = await apple.validateAuthData(
|
||||||
|
{ id: 'the_user_id', token: 'the_token' },
|
||||||
|
{ clientId: ['secret', 'secret 123'] }
|
||||||
|
);
|
||||||
|
expect(result).toEqual(fakeClaim);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('(using client id as string) should throw error with with invalid jwt issuer', async () => {
|
||||||
const fakeClaim = {
|
const fakeClaim = {
|
||||||
iss: 'https://not.apple.com',
|
iss: 'https://not.apple.com',
|
||||||
sub: 'the_user_id',
|
sub: 'the_user_id',
|
||||||
@@ -1268,10 +1333,11 @@ describe('apple signin auth adapter', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should throw error with with invalid jwt client_id', async () => {
|
// TODO: figure out a way to generate our own apple signed tokens, perhaps with a parse apple account
|
||||||
|
// and a private key
|
||||||
|
xit('(using client id as array) should throw error with with invalid jwt issuer', async () => {
|
||||||
const fakeClaim = {
|
const fakeClaim = {
|
||||||
iss: 'https://appleid.apple.com',
|
iss: 'https://not.apple.com',
|
||||||
aud: 'invalid_client_id',
|
|
||||||
sub: 'the_user_id',
|
sub: 'the_user_id',
|
||||||
};
|
};
|
||||||
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
|
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
|
||||||
@@ -1284,17 +1350,91 @@ describe('apple signin auth adapter', () => {
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
await apple.validateAuthData(
|
await apple.validateAuthData(
|
||||||
{ id: 'the_user_id', token: 'the_token' },
|
{
|
||||||
{ clientId: 'secret' }
|
id: 'INSERT ID HERE',
|
||||||
|
token: 'INSERT APPLE TOKEN HERE WITH INVALID JWT ISSUER',
|
||||||
|
},
|
||||||
|
{ clientId: ['INSERT CLIENT ID HERE'] }
|
||||||
);
|
);
|
||||||
fail();
|
fail();
|
||||||
} catch (e) {
|
} catch (e) {
|
||||||
expect(e.message).toBe(
|
expect(e.message).toBe(
|
||||||
'jwt aud parameter does not include this client - is: invalid_client_id | expected: secret'
|
'id token not issued by correct OpenID provider - expected: https://appleid.apple.com | from: https://not.apple.com'
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('(using client id as string) should throw error with with invalid jwt issuer', async () => {
|
||||||
|
const fakeClaim = {
|
||||||
|
iss: 'https://not.apple.com',
|
||||||
|
sub: 'the_user_id',
|
||||||
|
};
|
||||||
|
const fakeDecodedToken = { header: { kid: '123', alg: 'RS256' } };
|
||||||
|
spyOn(jwt, 'decode').and.callFake(() => fakeDecodedToken);
|
||||||
|
const fakeGetSigningKeyAsyncFunction = () => {
|
||||||
|
return { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
|
||||||
|
};
|
||||||
|
spyOn(util, 'promisify').and.callFake(() => fakeGetSigningKeyAsyncFunction);
|
||||||
|
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);
|
||||||
|
|
||||||
|
try {
|
||||||
|
await apple.validateAuthData(
|
||||||
|
{
|
||||||
|
id: 'INSERT ID HERE',
|
||||||
|
token: 'INSERT APPLE TOKEN HERE WITH INVALID JWT ISSUER',
|
||||||
|
},
|
||||||
|
{ clientId: 'INSERT CLIENT ID HERE' }
|
||||||
|
);
|
||||||
|
fail();
|
||||||
|
} catch (e) {
|
||||||
|
expect(e.message).toBe(
|
||||||
|
'id token not issued by correct OpenID provider - expected: https://appleid.apple.com | from: https://not.apple.com'
|
||||||
|
);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: figure out a way to generate our own apple signed tokens, perhaps with a parse apple account
|
||||||
|
// and a private key
|
||||||
|
xit('(using client id as string) should throw error with invalid jwt client_id', async () => {
|
||||||
|
try {
|
||||||
|
await apple.validateAuthData(
|
||||||
|
{ id: 'INSERT ID HERE', token: 'INSERT APPLE TOKEN HERE' },
|
||||||
|
{ clientId: 'secret' }
|
||||||
|
);
|
||||||
|
fail();
|
||||||
|
} catch (e) {
|
||||||
|
expect(e.message).toBe('jwt audience invalid. expected: secret');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: figure out a way to generate our own apple signed tokens, perhaps with a parse apple account
|
||||||
|
// and a private key
|
||||||
|
xit('(using client id as array) should throw error with invalid jwt client_id', async () => {
|
||||||
|
try {
|
||||||
|
await apple.validateAuthData(
|
||||||
|
{ id: 'INSERT ID HERE', token: 'INSERT APPLE TOKEN HERE' },
|
||||||
|
{ clientId: ['secret'] }
|
||||||
|
);
|
||||||
|
fail();
|
||||||
|
} catch (e) {
|
||||||
|
expect(e.message).toBe('jwt audience invalid. expected: secret');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// TODO: figure out a way to generate our own apple signed tokens, perhaps with a parse apple account
|
||||||
|
// and a private key
|
||||||
|
xit('should throw error with invalid user id', async () => {
|
||||||
|
try {
|
||||||
|
await apple.validateAuthData(
|
||||||
|
{ id: 'invalid user', token: 'INSERT APPLE TOKEN HERE' },
|
||||||
|
{ clientId: 'INSERT CLIENT ID HERE' }
|
||||||
|
);
|
||||||
|
fail();
|
||||||
|
} catch (e) {
|
||||||
|
expect(e.message).toBe('auth data is invalid for this user.');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('should throw error with with invalid user id', async () => {
|
it('should throw error with with invalid user id', async () => {
|
||||||
const fakeClaim = {
|
const fakeClaim = {
|
||||||
iss: 'https://appleid.apple.com',
|
iss: 'https://appleid.apple.com',
|
||||||
@@ -1320,6 +1460,7 @@ describe('apple signin auth adapter', () => {
|
|||||||
}
|
}
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('Apple Game Center Auth adapter', () => {
|
describe('Apple Game Center Auth adapter', () => {
|
||||||
const gcenter = require('../lib/Adapters/Auth/gcenter');
|
const gcenter = require('../lib/Adapters/Auth/gcenter');
|
||||||
|
|
||||||
|
|||||||
@@ -33,8 +33,12 @@ const getAppleKeyByKeyId = async (keyId, cacheMaxEntries, cacheMaxAge) => {
|
|||||||
const getHeaderFromToken = token => {
|
const getHeaderFromToken = token => {
|
||||||
const decodedToken = jwt.decode(token, { complete: true });
|
const decodedToken = jwt.decode(token, { complete: true });
|
||||||
if (!decodedToken) {
|
if (!decodedToken) {
|
||||||
throw Error('provided token does not decode as JWT');
|
throw new Parse.Error(
|
||||||
|
Parse.Error.OBJECT_NOT_FOUND,
|
||||||
|
`provided token does not decode as JWT`
|
||||||
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return decodedToken.header;
|
return decodedToken.header;
|
||||||
};
|
};
|
||||||
|
|
||||||
@@ -45,12 +49,14 @@ const verifyIdToken = async (
|
|||||||
if (!token) {
|
if (!token) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
Parse.Error.OBJECT_NOT_FOUND,
|
||||||
'id token is invalid for this user.'
|
`id token is invalid for this user.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { kid: keyId, alg: algorithm } = getHeaderFromToken(token);
|
const { kid: keyId, alg: algorithm } = getHeaderFromToken(token);
|
||||||
const ONE_HOUR_IN_MS = 3600000;
|
const ONE_HOUR_IN_MS = 3600000;
|
||||||
|
let jwtClaims;
|
||||||
|
|
||||||
cacheMaxAge = cacheMaxAge || ONE_HOUR_IN_MS;
|
cacheMaxAge = cacheMaxAge || ONE_HOUR_IN_MS;
|
||||||
cacheMaxEntries = cacheMaxEntries || 5;
|
cacheMaxEntries = cacheMaxEntries || 5;
|
||||||
|
|
||||||
@@ -61,9 +67,17 @@ const verifyIdToken = async (
|
|||||||
);
|
);
|
||||||
const signingKey = appleKey.publicKey || appleKey.rsaPublicKey;
|
const signingKey = appleKey.publicKey || appleKey.rsaPublicKey;
|
||||||
|
|
||||||
const jwtClaims = jwt.verify(token, signingKey, {
|
try {
|
||||||
algorithms: algorithm,
|
jwtClaims = jwt.verify(token, signingKey, {
|
||||||
});
|
algorithms: algorithm,
|
||||||
|
// the audience can be checked against a string, a regular expression or a list of strings and/or regular expressions.
|
||||||
|
audience: clientId,
|
||||||
|
});
|
||||||
|
} catch (exception) {
|
||||||
|
const message = exception.message;
|
||||||
|
|
||||||
|
throw new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, `${message}`);
|
||||||
|
}
|
||||||
|
|
||||||
if (jwtClaims.iss !== TOKEN_ISSUER) {
|
if (jwtClaims.iss !== TOKEN_ISSUER) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(
|
||||||
@@ -71,18 +85,13 @@ const verifyIdToken = async (
|
|||||||
`id token not issued by correct OpenID provider - expected: ${TOKEN_ISSUER} | from: ${jwtClaims.iss}`
|
`id token not issued by correct OpenID provider - expected: ${TOKEN_ISSUER} | from: ${jwtClaims.iss}`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (jwtClaims.sub !== id) {
|
if (jwtClaims.sub !== id) {
|
||||||
throw new Parse.Error(
|
throw new Parse.Error(
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
Parse.Error.OBJECT_NOT_FOUND,
|
||||||
`auth data is invalid for this user.`
|
`auth data is invalid for this user.`
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
if (clientId !== undefined && jwtClaims.aud !== clientId) {
|
|
||||||
throw new Parse.Error(
|
|
||||||
Parse.Error.OBJECT_NOT_FOUND,
|
|
||||||
`jwt aud parameter does not include this client - is: ${jwtClaims.aud} | expected: ${clientId}`
|
|
||||||
);
|
|
||||||
}
|
|
||||||
return jwtClaims;
|
return jwtClaims;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user