6.1 KiB
Auth — client.auth
AuthService (source: src/auth/AuthService.ts). Player authentication:
device ID login (primary for game clients), Google / Apple sign-in, custom
webhook login, identity linking/unlinking, logout, and auth state observation.
Methods
loginWithDevice(options?: LoginWithDeviceOptions): Promise<LoginViaDeviceResponse>
loginWithGoogle(options: LoginWithGoogleOptions): Promise<LoginViaGoogleResponse>
loginWithApple(options: LoginWithAppleOptions): Promise<LoginViaAppleResponse>
loginWithCustom(options: LoginWithCustomOptions): Promise<LoginViaCustomResponse>
linkWithGoogle(idToken: string): Promise<void>
linkWithApple(idToken: string): Promise<void>
linkWithCustom(options: LinkWithCustomOptions): Promise<void>
unlinkIdentity(provider: string): Promise<void>
logout(): void
get isAuthenticated(): boolean
onAuthStateChange(listener: AuthStateListener): () => void
LoginWithDeviceOptions
{
region?: string; // default 'global'
language?: string; // default 'en'
nickname?: string; // omitted from the request when not set
}
The device ID is auto-generated on first call and persisted in localStorage
(src/device/DeviceId.ts). The request also carries the client's
projectKey.
LoginWithGoogleOptions / LoginWithAppleOptions
{
idToken: string; // required — Google ID token / Apple identity token
region?: string; // default 'global'
language?: string; // default 'en'
nickname?: string; // omitted from the request when not set
}
The client obtains the idToken from the provider's own sign-in flow
(Google Identity Services, Sign in with Apple) and forwards it to
POST /sdk/v1/authorization/google or /sdk/v1/authorization/apple.
LoginWithCustomOptions
{
customData: Record<string, unknown>; // required — forwarded to the project's custom auth webhook
provider?: string; // custom provider name; omitted = the default provider
region?: string; // default 'global'
language?: string; // default 'en'
nickname?: string;
}
Login responses
interface LoginViaDeviceResponse { accessToken?: string; refreshToken?: string }
interface LoginViaGoogleResponse { accessToken?: string; refreshToken?: string }
interface LoginViaAppleResponse { accessToken?: string; refreshToken?: string }
interface LoginViaCustomResponse { accessToken?: string; refreshToken?: string }
On success both tokens are saved to the client's TokenStore, player and
catalog are warmed, the revision poll starts, and the state flips to
'signed-in'.
Identity linking
linkWithGoogle / linkWithApple / linkWithCustom attach an additional
identity to the currently signed-in player; unlinkIdentity detaches one.
All four require an active session — the access token is sent automatically
(transport level). They return nothing (HTTP 204).
await client.auth.linkWithGoogle(idToken);
await client.auth.linkWithApple(idToken);
await client.auth.linkWithCustom({ customData, provider }); // provider optional
await client.auth.unlinkIdentity('custom:my-backend');
LinkWithCustomOptions
{
customData: Record<string, unknown>; // required
provider?: string; // custom provider name; omitted = the default provider
}
unlinkIdentity(provider) takes the full provider string: device,
google, apple, custom, or custom:<name>.
Linked identities appear on the player profile:
client.player.data.identities: PlayerProfileIdentity[] with
{ provider: string; subject: string; createdAt: string }.
Environments
A project has two environments, staging and prod. The SDK key you pass as
RudderClientOptions.projectKey belongs to one of them, so the environment is
resolved at login and carried inside the access and refresh tokens; nothing in
the client API takes an environment argument, and a player created in one
environment is invisible in the other. Content released only to staging is
empty for a prod key and vice versa.
Tokens issued before SDK 2.0.0 carry no environment claim and are rejected with
401. The transport's refresh then fails, clears the token store and emits
'signed-out' — log the player in again.
Auth state
type AuthState = 'signed-in' | 'signed-out';
type AuthStateListener = (state: AuthState) => void;
isAuthenticatedistruewhile an access token is present in the token store.onAuthStateChangefires the listener immediately with the current state and returns an unsubscribe function.logout()clears tokens, stops the revision poll, drops cached domain data, and emits'signed-out'.
Token refresh (automatic, transport level)
Source: src/transport/request.ts.
- Every request injects
Authorization: Bearer <accessToken>when a token exists. - On 401 the transport does a single-flight refresh against
POST /sdk/v1/authorization/refresh(concurrent 401s share one refresh) and retries the original request once. - If refresh fails, tokens are cleared,
onAuthStateChangelisteners get'signed-out', and the request throwsRudderAuthError.
TokenStore
interface TokenStore {
getAccessToken(): string | null;
getRefreshToken(): string | null;
saveTokens(accessToken: string, refreshToken: string): void;
clear(): void;
}
Factories (exported from the package root):
createDefaultTokenStore()— localStorage, with a silent in-memory fallback where localStorage is unavailable (SSR, private mode). This is the default whentokenStoreis omitted fromRudderClientOptions.createLocalStorageTokenStore()— keysrudder_access_token/rudder_refresh_token.
Provide a custom TokenStore via RudderClientOptions.tokenStore for other
backends (sessionStorage, cookies).
Errors
- Constructor: missing
baseUrl/projectKey→RudderError,code: 'sdk/invalid-options'. - Login failure →
RudderHttpError(e.g. unknown project key) orRudderNetworkError. - Any later request with an expired session →
RudderAuthError(after the refresh attempt above fails).