@proteles/js
v0.1.1
Published
Framework-agnostic OAuth2/OIDC client core (PKCE, code exchange, refresh rotation, revoke, userinfo) for the Proteles identity platform.
Maintainers
Readme
@proteles/js
Framework-agnostic OAuth2/OIDC client core for the Proteles identity platform.
This is the substrate the framework SDKs build on. Most apps should use a higher-level package instead:
@proteles/next— a Backend-for-Frontend for Next.js. Tokens live in encrypted, httpOnly cookies on your own server and never reach the browser. Recommended.@proteles/react— drop-in components (<SignInButton>,<UserButton>,useUser()) that talk to the BFF.
Reach for @proteles/js directly only when integrating a runtime that doesn't
yet have a dedicated SDK.
It is a TypeScript port of the reference webapp/oauth_client.go: the same
mandatory PKCE, state/nonce binding, refresh-with-rotation, best-effort
revoke, and userinfo fetch — validated against the same authorization server.
Install
npm install @proteles/jsRequires a runtime with fetch and Web Crypto (Node 18.12+, modern browsers,
edge runtimes). Node 18 is supported via a node:crypto fallback.
Usage
The client holds no session state. You generate an authorization request,
persist the returned state / codeVerifier / nonce somewhere only your
server can read (an encrypted cookie — exactly what @proteles/next does), then
complete the exchange on the callback.
import { OAuthClient } from "@proteles/js";
const client = new OAuthClient({
issuer: "https://auth.example.com", // your tenant's issuer
clientId: process.env.OAUTH_CLIENT_ID!,
clientSecret: process.env.OAUTH_CLIENT_SECRET, // omit for a public (PKCE-only) client
redirectUri: "https://app.example.com/api/auth/callback",
scopes: ["openid", "profile", "email", "offline_access"],
});
// 1. Start login — redirect the browser to `url`, persist the rest server-side.
const { url, state, codeVerifier, nonce } = await client.createAuthorizationUrl();
// 2. On the callback: verify `state` matches, then exchange the code.
const tokens = await client.exchangeCode({ code, codeVerifier });
// 3. Read the user.
const user = await client.fetchUserInfo(tokens.accessToken);
// 4. Later: refresh (rotation-aware) and revoke on logout.
const refreshed = await client.refresh(tokens.refreshToken!);
await client.revoke(tokens.refreshToken!);Confidential vs. public clients
- With a
clientSecret, the token endpoint is called with HTTP Basic (client_secret_basic). - Without one, the client is public: it sends
client_idin the request body and relies on PKCE. The server must allowtoken_endpoint_auth_method: nonefor that client.
Endpoints
Endpoints are derived from issuer using the server's conventional paths
(/oauth/authorize, /oauth/token, /oauth/userinfo, /oauth/revoke). Pass
endpoints to override any of them.
Security notes
state,codeVerifier, andnonceare secrets for the duration of a login; never expose them to the browser. Validatestateon the callback with the providedtimingSafeEqual.OAuthError.messageis diagnostic only — never surface it to end users.- Every network call has a 10s default timeout (
timeoutMs) so a slow AS can't hang your request.
Develop
npm install # from the sdk/ workspace root
npm run build # tsc -> dist
npm test # tsx + node:test