Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
37 changes: 37 additions & 0 deletions spec/AuthenticationAdapters.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -575,6 +575,27 @@ describe('google auth adapter', () => {
expect(result).toEqual(fakeClaim);
});

it('(using client id as array with multiple items) should verify id_token (google.com)', async () => {
const fakeClaim = {
iss: 'https://accounts.google.com',
aud: 'secret',
exp: Date.now(),
sub: 'the_user_id',
};
const fakeDecodedToken = { kid: '123', alg: 'RS256' };
const fakeSigningKey = { kid: '123', rsaPublicKey: 'the_rsa_public_key' };
spyOn(authUtils, 'getHeaderFromToken').and.callFake(() => fakeDecodedToken);
spyOn(authUtils, 'getSigningKey').and.resolveTo(fakeSigningKey);
spyOn(jwt, 'verify').and.callFake(() => fakeClaim);

await expectAsync(
google.validateAuthData(
{ id: 'the_user_id', id_token: 'the_token' },
{ clientId: ['secret', 'other-client-id'] }
)
).toBeResolvedTo(fakeClaim);
});

it('(using client id as string) should throw error with with invalid jwt issuer (google.com)', async () => {
const fakeClaim = {
iss: 'https://not.google.com',
Expand Down Expand Up @@ -655,6 +676,14 @@ describe('google auth adapter', () => {
expect(e.message).toBe('Google auth is not configured.');
}
});

it('should throw error when clientId is an empty array', async () => {
await expectAsync(
google.validateAuthData({ id: 'the_user_id', id_token: 'the_token' }, { clientId: [] })
).toBeRejectedWith(
new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Google auth is not configured.')
);
});
});

describe('keycloak auth adapter', () => {
Expand Down Expand Up @@ -1470,6 +1499,14 @@ describe('apple signin auth adapter', () => {
expect(e.message).toBe('Apple auth is not configured.');
}
});

it('should throw error when clientId is an empty array', async () => {
await expectAsync(
apple.validateAuthData({ id: 'the_user_id', token: 'the_token' }, { clientId: [] })
).toBeRejectedWith(
new Parse.Error(Parse.Error.OBJECT_NOT_FOUND, 'Apple auth is not configured.')
);
});
});

describe('phant auth adapter', () => {
Expand Down
8 changes: 6 additions & 2 deletions src/Adapters/Auth/apple.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* @class AppleAdapter
* @param {Object} options - Configuration options for the adapter.
* @param {string} options.clientId - Your Apple App ID.
* @param {string|string[]} options.clientId - Your Apple App ID, or an array of App IDs to accept tokens for multiple bundle IDs (e.g. separate iPhone and iPad apps sharing one Parse app).
*
* @param {Object} authData - The authentication data provided by the client.
* @param {string} authData.id - The user ID obtained from Apple.
Expand All @@ -21,6 +21,10 @@
* }
* }
* ```
* `clientId` also accepts an array of App IDs to accept tokens issued for any of several bundle IDs:
* ```json
* { "auth": { "apple": { "clientId": ["12345", "67890"] } } }
* ```
*
* ## Expected `authData` from the Client
* The adapter expects the client to provide the following `authData` payload:
Expand Down Expand Up @@ -73,7 +77,7 @@ const getAppleKeyByKeyId = async (keyId, cacheMaxEntries, cacheMaxAge) => {
};

const verifyIdToken = async ({ token, id }, { clientId, cacheMaxEntries, cacheMaxAge }) => {
if (!clientId) {
if (!clientId || (Array.isArray(clientId) && !clientId.length)) {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
'Apple auth is not configured.'
Expand Down
8 changes: 6 additions & 2 deletions src/Adapters/Auth/google.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
*
* @class GoogleAdapter
* @param {Object} options - The adapter configuration options.
* @param {string} options.clientId - Your Google application Client ID.
* @param {string|string[]} options.clientId - Your Google application Client ID, or an array of Client IDs to accept tokens issued for any of them.
* @param {number} [options.cacheMaxEntries] - Maximum number of JWKS cache entries. Default: 5.
* @param {number} [options.cacheMaxAge] - Maximum age of JWKS cache entries in ms. Default: 3600000 (1 hour).
*
Expand All @@ -19,6 +19,10 @@
* }
* }
* ```
* `clientId` also accepts an array of Client IDs to accept tokens issued for any of them:
* ```json
* { "auth": { "google": { "clientId": ["id-1", "id-2"] } } }
* ```
*
* The adapter requires the following `authData` fields:
* - **id**: The Google user ID.
Expand Down Expand Up @@ -74,7 +78,7 @@ const getGoogleKeyByKeyId = async (keyId, cacheMaxEntries, cacheMaxAge) => {
};

async function verifyIdToken({ id_token: token, id }, { clientId, cacheMaxEntries, cacheMaxAge }) {
if (!clientId) {
if (!clientId || (Array.isArray(clientId) && !clientId.length)) {
throw new Parse.Error(
Parse.Error.OBJECT_NOT_FOUND,
'Google auth is not configured.'
Expand Down
Loading