From 99563227aa35f905623f50c8b1649b186c935a39 Mon Sep 17 00:00:00 2001 From: Jacek Date: Sun, 14 Dec 2025 20:24:15 -0600 Subject: [PATCH 1/2] feat: ensure has is always defined --- .../src/hooks/__tests__/useAuth.test.tsx | 3 +- .../src/hooks/__tests__/useAuth.type.test.ts | 2 +- packages/shared/src/authorization.ts | 60 +++++++++---------- packages/shared/src/types/hooks.ts | 4 +- 4 files changed, 35 insertions(+), 34 deletions(-) diff --git a/packages/react/src/hooks/__tests__/useAuth.test.tsx b/packages/react/src/hooks/__tests__/useAuth.test.tsx index fcffe1bdc17..bc2d31347cb 100644 --- a/packages/react/src/hooks/__tests__/useAuth.test.tsx +++ b/packages/react/src/hooks/__tests__/useAuth.test.tsx @@ -98,7 +98,8 @@ describe('useDerivedAuth', () => { expect(current.orgId).toBeUndefined(); expect(current.orgRole).toBeUndefined(); expect(current.orgSlug).toBeUndefined(); - expect(current.has).toBeUndefined(); + expect(current.has).toBeInstanceOf(Function); + expect(current.has?.({ permission: 'test' })).toBe(false); }); it('returns loaded but not signed in when sessionId and userId are null', () => { diff --git a/packages/react/src/hooks/__tests__/useAuth.type.test.ts b/packages/react/src/hooks/__tests__/useAuth.type.test.ts index 34ae3a05176..1e29d04bc8a 100644 --- a/packages/react/src/hooks/__tests__/useAuth.type.test.ts +++ b/packages/react/src/hooks/__tests__/useAuth.type.test.ts @@ -4,7 +4,7 @@ import { describe, expectTypeOf, it } from 'vitest'; import type { useAuth } from '../useAuth'; type UseAuthParameters = Parameters[0]; -type HasFunction = Exclude['has'], undefined>; +type HasFunction = ReturnType['has']; type ParamsOfHas = Parameters[0]; describe('useAuth type tests', () => { diff --git a/packages/shared/src/authorization.ts b/packages/shared/src/authorization.ts index 301835604bb..0834ecb86e2 100644 --- a/packages/shared/src/authorization.ts +++ b/packages/shared/src/authorization.ts @@ -260,86 +260,86 @@ const resolveAuthState = ({ }: AuthStateOptions): UseAuthReturn | undefined => { if (sessionId === undefined && userId === undefined) { return { + actor: undefined, + getToken, + has: () => false, isLoaded: false, isSignedIn: undefined, - sessionId, - sessionClaims: undefined, - userId, - actor: undefined, orgId: undefined, orgRole: undefined, orgSlug: undefined, - has: undefined, + sessionClaims: undefined, + sessionId, signOut, - getToken, + userId, } as const; } if (sessionId === null && userId === null) { return { + actor: null, + getToken, + has: () => false, isLoaded: true, isSignedIn: false, - sessionId, - userId, - sessionClaims: null, - actor: null, orgId: null, orgRole: null, orgSlug: null, - has: () => false, + sessionClaims: null, + sessionId, signOut, - getToken, + userId, } as const; } if (treatPendingAsSignedOut && sessionStatus === 'pending') { return { + actor: null, + getToken, + has: () => false, isLoaded: true, isSignedIn: false, - sessionId: null, - userId: null, - sessionClaims: null, - actor: null, orgId: null, orgRole: null, orgSlug: null, - has: () => false, + sessionClaims: null, + sessionId: null, signOut, - getToken, + userId: null, } as const; } if (!!sessionId && !!sessionClaims && !!userId && !!orgId && !!orgRole) { return { + actor: actor || null, + getToken, + has, isLoaded: true, isSignedIn: true, - sessionId, - sessionClaims, - userId, - actor: actor || null, orgId, orgRole, orgSlug: orgSlug || null, - has, + sessionClaims, + sessionId, signOut, - getToken, + userId, } as const; } if (!!sessionId && !!sessionClaims && !!userId && !orgId) { return { + actor: actor || null, + getToken, + has, isLoaded: true, isSignedIn: true, - sessionId, - sessionClaims, - userId, - actor: actor || null, orgId: null, orgRole: null, orgSlug: null, - has, + sessionClaims, + sessionId, signOut, - getToken, + userId, } as const; } }; diff --git a/packages/shared/src/types/hooks.ts b/packages/shared/src/types/hooks.ts index d0927ec8738..622d588478b 100644 --- a/packages/shared/src/types/hooks.ts +++ b/packages/shared/src/types/hooks.ts @@ -14,11 +14,11 @@ import type { UserResource } from './user'; /** * @inline */ -type CheckAuthorizationSignedOut = undefined; +type CheckAuthorizationWithoutOrgOrUser = (params: Parameters[0]) => false; /** * @inline */ -type CheckAuthorizationWithoutOrgOrUser = (params: Parameters[0]) => false; +type CheckAuthorizationSignedOut = CheckAuthorizationWithoutOrgOrUser; /** * @inline From 7f57e9ab8bee77d55cf4c06231b265b2837f9519 Mon Sep 17 00:00:00 2001 From: Jacek Date: Mon, 15 Dec 2025 10:02:14 -0600 Subject: [PATCH 2/2] changeset --- .changeset/steady-has-default.md | 6 ++++++ 1 file changed, 6 insertions(+) create mode 100644 .changeset/steady-has-default.md diff --git a/.changeset/steady-has-default.md b/.changeset/steady-has-default.md new file mode 100644 index 00000000000..f4e82d0b6d0 --- /dev/null +++ b/.changeset/steady-has-default.md @@ -0,0 +1,6 @@ +--- +'@clerk/react': patch +'@clerk/shared': patch +--- + +Ensure `useAuth().has` is always defined by defaulting to false when auth data is missing.