Skip to content

Commit

Permalink
feat: add 'revoke' helper method
Browse files Browse the repository at this point in the history
  • Loading branch information
marcomontalbano committed Mar 26, 2024
1 parent d561938 commit 47c400c
Show file tree
Hide file tree
Showing 6 changed files with 98 additions and 3 deletions.
15 changes: 15 additions & 0 deletions packages/js-auth/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ A JavaScript Library wrapper that helps you use the Commerce Layer API for [Auth
- [Integration application with client credentials flow](#integration-client-credentials)
- [Webapp application with authorization code flow](#webapp-authorization-code)
- [Provisioning application](#provisioning)
- [Revoking a token](#revoking-a-token)
- [Utilities](#utilities)
- [Decode an access token](#decode-an-access-token)
- [Contributors guide](#contributors-guide)
Expand Down Expand Up @@ -205,6 +206,20 @@ console.log('My access token: ', auth.accessToken)
console.log('Expiration date: ', auth.expires)
```
### Revoking a token
Any previously generated access tokens (refresh tokens included) can be revoked before their natural expiration date.
```ts
import { revoke } from '@commercelayer/js-auth'
await revoke({
clientId: 'your-client-id',
clientSecret: 'your-client-secret',
token: 'a-generated-access-token'
})
```
## Utilities
### Decode an access token
Expand Down
4 changes: 3 additions & 1 deletion packages/js-auth/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,12 @@
export { authenticate } from './authenticate.js'
export { revoke } from './revoke.js'

export {
jwtDecode,
jwtIsDashboard,
jwtIsIntegration,
jwtIsUser,
jwtIsSalesChannel,
jwtIsUser,
jwtIsWebApp
} from './jwtDecode.js'

Expand Down
44 changes: 44 additions & 0 deletions packages/js-auth/src/revoke.spec.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
import { authenticate, revoke } from './index.js'

const integrationClientId = process.env.VITE_TEST_INTEGRATION_CLIENT_ID
const clientSecret = process.env.VITE_TEST_CLIENT_SECRET
const domain = process.env.VITE_TEST_DOMAIN

describe('Revoke', () => {
it('should be able to revoke a valid integration accessToken', async () => {
const authenticateResponse = await authenticate('client_credentials', {
clientId: integrationClientId,
clientSecret,
domain
})

expect(authenticateResponse).toHaveProperty('accessToken')

const revokeResponse = await revoke({
clientId: integrationClientId,
clientSecret,
token: authenticateResponse.accessToken,
domain
})

expect(revokeResponse).toStrictEqual({})
})

it('should respond with error when something goes wrong', async () => {
// @ts-expect-error I need to test this scenario
const revokeResponse = await revoke({
clientSecret,
token: '1234',
domain
})

expect(revokeResponse).toHaveProperty('errors')
expect(revokeResponse.errors).toBeInstanceOf(Array)
expect(revokeResponse.errors?.[0]).toMatchObject({
code: 'FORBIDDEN',
detail: 'You are not authorized to revoke this token',
status: 403,
title: 'unauthorized_client'
})
})
})
22 changes: 22 additions & 0 deletions packages/js-auth/src/revoke.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
import type { RevokeOptions, RevokeReturn } from '#types/index.js'

import { camelCaseToSnakeCase } from '#utils/camelCaseToSnakeCase.js'
import { mapKeys } from '#utils/mapKeys.js'

export async function revoke({
domain = 'commercelayer.io',
...options
}: RevokeOptions): Promise<RevokeReturn> {
const body = mapKeys(options, camelCaseToSnakeCase)

const response = await fetch(`https://auth.${domain}/oauth/revoke`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
Accept: 'application/json'
},
body: JSON.stringify(body)
})

return (await response.json()) as RevokeReturn
}
5 changes: 4 additions & 1 deletion packages/js-auth/src/types/base.ts
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ export interface TBaseOptions {
}
}

export interface TBaseReturn {
export type TBaseReturn = {
/**
* The access token.
*/
Expand All @@ -64,6 +64,9 @@ export interface TBaseReturn {
* The creation date of the access token.
*/
createdAt: number
} & TError

export interface TError {
/**
* The list of errors when something goes wrong.
*/
Expand Down
11 changes: 10 additions & 1 deletion packages/js-auth/src/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import type {
TAuthorizationCodeOptions,
TAuthorizationCodeReturn
} from './authorizationCode.js'
import type { TBaseReturn } from './base.js'
import type { TBaseOptions, TBaseReturn, TError } from './base.js'
import type { TClientCredentialsOptions } from './clientCredentials.js'
import type { TPasswordOptions, TPasswordReturn } from './password.js'
import type { TRefreshTokenOptions } from './refreshToken.js'
Expand Down Expand Up @@ -37,3 +37,12 @@ export type AuthenticateReturn<TGrantType extends GrantType> =
: TGrantType extends 'authorization_code'
? TAuthorizationCodeReturn
: never

export type RevokeOptions = Pick<TBaseOptions, 'clientId' | 'domain'> & {
/** Your application's client secret (required for confidential API credentials and non-confidential API credentials without a customer or a user in the JWT only). */
clientSecret?: string
/** A valid access or refresh token. */
token: string
}

export type RevokeReturn = Pick<TError, 'errors'>

0 comments on commit 47c400c

Please sign in to comment.