@identityprovider/servicestack v2.3.1
@identityprovider/servicestack
A lightweight wrapper for @identityprovider/client that seamlessly integrates with ServiceStack via its JsonServiceClient.
This SDK is designed to simplify OpenID Connect (OIDC) authentication and token management for applications that already use ServiceStack. It provides a unified client with built-in support for:
✨ Features
- Authorization Code Flow with PKCE
silentAuthorize()via invisible iframe (prompt=none)- Secure logout with
postLogoutRedirectUriandidTokenHint - Session-scoped PKCE values stored in
sessionStorage - Configurable PKCE cleanup delay (handles React double-render issues)
- Optional integration with your backend to manage
HttpOnlycookie-based tokens - Full ID token validation
- Seamless drop-in for
JsonServiceClient-basedapps
🚀 Installation
npm install @identityprovider/servicestackYou must also install its peer dependencies:
npm install @identityprovider/client @servicestack/client🧱 Usage
import { ServiceStackClient } from "@identityprovider/servicestack";
const client = new ServiceStackClient(
"https://my-app.com", // Your app's API base URL
"https://identity.mycompany.com", // Your IdP's base URL
"my-client-id", // OIDC client_id
5000 // Optional: PKCE cleanup delay (ms)
);
// Start login
await client.authorize({
redirectUri: "https://my-app.com/callback",
scope: "openid profile email"
});
// Silent login if user is already authenticated
const code = await client.silentAuthorize({
scope: "openid profile email"
});
// Exchange the code for tokens
const tokens = await client.token({
code,
redirectUri: "https://my-app.com/callback"
});
// Log out the user
client.logout({
postLogoutRedirectUri: "https://my-app.com/logout"
});🔄 PKCE Session Storage
PKCE state, nonce, and code_verifier values are stored in sessionStorage and automatically cleared after a successful token exchange, using a delay (default: 5 seconds) to support React’s double-rendering behavior.
You can customize this delay by passing a fourth argument to the constructor:
const client = new ServiceStackClient(apiUrl, idpUrl, clientId, 7000); // 7 seconds🔐 HttpOnly Token Flow
If you’re managing tokens via secure HttpOnly cookies (recommended), pass a tokenExchangeHandler to the .token() method:
await client.token({
code,
redirectUri: "https://my-app.com/callback",
tokenExchangeHandler: async (request) => {
const res = await fetch("/api/token", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify(request),
credentials: "include"
});
if (!res.ok) throw new Error("Token exchange failed");
return await res.json();
}
});🛠️ ServiceStack-Specific Features
Because this client extends JsonServiceClient, you can use ServiceStack features like:
client.enableAutoRefreshToken = true;
client.onAuthenticationRequired = async () => {
await client.authorize({ redirectUri: "https://my-app.com/callback" });
};
client.bearerToken = tokens.accessToken; // If you manage tokens manually
client.refreshTokenUri = "/api/refresh"; // If you use custom refresh endpoint🧪 Typed Error Handling
This client surfaces all errors from @identityprovider/client, including:
StateMismatchErrorCodeVerifierMissingErrorNonceMissingErrorIdTokenMissingErrorTokenExchangeErrorSilentAuthorizationError
Handle them with instanceof:
try {
await client.token();
} catch (e) {
if (e instanceof TokenExchangeError) {
console.error("Token error:", e.message);
}
}🛡️ Security Best Practices
- Always use
stateandnonce(handled automatically) - Prefer
HttpOnlycookie storage over in-memory access tokens - Use
SameSite=LaxorStrictfor session cookies - Avoid storing tokens in
localStorageor sharing across tabs
📦 License
MIT