Context
When building an app that stores the authenticated session in its own state (e.g. a Hono context variable, a request-scoped store, or a React provider), I want a single named type that describes "the user-facing payload from a successful `session.authenticate()` or `session.refresh()`" — i.e. the fields it's safe and useful to expose to my app code.
Today the SDK exports:
- `SessionCookieData` —
Pick<AuthenticationResponse, 'accessToken' | 'authenticationMethod' | 'impersonator' | 'organizationId' | 'refreshToken' | 'user'>. This is the sealed-cookie shape and intentionally omits role, permissions, roles, entitlements, featureFlags.
- `AuthenticateWithSessionCookieSuccessResponse` — includes
authenticated: true, accessToken, sessionId, authenticationMethod alongside the user-facing fields. Useful as a return type, but heavyweight for app state.
- `RefreshSessionSuccessResponse` —
Omit<AuthenticateWithSessionCookieSuccessResponse, 'accessToken'> & { ... session?: AuthenticationResponse }.
There's no single exported type that says "this is the session payload your app should hold onto." Today, consumers either roll their own:
interface SessionData {
user: User;
organizationId?: string;
role?: string;
permissions?: string[];
impersonator?: Impersonator | null;
}
…or write a hand-rolled Omit<AuthenticateWithSessionCookieSuccessResponse, 'authenticated' | 'accessToken' | 'sessionId' | 'authenticationMethod'>, which is fragile and drifts as the SDK grows new claims (e.g. roles, entitlements, featureFlags).
Proposal
Export a Session interface (name TBD — SessionPayload, AuthenticatedSession, etc. also fine) representing the user-facing claims:
export interface Session {
user: User;
organizationId?: string;
role?: string;
roles?: string[];
permissions?: string[];
entitlements?: string[];
featureFlags?: string[];
impersonator?: Impersonator;
}
Both AuthenticateWithSessionCookieSuccessResponse and RefreshSessionSuccessResponse would extend it (or compose it via intersection), keeping their existing extra fields. Consumers could then write:
import type { Session } from '@workos-inc/node';
type AppEnv = {
Variables: {
session: Session;
};
};
…and assign authResult / refreshResult directly with no casts and no risk of drift when new JWT claims land in the SDK.
Why this is worth doing
- One fewer paper-cut for SDK consumers — most non-trivial apps that hold session state will benefit
- The "right" shape is centralized in the SDK rather than re-derived by every consumer
- New claims (e.g.
entitlements, featureFlags) automatically flow into consumer apps without TS errors
- Pairs naturally with
SessionCookieData (the sealed-cookie shape) — they're two clearly-distinguished concerns
References
Came up while reviewing a custom-UI example app for impersonation support — landed at workos/workos-custom-ui-authkit-example#9, where the original code used as unknown as SessionData to bridge the gap. Turns out the cast wasn't strictly required because the success response is structurally compatible with a hand-rolled interface, but having an officially-exported type would make the pattern obvious and resilient to future SDK changes.
Context
When building an app that stores the authenticated session in its own state (e.g. a Hono context variable, a request-scoped store, or a React provider), I want a single named type that describes "the user-facing payload from a successful `session.authenticate()` or `session.refresh()`" — i.e. the fields it's safe and useful to expose to my app code.
Today the SDK exports:
Pick<AuthenticationResponse, 'accessToken' | 'authenticationMethod' | 'impersonator' | 'organizationId' | 'refreshToken' | 'user'>. This is the sealed-cookie shape and intentionally omitsrole,permissions,roles,entitlements,featureFlags.authenticated: true,accessToken,sessionId,authenticationMethodalongside the user-facing fields. Useful as a return type, but heavyweight for app state.Omit<AuthenticateWithSessionCookieSuccessResponse, 'accessToken'> & { ... session?: AuthenticationResponse }.There's no single exported type that says "this is the session payload your app should hold onto." Today, consumers either roll their own:
…or write a hand-rolled
Omit<AuthenticateWithSessionCookieSuccessResponse, 'authenticated' | 'accessToken' | 'sessionId' | 'authenticationMethod'>, which is fragile and drifts as the SDK grows new claims (e.g.roles,entitlements,featureFlags).Proposal
Export a
Sessioninterface (name TBD —SessionPayload,AuthenticatedSession, etc. also fine) representing the user-facing claims:Both
AuthenticateWithSessionCookieSuccessResponseandRefreshSessionSuccessResponsewould extend it (or compose it via intersection), keeping their existing extra fields. Consumers could then write:…and assign
authResult/refreshResultdirectly with no casts and no risk of drift when new JWT claims land in the SDK.Why this is worth doing
entitlements,featureFlags) automatically flow into consumer apps without TS errorsSessionCookieData(the sealed-cookie shape) — they're two clearly-distinguished concernsReferences
Came up while reviewing a custom-UI example app for impersonation support — landed at workos/workos-custom-ui-authkit-example#9, where the original code used
as unknown as SessionDatato bridge the gap. Turns out the cast wasn't strictly required because the success response is structurally compatible with a hand-rolled interface, but having an officially-exported type would make the pattern obvious and resilient to future SDK changes.