From d339f4dd7372eeb506f2bc458a83336fff7a8c21 Mon Sep 17 00:00:00 2001 From: ijxy <32483798+ijxy@users.noreply.github.com> Date: Fri, 20 Dec 2024 12:13:59 +0000 Subject: [PATCH] feat(astro,clerk-react,types,vue): expose `sessionClaims` from `useAuth` hook --- .changeset/honest-oranges-accept.md | 8 +++ packages/astro/src/react/hooks.ts | 10 +++- packages/react/src/contexts/AuthContext.ts | 3 +- .../src/hooks/__tests__/useAuth.test.tsx | 60 +++++++++++++------ packages/react/src/hooks/useAuth.ts | 13 +++- packages/types/src/hooks.ts | 5 ++ packages/vue/src/composables/useAuth.ts | 10 +++- packages/vue/src/plugin.ts | 4 +- packages/vue/src/types.ts | 2 + 9 files changed, 84 insertions(+), 31 deletions(-) create mode 100644 .changeset/honest-oranges-accept.md diff --git a/.changeset/honest-oranges-accept.md b/.changeset/honest-oranges-accept.md new file mode 100644 index 0000000000..94e7dfa4f3 --- /dev/null +++ b/.changeset/honest-oranges-accept.md @@ -0,0 +1,8 @@ +--- +'@clerk/astro': patch +'@clerk/clerk-react': patch +'@clerk/types': patch +'@clerk/vue': patch +--- + +Expose `sessionClaims` from `useAuth` hooks diff --git a/packages/astro/src/react/hooks.ts b/packages/astro/src/react/hooks.ts index 6acc982a93..7950377d58 100644 --- a/packages/astro/src/react/hooks.ts +++ b/packages/astro/src/react/hooks.ts @@ -78,7 +78,7 @@ type UseAuth = () => UseAuthReturn; * } */ export const useAuth: UseAuth = () => { - const { sessionId, userId, actor, orgId, orgRole, orgSlug, orgPermissions } = useStore($authStore); + const { sessionId, sessionClaims, userId, actor, orgId, orgRole, orgSlug, orgPermissions } = useStore($authStore); const getToken: GetToken = useCallback(createGetToken(), []); const signOut: SignOut = useCallback(createSignOut(), []); @@ -113,6 +113,7 @@ export const useAuth: UseAuth = () => { isLoaded: false, isSignedIn: undefined, sessionId, + sessionClaims: undefined, userId, actor: undefined, orgId: undefined, @@ -129,6 +130,7 @@ export const useAuth: UseAuth = () => { isLoaded: true, isSignedIn: false, sessionId, + sessionClaims: null, userId, actor: null, orgId: null, @@ -140,11 +142,12 @@ export const useAuth: UseAuth = () => { }; } - if (!!sessionId && !!userId && !!orgId && !!orgRole) { + if (!!sessionId && !!sessionClaims && !!userId && !!orgId && !!orgRole) { return { isLoaded: true, isSignedIn: true, sessionId, + sessionClaims, userId, actor: actor || null, orgId, @@ -156,11 +159,12 @@ export const useAuth: UseAuth = () => { }; } - if (!!sessionId && !!userId && !orgId) { + if (!!sessionId && !!sessionClaims && !!userId && !orgId) { return { isLoaded: true, isSignedIn: true, sessionId, + sessionClaims, userId, actor: actor || null, orgId: null, diff --git a/packages/react/src/contexts/AuthContext.ts b/packages/react/src/contexts/AuthContext.ts index 02d3919620..bed3baf52b 100644 --- a/packages/react/src/contexts/AuthContext.ts +++ b/packages/react/src/contexts/AuthContext.ts @@ -1,9 +1,10 @@ import { createContextAndHook } from '@clerk/shared/react'; -import type { ActJWTClaim, OrganizationCustomPermissionKey, OrganizationCustomRoleKey } from '@clerk/types'; +import type { ActJWTClaim, JwtPayload, OrganizationCustomPermissionKey, OrganizationCustomRoleKey } from '@clerk/types'; export type AuthContextValue = { userId: string | null | undefined; sessionId: string | null | undefined; + sessionClaims: JwtPayload | null | undefined; actor: ActJWTClaim | null | undefined; orgId: string | null | undefined; orgRole: OrganizationCustomRoleKey | null | undefined; diff --git a/packages/react/src/hooks/__tests__/useAuth.test.tsx b/packages/react/src/hooks/__tests__/useAuth.test.tsx index c4272cff3b..03bc16519c 100644 --- a/packages/react/src/hooks/__tests__/useAuth.test.tsx +++ b/packages/react/src/hooks/__tests__/useAuth.test.tsx @@ -1,6 +1,6 @@ import { createCheckAuthorization } from '@clerk/shared/authorization'; import { ClerkInstanceContext } from '@clerk/shared/react'; -import type { LoadedClerk } from '@clerk/types'; +import type { LoadedClerk, UseAuthReturn } from '@clerk/types'; import { render, renderHook } from '@testing-library/react'; import React from 'react'; import { afterAll, beforeAll, beforeEach, describe, expect, it, test, vi } from 'vitest'; @@ -24,6 +24,21 @@ vi.mock('../../errors/errorThrower', () => ({ }, })); +const stubSessionClaims = (input: { + sessionId: string; + userId: string; + orgId?: string; +}): NonNullable => ({ + __raw: '', + exp: 1, + iat: 1, + iss: '', + nbf: 1, + sid: input.sessionId, + sub: input.userId, + org_id: input.orgId, +}); + const TestComponent = () => { const { isLoaded, isSignedIn } = useAuth(); return ( @@ -77,6 +92,7 @@ describe('useDerivedAuth', () => { expect(current.isLoaded).toBe(false); expect(current.isSignedIn).toBeUndefined(); expect(current.sessionId).toBeUndefined(); + expect(current.sessionClaims).toBeUndefined(); expect(current.userId).toBeUndefined(); expect(current.actor).toBeUndefined(); expect(current.orgId).toBeUndefined(); @@ -92,6 +108,7 @@ describe('useDerivedAuth', () => { expect(current.isLoaded).toBe(true); expect(current.isSignedIn).toBe(false); expect(current.sessionId).toBeNull(); + expect(current.sessionClaims).toBeNull(); expect(current.userId).toBeNull(); expect(current.actor).toBeNull(); expect(current.orgId).toBeNull(); @@ -104,14 +121,15 @@ describe('useDerivedAuth', () => { it('returns signed in with org context when sessionId, userId, orgId, and orgRole are present', () => { const authObject = { sessionId: 'session123', + sessionClaims: stubSessionClaims({ sessionId: 'session123', userId: 'user123' }), userId: 'user123', - actor: 'actor123', + actor: { sub: 'actor123' }, orgId: 'org123', orgRole: 'admin', orgSlug: 'my-org', signOut: vi.fn(), getToken: vi.fn(), - }; + } satisfies Partial; const { result: { current }, @@ -119,12 +137,13 @@ describe('useDerivedAuth', () => { expect(current.isLoaded).toBe(true); expect(current.isSignedIn).toBe(true); - expect(current.sessionId).toBe('session123'); - expect(current.userId).toBe('user123'); - expect(current.actor).toBe('actor123'); - expect(current.orgId).toBe('org123'); - expect(current.orgRole).toBe('admin'); - expect(current.orgSlug).toBe('my-org'); + expect(current.sessionId).toBe(authObject.sessionId); + expect(current.userId).toBe(authObject.userId); + expect(current.sessionClaims).toBe(authObject.sessionClaims); + expect(current.actor?.sub).toBe(authObject.actor.sub); + expect(current.orgId).toBe(authObject.orgId); + expect(current.orgRole).toBe(authObject.orgRole); + expect(current.orgSlug).toBe(authObject.orgSlug); expect(typeof current.has).toBe('function'); expect(current.signOut).toBe(authObject.signOut); expect(current.getToken).toBe(authObject.getToken); @@ -136,21 +155,23 @@ describe('useDerivedAuth', () => { it('returns signed in without org context when sessionId and userId are present but no orgId', () => { const authObject = { - sessionId: 'session123', userId: 'user123', - actor: 'actor123', + sessionId: 'session123', + sessionClaims: stubSessionClaims({ sessionId: 'session123', userId: 'user123' }), + actor: { sub: 'actor123' }, signOut: vi.fn(), getToken: vi.fn(), - }; + } satisfies Partial; const { result: { current }, } = renderHook(() => useDerivedAuth(authObject)); expect(current.isLoaded).toBe(true); expect(current.isSignedIn).toBe(true); - expect(current.sessionId).toBe('session123'); - expect(current.userId).toBe('user123'); - expect(current.actor).toBe('actor123'); + expect(current.sessionId).toBe(authObject.sessionId); + expect(current.userId).toBe(authObject.userId); + expect(current.sessionClaims).toBe(authObject.sessionClaims); + expect(current.actor?.sub).toBe(authObject.actor.sub); expect(current.orgId).toBeNull(); expect(current.orgRole).toBeNull(); expect(current.orgSlug).toBeNull(); @@ -165,9 +186,9 @@ describe('useDerivedAuth', () => { it('throws invalid state error if none of the conditions match', () => { const authObject = { - sessionId: true, userId: undefined, - }; + sessionId: 'session123', + } satisfies Partial; renderHook(() => useDerivedAuth(authObject)); // eslint-disable-next-line @typescript-eslint/unbound-method @@ -177,10 +198,11 @@ describe('useDerivedAuth', () => { it('uses provided has function if available', () => { const mockHas = vi.fn().mockReturnValue('mocked-result'); const authObject = { - sessionId: 'session123', userId: 'user123', + sessionId: 'session123', + sessionClaims: stubSessionClaims({ sessionId: 'session123', userId: 'user123' }), has: mockHas, - }; + } satisfies Partial; const { result: { current }, } = renderHook(() => useDerivedAuth(authObject)); diff --git a/packages/react/src/hooks/useAuth.ts b/packages/react/src/hooks/useAuth.ts index 4598fd4226..6af4d5020b 100644 --- a/packages/react/src/hooks/useAuth.ts +++ b/packages/react/src/hooks/useAuth.ts @@ -57,7 +57,8 @@ export const useAuth: UseAuth = (initialAuthState = {}) => { authContext = initialAuthState != null ? initialAuthState : {}; } - const { sessionId, userId, actor, orgId, orgRole, orgSlug, orgPermissions, factorVerificationAge } = authContext; + const { sessionId, sessionClaims, userId, actor, orgId, orgRole, orgSlug, orgPermissions, factorVerificationAge } = + authContext; const isomorphicClerk = useIsomorphicClerkContext(); const getToken: GetToken = useCallback(createGetToken(isomorphicClerk), [isomorphicClerk]); @@ -65,6 +66,7 @@ export const useAuth: UseAuth = (initialAuthState = {}) => { return useDerivedAuth({ sessionId, + sessionClaims, userId, actor, orgId, @@ -106,6 +108,7 @@ export const useAuth: UseAuth = (initialAuthState = {}) => { export function useDerivedAuth(authObject: any): UseAuthReturn { const { sessionId, + sessionClaims, userId, actor, orgId, @@ -139,6 +142,7 @@ export function useDerivedAuth(authObject: any): UseAuthReturn { isLoaded: false, isSignedIn: undefined, sessionId, + sessionClaims: undefined, userId, actor: undefined, orgId: undefined, @@ -155,6 +159,7 @@ export function useDerivedAuth(authObject: any): UseAuthReturn { isLoaded: true, isSignedIn: false, sessionId, + sessionClaims: null, userId, actor: null, orgId: null, @@ -166,11 +171,12 @@ export function useDerivedAuth(authObject: any): UseAuthReturn { }; } - if (!!sessionId && !!userId && !!orgId && !!orgRole) { + if (!!sessionId && !!sessionClaims && !!userId && !!orgId && !!orgRole) { return { isLoaded: true, isSignedIn: true, sessionId, + sessionClaims, userId, actor: actor || null, orgId, @@ -182,11 +188,12 @@ export function useDerivedAuth(authObject: any): UseAuthReturn { }; } - if (!!sessionId && !!userId && !orgId) { + if (!!sessionId && !!sessionClaims && !!userId && !orgId) { return { isLoaded: true, isSignedIn: true, sessionId, + sessionClaims, userId, actor: actor || null, orgId: null, diff --git a/packages/types/src/hooks.ts b/packages/types/src/hooks.ts index 1b5d4ab0b0..66dcae3282 100644 --- a/packages/types/src/hooks.ts +++ b/packages/types/src/hooks.ts @@ -3,6 +3,7 @@ import type { SignInResource } from 'signIn'; import type { SetActive, SignOut } from './clerk'; import type { ActJWTClaim } from './jwt'; +import type { JwtPayload } from './jwtv2'; import type { ActiveSessionResource, CheckAuthorizationWithCustomPermissions, @@ -21,6 +22,7 @@ export type UseAuthReturn = isSignedIn: undefined; userId: undefined; sessionId: undefined; + sessionClaims: undefined; actor: undefined; orgId: undefined; orgRole: undefined; @@ -34,6 +36,7 @@ export type UseAuthReturn = isSignedIn: false; userId: null; sessionId: null; + sessionClaims: null; actor: null; orgId: null; orgRole: null; @@ -47,6 +50,7 @@ export type UseAuthReturn = isSignedIn: true; userId: string; sessionId: string; + sessionClaims: JwtPayload; actor: ActJWTClaim | null; orgId: null; orgRole: null; @@ -60,6 +64,7 @@ export type UseAuthReturn = isSignedIn: true; userId: string; sessionId: string; + sessionClaims: JwtPayload; actor: ActJWTClaim | null; orgId: string; orgRole: OrganizationCustomRoleKey; diff --git a/packages/vue/src/composables/useAuth.ts b/packages/vue/src/composables/useAuth.ts index 3a39de0734..d9854f7285 100644 --- a/packages/vue/src/composables/useAuth.ts +++ b/packages/vue/src/composables/useAuth.ts @@ -74,7 +74,7 @@ export const useAuth: UseAuth = () => { const { clerk, authCtx } = useClerkContext(); const result = computed(() => { - const { sessionId, userId, actor, orgId, orgRole, orgSlug, orgPermissions } = authCtx.value; + const { sessionId, sessionClaims, userId, actor, orgId, orgRole, orgSlug, orgPermissions } = authCtx.value; const getToken: GetToken = createGetToken(clerk); const signOut: SignOut = createSignOut(clerk); @@ -103,6 +103,7 @@ export const useAuth: UseAuth = () => { isLoaded: false, isSignedIn: undefined, sessionId, + sessionClaims: undefined, userId, actor: undefined, orgId: undefined, @@ -119,6 +120,7 @@ export const useAuth: UseAuth = () => { isLoaded: true, isSignedIn: false, sessionId, + sessionClaims: null, userId, actor: null, orgId: null, @@ -130,11 +132,12 @@ export const useAuth: UseAuth = () => { }; } - if (!!sessionId && !!userId && !!orgId && !!orgRole) { + if (!!sessionId && !!sessionClaims && !!userId && !!orgId && !!orgRole) { return { isLoaded: true, isSignedIn: true, sessionId, + sessionClaims, userId, actor: actor || null, orgId, @@ -146,11 +149,12 @@ export const useAuth: UseAuth = () => { }; } - if (!!sessionId && !!userId && !orgId) { + if (!!sessionId && !!sessionClaims && !!userId && !orgId) { return { isLoaded: true, isSignedIn: true, sessionId, + sessionClaims, userId, actor: actor || null, orgId: null, diff --git a/packages/vue/src/plugin.ts b/packages/vue/src/plugin.ts index ab5acddaff..92b97e0e29 100644 --- a/packages/vue/src/plugin.ts +++ b/packages/vue/src/plugin.ts @@ -70,8 +70,8 @@ export const clerkPlugin: Plugin = { const derivedState = computed(() => deriveState(loaded.value, resources.value, initialState)); const authCtx = computed(() => { - const { sessionId, userId, orgId, actor, orgRole, orgSlug, orgPermissions } = derivedState.value; - return { sessionId, userId, actor, orgId, orgRole, orgSlug, orgPermissions }; + const { sessionId, sessionClaims, userId, orgId, actor, orgRole, orgSlug, orgPermissions } = derivedState.value; + return { sessionId, sessionClaims, userId, actor, orgId, orgRole, orgSlug, orgPermissions }; }); const clientCtx = computed(() => resources.value.client); const userCtx = computed(() => derivedState.value.user); diff --git a/packages/vue/src/types.ts b/packages/vue/src/types.ts index 213c909b65..beb6695941 100644 --- a/packages/vue/src/types.ts +++ b/packages/vue/src/types.ts @@ -6,6 +6,7 @@ import type { ClientResource, CustomMenuItem, CustomPage, + JwtPayload, OrganizationCustomPermissionKey, OrganizationCustomRoleKey, OrganizationResource, @@ -19,6 +20,7 @@ export interface VueClerkInjectionKeyType { authCtx: ComputedRef<{ userId: string | null | undefined; sessionId: string | null | undefined; + sessionClaims: JwtPayload | null | undefined; actor: ActJWTClaim | null | undefined; orgId: string | null | undefined; orgRole: OrganizationCustomRoleKey | null | undefined;