Skip to content

Commit

Permalink
Merge pull request #820 from commercelayer/token-provider-extras-user
Browse files Browse the repository at this point in the history
Allow to pass an user from TokenProvider `extras` prop
  • Loading branch information
gciotola authored Oct 30, 2024
2 parents 0afb825 + 894e90e commit 1b3b232
Show file tree
Hide file tree
Showing 4 changed files with 90 additions and 3 deletions.
Original file line number Diff line number Diff line change
@@ -1,5 +1,9 @@
import { type TokenProviderTokenApplicationKind } from '#providers/TokenProvider'
import { decodeExtras, getExtrasFromUrl } from '#providers/TokenProvider/extras'
import {
decodeExtras,
getExtrasFromUrl,
isValidUser
} from '#providers/TokenProvider/extras'
import { extractDomainFromApiBaseEndpoint } from '#providers/TokenProvider/url'
import { PageError } from '#ui/composite/PageError'
import { PageSkeleton } from '#ui/composite/PageSkeleton'
Expand Down Expand Up @@ -243,6 +247,11 @@ export const TokenProvider: React.FC<TokenProviderProps> = ({

removeAuthParamsFromUrl()

const userFromExtras =
extrasFromProp?.user != null && isValidUser(extrasFromProp?.user)
? extrasFromProp.user
: null

dispatch({
type: 'validToken',
payload: {
Expand All @@ -258,7 +267,7 @@ export const TokenProvider: React.FC<TokenProviderProps> = ({
scopes: tokenInfo.scopes,
extras
},
user: tokenInfo.user,
user: tokenInfo.user ?? userFromExtras,
organization,
rolePermissions: tokenInfo.permissions ?? {},
accessibleApps: tokenInfo.accessibleApps ?? []
Expand Down
42 changes: 42 additions & 0 deletions packages/app-elements/src/providers/TokenProvider/extras.test.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { isValidUser } from '#providers/TokenProvider/extras'
import { decodeExtras, encodeExtras, getExtrasFromUrl } from './extras'
import type { TokenProviderExtras } from './types'

Expand Down Expand Up @@ -116,3 +117,44 @@ describe('Encode object > Add in URL query string > Decode it from URL', () => {
expect(decodeExtras(getExtrasFromUrl())).toEqual(undefined)
})
})

describe('isValidUser', () => {
test('should return true if user is valid', () => {
const user = {
id: '1',
email: '[email protected]',
displayName: 'J.Doe',
firstName: 'John',
lastName: 'Doe',
fullName: 'John Doe',
timezone: 'UTC'
}
expect(isValidUser(user)).toBe(true)
})

test('should return false if user is null', () => {
expect(isValidUser(null)).toBe(false)
})

test('should return false if user is undefined', () => {
expect(isValidUser(undefined)).toBe(false)
})

test('should return false if user is empty', () => {
// @ts-expect-error mismatching type for testing invalid user
expect(isValidUser({})).toBe(false)
})

test('should return false if user is missing keys', () => {
const user = {
id: '1',
email: '[email protected]',
firstName: 'John',
lastName: 'Doe',
fullName: 'John Doe',
timezone: 'UTC'
}
// @ts-expect-error mismatching type for testing invalid user
expect(isValidUser(user)).toBe(false)
})
})
36 changes: 35 additions & 1 deletion packages/app-elements/src/providers/TokenProvider/extras.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import isEmpty from 'lodash/isEmpty'
import type { TokenProviderExtras } from './types'
import type { TokenProviderAuthUser, TokenProviderExtras } from './types'

/**
* Encodes the given extras object into a Base64 string.
Expand Down Expand Up @@ -72,3 +72,37 @@ const base64URLSafe = {
return Buffer.from(encodedData, 'base64url').toString('binary')
}
}

/**
* Validates if the user object received from `extras` is a valid one and can be added to the TokenProvider context.
*/
export function isValidUser(
user?: TokenProviderAuthUser | null
): user is TokenProviderAuthUser {
const compareKeys = Object.keys({
id: '',
email: '',
firstName: '',
lastName: '',
displayName: '',
fullName: '',
timezone: ''
} satisfies TokenProviderAuthUser).sort()

if (user == null || isEmpty(user)) {
return false
}

if (isEmpty(user.email) || isEmpty(user.id)) {
return false
}

if (isEmpty(user.firstName) && isEmpty(user.lastName)) {
// at least one of the them should not be empty string
return false
}

return Object.keys(user)
.sort()
.every((key, index) => key === compareKeys[index])
}
2 changes: 2 additions & 0 deletions packages/app-elements/src/providers/TokenProvider/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -173,4 +173,6 @@ export interface TokenProviderExtras {
organizations?: number
skus?: number
}
/** Allow to pass the current user in the app context when using an access token that does not contain an owner. */
user?: TokenProviderAuthUser
}

0 comments on commit 1b3b232

Please sign in to comment.