Bug Description
useAuth().organizationId is always null even when the onRefresh callback receives a valid organizationId. This causes issues when checking if a user has an organization after authentication.
Environment
@workos-inc/authkit-react: 0.16.0
@workos-inc/authkit-js: 0.17.0
- React: 18.x
- Browser: Chrome (also reproducible in other browsers)
Steps to Reproduce
- Configure
AuthKitProvider with onRefresh callback
- User authenticates and selects an organization during WorkOS login flow
- Check
useAuth().organizationId after authentication completes
Expected Behavior
useAuth().organizationId should return the organization ID (e.g., "org_01KEWJVGA8A9WS4QT6F22MBCS8")
Actual Behavior
useAuth().organizationId returns null, even though:
- The WorkOS API returns
organization_id in the authentication response
- The
onRefresh callback receives the correct organizationId
- The JWT contains the correct
org_id claim
Console Evidence
Adding logging to verify:
<AuthKitProvider
onRefresh={(response) => {
console.log('onRefresh organizationId:', response.organizationId);
}}
>
Output:
onRefresh organizationId: org_01KEWJVGA8A9WS4QT6F22MBCS8
// But useAuth() returns { organizationId: null } on every subsequent render
Root Cause Analysis
The bug is in AuthKitProvider in src/provider.tsx. Here's the problematic flow:
| Step |
What Happens |
organizationId State |
| 1 |
createClient() is called |
null (initial) |
| 2 |
Session refresh triggers onRefresh callback |
null |
| 3 |
handleRefresh calls setState() with correct organizationId |
✅ "org_01..." |
| 4 |
createClient() promise resolves |
"org_01..." |
| 5 |
.then() block calls setState(readyState) |
❌ null |
The Bug (lines ~196-197 in compiled code)
// In the .then() block after createClient resolves:
const readyState = { ...initialState, isLoading: false, user };
setState(readyState); // BUG: This overwrites organizationId set by handleRefresh!
The problem is that readyState spreads from initialState, which has organizationId: null. This overwrites the organizationId that was correctly set by handleRefresh moments earlier.
Proposed Fix
Change the .then() block to preserve existing state instead of resetting to initialState:
- const readyState = { ...initialState, isLoading: false, user };
- setState(readyState);
+ setState((prev) => ({ ...prev, isLoading: false, user }));
Workaround
Until this is fixed, we're using a workaround that captures organizationId from onRefresh before it gets overwritten:
// Create a context to store the real organizationId
const OrgIdContext = createContext<string | null>(null);
function AuthKitWrapper({ children }) {
const [orgId, setOrgId] = useState<string | null>(null);
return (
<OrgIdContext.Provider value={orgId}>
<AuthKitProvider
onRefresh={(response) => {
// Capture before authkit-react resets it
if (response.organizationId) {
setOrgId(response.organizationId);
}
}}
>
{children}
</AuthKitProvider>
</OrgIdContext.Provider>
);
}
// Custom hook that uses the workaround
function useAuth() {
const authKitState = useAuthKit();
const orgIdFromRefresh = useContext(OrgIdContext);
return {
...authKitState,
organizationId: orgIdFromRefresh ?? authKitState.organizationId,
};
}
Impact
This bug breaks any application that:
- Checks
organizationId to determine if a user has selected an organization
- Uses
organizationId for authorization decisions
- Redirects based on organization membership
Without the workaround, this causes infinite redirect loops when checking if (!organizationId) { signIn() }.
Bug Description
useAuth().organizationIdis alwaysnulleven when theonRefreshcallback receives a validorganizationId. This causes issues when checking if a user has an organization after authentication.Environment
@workos-inc/authkit-react: 0.16.0@workos-inc/authkit-js: 0.17.0Steps to Reproduce
AuthKitProviderwithonRefreshcallbackuseAuth().organizationIdafter authentication completesExpected Behavior
useAuth().organizationIdshould return the organization ID (e.g.,"org_01KEWJVGA8A9WS4QT6F22MBCS8")Actual Behavior
useAuth().organizationIdreturnsnull, even though:organization_idin the authentication responseonRefreshcallback receives the correctorganizationIdorg_idclaimConsole Evidence
Adding logging to verify:
Output:
Root Cause Analysis
The bug is in
AuthKitProviderinsrc/provider.tsx. Here's the problematic flow:organizationIdStatecreateClient()is callednull(initial)onRefreshcallbacknullhandleRefreshcallssetState()with correctorganizationId"org_01..."createClient()promise resolves"org_01...".then()block callssetState(readyState)nullThe Bug (lines ~196-197 in compiled code)
The problem is that
readyStatespreads frominitialState, which hasorganizationId: null. This overwrites theorganizationIdthat was correctly set byhandleRefreshmoments earlier.Proposed Fix
Change the
.then()block to preserve existing state instead of resetting toinitialState:Workaround
Until this is fixed, we're using a workaround that captures
organizationIdfromonRefreshbefore it gets overwritten:Impact
This bug breaks any application that:
organizationIdto determine if a user has selected an organizationorganizationIdfor authorization decisionsWithout the workaround, this causes infinite redirect loops when checking
if (!organizationId) { signIn() }.