valyd-idp-sdk
v0.2.1
Published
Official Node.js SDK for the Valyd Identity Provider — Third-Party SSO (OAuth2 Authorization Code flow).
Downloads
401
Maintainers
Readme
valyd-idp-sdk
Official Node.js SDK for the Valyd Identity Provider — Third-Party SSO over the OAuth2 Authorization Code flow. Add "Login with Valyd" to your app and read verified profiles, identity verifications, and professional licenses.
- 📘 API reference: https://docs.valyd.id/docs
- 🔒 Server-side only — your
clientSecretmust never reach a browser. - 🟢 Requires Node.js 18+ (uses the built-in
fetch). - 🧩 Ships ESM + CommonJS builds with full TypeScript types.
Installation
npm install valyd-idp-sdkQuick start
import { ValydClient } from "valyd-idp-sdk";
const valyd = new ValydClient({
clientId: process.env.VALYD_CLIENT_ID!,
clientSecret: process.env.VALYD_CLIENT_SECRET!,
redirectUri: "https://myapp.com/auth/valyd/callback",
// baseUrl defaults to https://idp.valyd.id
});
// 1. Start a login session and send the user to Valyd to log in.
const session = await valyd.createLoginSession();
// Persist session.marker server-side (HTTP-only cookie or session store).
const loginUrl = valyd.getAuthorizationUrl({
state: session.authorizeState,
scope: ["profile", "verifications"],
productName: "My App",
});
// 2. On your callback route, read the redirect params...
const { code } = valyd.parseCallback(requestUrl);
// 3. ...verify the stored marker (CSRF check) before doing anything else.
const { valid } = await valyd.verifyLoginSession(marker);
if (!valid) throw new Error("Invalid or expired login session");
// 4. Exchange the code for tokens.
const tokens = await valyd.exchangeCode(code!);
// 5. Call resource endpoints with the access token.
const profile = await valyd.getUserInfo(tokens.accessToken);
const verifications = await valyd.getVerifications(tokens.accessToken);CSRF note: The Valyd IdP does not echo the OAuth
statevalue back on the callback for TPSSO — thestateyou receive is the IdP's own session id. Do not compare it to the value you sent. UsecreateLoginSession()/verifyLoginSession()for CSRF protection instead.
A complete Express example lives in examples/express-login.ts.
The OAuth2 flow
| Step | Method | Where |
|------|--------|-------|
| 1. Start a login session, store the marker | createLoginSession() | Server-side |
| 2. Redirect user to Valyd | getAuthorizationUrl() | Browser redirect |
| 3. Valyd redirects back with ?code=&state= | parseCallback() | Your callback route |
| 4. Confirm the stored marker (CSRF check) | verifyLoginSession() | Server-side |
| 5. Exchange code for tokens | exchangeCode() | Server-side |
| 6. Read user data | getUserInfo(), getLicenses(), … | Server-side |
| 7. Renew an expired token | refreshToken() | Server-side |
API
new ValydClient(options)
| Option | Type | Default | Notes |
|--------|------|---------|-------|
| clientId | string | — | Required. From the Valyd Dev Portal. |
| clientSecret | string | — | Required. Server-side only. |
| redirectUri | string | — | Default redirect URI for getAuthorizationUrl(). |
| baseUrl | string | https://idp.valyd.id | IdP origin. |
| timeoutMs | number | 15000 | Per-request timeout. |
| fetch | typeof fetch | global fetch | Custom fetch implementation. |
Methods
createLoginSession()→Promise<{ authorizeState, marker }>Starts a login session. Storemarkerserver-side; passauthorizeStatetogetAuthorizationUrl({ state }).verifyLoginSession(marker)→Promise<{ valid }>Verifies a storedmarkeron the callback (CSRF check). Returns{ valid: false }for missing, tampered, or expired markers — never throws. Markers are valid for 10 minutes.getAuthorizationUrl({ scope?, state?, redirectUri?, productName? })→stringBuilds the hosted login URL to redirect the browser to.parseCallback(input)→{ code?, state?, error?, errorDescription? }Extracts query params from a URL, query string, or params object.exchangeCode(code)→TokenResponseExchanges a one-time code for{ accessToken, refreshToken, expiresIn, user }.refreshToken(refreshToken, { rotate? })→RefreshResponseRenews an access token. Passrotate: truefor refresh-token rotation.getUserInfo(accessToken)→UserInfo— requiresprofilescope.getLicenses(accessToken)→License[]— requiresverificationsscope.getCprLicense(accessToken)→License— requiresverificationsscope.getDoctorLicense(accessToken)→DoctorLicenseResult— requiresdoctor_licensescope.getVerifications(accessToken)→ identity verification payload — requiresverificationsscope.
Scopes
| Scope | Grants |
|-------|--------|
| profile | User profile and verification status (granted by default). |
| verifications | Identity verification results and professional licenses. |
| zkp | Zero-knowledge age-band proofs (is_18_plus, is_21_plus, …). |
| doctor_license | Doctor-license classification endpoint. |
Non-default scopes must be enabled for your client in the Valyd Dev Portal.
Error handling
Every failure throws a ValydError:
import { ValydError } from "valyd-idp-sdk";
try {
await valyd.getUserInfo(token);
} catch (err) {
if (err instanceof ValydError) {
console.error(err.code, err.message, err.status);
}
}code is a machine-readable string — from the API (invalid_token,
invalid_grant, …) or the SDK (network_error, timeout, config_error).
Local development
npm install
npm run build # bundles to dist/ (ESM + CJS + .d.ts)
npm run typecheck # type-check without emittingLicense
MIT
