From 3617e8d3d64ad3c13a17be8ede025ca2fbaaba9c Mon Sep 17 00:00:00 2001 From: Robert Polovitzer Date: Sat, 18 Jun 2022 12:58:35 -0400 Subject: [PATCH 1/5] Revoke authorization --- src/index.js | 32 ++++++++++++++++++++++++++++++++ 1 file changed, 32 insertions(+) diff --git a/src/index.js b/src/index.js index 7fb8351..96ba5f8 100644 --- a/src/index.js +++ b/src/index.js @@ -253,6 +253,36 @@ const refreshAuthorizationToken = async ( }).then((res) => res.json()); }; +/** Revoke Apple authorization token */ +const revokeAuthorizationToken = async ( + refreshToken: string, + options: { + clientID: string, + clientSecret: string, + }, +): Promise => { + if (!options.clientID) { + throw new Error('clientID is empty'); + } + if (!options.clientSecret) { + throw new Error('clientSecret is empty'); + } + + const url = new URL(ENDPOINT_URL); + url.pathname = '/auth/revoke'; + + const params = new URLSearchParams(); + params.append('client_id', options.clientID); + params.append('client_secret', options.clientSecret); + params.append('refresh_token', refreshToken); + params.append('token_hint_type', 'refresh_token'); + + return fetch(url.toString(), { + method: 'POST', + body: params, + }).then((res) => res.json()); +}; + /** Gets an Array of Apple Public Keys that can be used to decode Apple's id tokens */ const _getApplePublicKeys = async ({ disableCaching, @@ -370,6 +400,7 @@ export { getClientSecret, getAuthorizationToken, refreshAuthorizationToken, + revokeAuthorizationToken, verifyIdToken, verifyWebhookToken, // Internals - exposed for hacky people @@ -383,6 +414,7 @@ export default { getClientSecret, getAuthorizationToken, refreshAuthorizationToken, + revokeAuthorizationToken, verifyIdToken, verifyWebhookToken, // Internals - exposed for hacky people From 5f576263825a81627cafaf6881975dae9453f43e Mon Sep 17 00:00:00 2001 From: Robert Polovitzer Date: Sat, 18 Jun 2022 13:29:43 -0400 Subject: [PATCH 2/5] Update README --- README.md | 26 ++++++++++++++++++++++++++ 1 file changed, 26 insertions(+) diff --git a/README.md b/README.md index 23c7979..66f0cd4 100644 --- a/README.md +++ b/README.md @@ -136,6 +136,32 @@ try { } ``` +### 5. Revoke tokens +```js + +const clientSecret = appleSignin.getClientSecret({ + clientID: 'com.company.app', // Apple Client ID + teamID: 'teamID', // Apple Developer Team ID. + privateKey: 'PRIVATE_KEY_STRING', // private key associated with your client ID. -- Or provide a `privateKeyPath` property instead. + keyIdentifier: 'XXXXXXXXXX', // identifier of the private key. - can be found here https://developer.apple.com/account/resources/authkeys/list + // OPTIONAL + expAfter: 15777000, // Duration after which to expire JWT +}); + +const options = { + clientID: 'com.company.app', // Apple Client ID + clientSecret +}; + +try { + const { + response + } = appleSignin.revokeAuthorizationToken(refreshToken, options); +} catch (err) { + console.error(err); +} +``` + ### Optional: Server-to-Server Notifications Apple provides realtime server-to-server notifications of several user lifecycle From 51db8c1092201785bc1523984f88365e74eb5c9e Mon Sep 17 00:00:00 2001 From: Robert Polovitzer Date: Tue, 21 Jun 2022 16:07:24 -0400 Subject: [PATCH 3/5] Add support for access token revoke --- README.md | 34 +++++++++++++++++++++++++++++----- src/index.js | 9 +++++---- 2 files changed, 34 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 66f0cd4..4c12538 100644 --- a/README.md +++ b/README.md @@ -136,7 +136,7 @@ try { } ``` -### 5. Revoke tokens +### 5. a, Revoke tokens with refresh_token ```js const clientSecret = appleSignin.getClientSecret({ @@ -150,13 +150,37 @@ const clientSecret = appleSignin.getClientSecret({ const options = { clientID: 'com.company.app', // Apple Client ID - clientSecret + clientSecret, + tokenHintType: 'refresh_token' }; try { - const { - response - } = appleSignin.revokeAuthorizationToken(refreshToken, options); + await appleSignin.revokeAuthorizationToken(refreshToken, options); +} catch (err) { + console.error(err); +} +``` + +### 5. b, Revoke tokens with access_token +```js + +const clientSecret = appleSignin.getClientSecret({ + clientID: 'com.company.app', // Apple Client ID + teamID: 'teamID', // Apple Developer Team ID. + privateKey: 'PRIVATE_KEY_STRING', // private key associated with your client ID. -- Or provide a `privateKeyPath` property instead. + keyIdentifier: 'XXXXXXXXXX', // identifier of the private key. - can be found here https://developer.apple.com/account/resources/authkeys/list + // OPTIONAL + expAfter: 15777000, // Duration after which to expire JWT +}); + +const options = { + clientID: 'com.company.app', // Apple Client ID + clientSecret, + tokenHintType: 'access_token' +}; + +try { + await appleSignin.revokeAuthorizationToken(accessToken, options); } catch (err) { console.error(err); } diff --git a/src/index.js b/src/index.js index 96ba5f8..9582378 100644 --- a/src/index.js +++ b/src/index.js @@ -255,12 +255,13 @@ const refreshAuthorizationToken = async ( /** Revoke Apple authorization token */ const revokeAuthorizationToken = async ( - refreshToken: string, + token: string, options: { clientID: string, clientSecret: string, + tokenHintType: 'refresh_token' | 'access_token' }, -): Promise => { +): Promise => { if (!options.clientID) { throw new Error('clientID is empty'); } @@ -274,8 +275,8 @@ const revokeAuthorizationToken = async ( const params = new URLSearchParams(); params.append('client_id', options.clientID); params.append('client_secret', options.clientSecret); - params.append('refresh_token', refreshToken); - params.append('token_hint_type', 'refresh_token'); + params.append('token', token); + params.append('token_hint_type', options.tokenHintType); return fetch(url.toString(), { method: 'POST', From 013ed9dffa0569a816ebc5c427d09bcbaf47cbbd Mon Sep 17 00:00:00 2001 From: Robert Polovitzer Date: Wed, 22 Jun 2022 11:14:40 -0400 Subject: [PATCH 4/5] Update tests for revoke authorization --- __tests__/index.test.js | 3 +++ typescript/index.d.ts | 14 ++++++++++++++ typescript/test.ts | 19 +++++++++++++++++++ 3 files changed, 36 insertions(+) diff --git a/__tests__/index.test.js b/__tests__/index.test.js index 6c3c4b2..f5d4849 100644 --- a/__tests__/index.test.js +++ b/__tests__/index.test.js @@ -6,6 +6,7 @@ import appleSignin, { getClientSecret, getAuthorizationToken, refreshAuthorizationToken, + revokeAuthorizationToken, verifyIdToken, verifyWebhookToken, _getApplePublicKeys, @@ -17,6 +18,7 @@ describe('appleSignin test', () => { expect(appleSignin.getClientSecret).toBeTruthy(); expect(appleSignin.getAuthorizationToken).toBeTruthy(); expect(appleSignin.refreshAuthorizationToken).toBeTruthy(); + expect(appleSignin.revokeAuthorizationToken).toBeTruthy(); expect(appleSignin.verifyIdToken).toBeTruthy(); expect(appleSignin.verifyWebhookToken).toBeTruthy(); expect(appleSignin._getApplePublicKeys).toBeTruthy(); @@ -27,6 +29,7 @@ describe('appleSignin test', () => { expect(getClientSecret).toBeTruthy(); expect(getAuthorizationToken).toBeTruthy(); expect(refreshAuthorizationToken).toBeTruthy(); + expect(revokeAuthorizationToken).toBeTruthy(); expect(verifyIdToken).toBeTruthy(); expect(verifyWebhookToken).toBeTruthy(); expect(_getApplePublicKeys).toBeTruthy(); diff --git a/typescript/index.d.ts b/typescript/index.d.ts index a01268a..6d3a9df 100644 --- a/typescript/index.d.ts +++ b/typescript/index.d.ts @@ -112,6 +112,18 @@ declare function refreshAuthorizationToken( }, ): Promise; +/** + * Revoke Apple authorization tokens + */ +declare function revokeAuthorizationToken( + token: string, + options: { + clientID: string; + clientSecret: string; + tokenHintType: 'refresh_token' | 'access_token'; + }, +): Promise; + /** * Verifies an Apple id token */ @@ -151,6 +163,7 @@ export { getAuthorizationUrl, getClientSecret, refreshAuthorizationToken, + revokeAuthorizationToken, verifyIdToken, verifyWebhookToken, _getApplePublicKeys, @@ -162,6 +175,7 @@ declare const _exports: { getAuthorizationUrl: typeof getAuthorizationUrl; getClientSecret: typeof getClientSecret; refreshAuthorizationToken: typeof refreshAuthorizationToken; + revokeAuthorizationToken: typeof revokeAuthorizationToken; verifyIdToken: typeof verifyIdToken; verifyWebhookToken: typeof verifyWebhookToken; _getApplePublicKeys: typeof _getApplePublicKeys; diff --git a/typescript/test.ts b/typescript/test.ts index 6c3db04..a29ebf2 100644 --- a/typescript/test.ts +++ b/typescript/test.ts @@ -3,6 +3,7 @@ import appleAuth, { getAuthorizationToken, getClientSecret, refreshAuthorizationToken, + revokeAuthorizationToken, verifyIdToken, _getApplePublicKeys, _setFetch, @@ -88,6 +89,24 @@ async function test() { // $ExpectError refreshAuthorizationToken(''); + // $ExpectType Promise + revokeAuthorizationToken('', { clientID: '', clientSecret: '', tokenHintType: 'refresh_token' }); + + // $ExpectType Promise + revokeAuthorizationToken('', { clientID: '', clientSecret: '', tokenHintType: 'access_token' }); + + // $ExpectError + revokeAuthorizationToken('', { clientID: '', clientSecret: '', tokenHintType: '' }); + + // $ExpectError + revokeAuthorizationToken('', { clientID: '', clientSecret: '' }); + + // $ExpectError + revokeAuthorizationToken('', { clientID: '' }); + + // $ExpectError + revokeAuthorizationToken(''); + const { aud, email, From 9872c5ad5f85594edbd6fd546c8f38c6b84a0d60 Mon Sep 17 00:00:00 2001 From: Robert Polovitzer Date: Sun, 26 Jun 2022 13:12:10 -0400 Subject: [PATCH 5/5] Fix revoke parameter name --- README.md | 4 ++-- __tests__/index.test.js | 6 +++--- src/index.js | 4 ++-- typescript/index.d.ts | 2 +- typescript/test.ts | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 4c12538..638ccd2 100644 --- a/README.md +++ b/README.md @@ -151,7 +151,7 @@ const clientSecret = appleSignin.getClientSecret({ const options = { clientID: 'com.company.app', // Apple Client ID clientSecret, - tokenHintType: 'refresh_token' + tokenTypeHint: 'refresh_token' }; try { @@ -176,7 +176,7 @@ const clientSecret = appleSignin.getClientSecret({ const options = { clientID: 'com.company.app', // Apple Client ID clientSecret, - tokenHintType: 'access_token' + tokenTypeHint: 'access_token' }; try { diff --git a/__tests__/index.test.js b/__tests__/index.test.js index d06232b..8be75e5 100644 --- a/__tests__/index.test.js +++ b/__tests__/index.test.js @@ -42,7 +42,7 @@ describe('test for each functions', () => { const option = { clientID: 'clientID', clientSecret: 'clientSecret', - tokenHintType: 'refresh_token', + tokenTypeHint: 'refresh_token', }; it('Should not throw error when response is empty', async () => { const result = await appleSignin.revokeAuthorizationToken(token, option); @@ -55,7 +55,7 @@ describe('test for each functions', () => { const option = { clientID: 'clientID', clientSecret: 'clientSecret', - tokenHintType: 'refresh_token', + tokenTypeHint: 'refresh_token', }; it('Should not throw error when response is empty', async () => { const result = await appleSignin.getAuthorizationToken(code, option); @@ -70,7 +70,7 @@ describe('test for each functions', () => { const option = { clientID: 'clientID', clientSecret: 'clientSecret', - tokenHintType: 'refresh_token', + tokenTypeHint: 'refresh_token', }; it('Should not throw error when response is empty', async () => { const result = await appleSignin.refreshAuthorizationToken( diff --git a/src/index.js b/src/index.js index 96fa1d5..8b18986 100644 --- a/src/index.js +++ b/src/index.js @@ -272,7 +272,7 @@ const revokeAuthorizationToken = async ( options: { clientID: string, clientSecret: string, - tokenHintType: 'refresh_token' | 'access_token', + tokenTypeHint: 'refresh_token' | 'access_token', }, ): Promise => { if (!options.clientID) { @@ -289,7 +289,7 @@ const revokeAuthorizationToken = async ( params.append('client_id', options.clientID); params.append('client_secret', options.clientSecret); params.append('token', token); - params.append('token_hint_type', options.tokenHintType); + params.append('token_type_hint', options.tokenTypeHint); const result = await fetch(url.toString(), { method: 'POST', diff --git a/typescript/index.d.ts b/typescript/index.d.ts index 6d3a9df..9741b24 100644 --- a/typescript/index.d.ts +++ b/typescript/index.d.ts @@ -120,7 +120,7 @@ declare function revokeAuthorizationToken( options: { clientID: string; clientSecret: string; - tokenHintType: 'refresh_token' | 'access_token'; + tokenTypeHint: 'refresh_token' | 'access_token'; }, ): Promise; diff --git a/typescript/test.ts b/typescript/test.ts index a29ebf2..d572a37 100644 --- a/typescript/test.ts +++ b/typescript/test.ts @@ -90,13 +90,13 @@ async function test() { refreshAuthorizationToken(''); // $ExpectType Promise - revokeAuthorizationToken('', { clientID: '', clientSecret: '', tokenHintType: 'refresh_token' }); + revokeAuthorizationToken('', { clientID: '', clientSecret: '', tokenTypeHint: 'refresh_token' }); // $ExpectType Promise - revokeAuthorizationToken('', { clientID: '', clientSecret: '', tokenHintType: 'access_token' }); + revokeAuthorizationToken('', { clientID: '', clientSecret: '', tokenTypeHint: 'access_token' }); // $ExpectError - revokeAuthorizationToken('', { clientID: '', clientSecret: '', tokenHintType: '' }); + revokeAuthorizationToken('', { clientID: '', clientSecret: '', tokenTypeHint: '' }); // $ExpectError revokeAuthorizationToken('', { clientID: '', clientSecret: '' });