sfsso-frappe
This page hasn't been translated yet. Showing English version.

Server Client API

Import:

import { createFrappeSSO } from 'sso-frappe/server';

createFrappeSSO(config)

Creates a server-side Frappe SSO client. This is the only entry point that accepts clientSecret.

const sso = createFrappeSSO({
  baseUrl: 'https://erp.example.com',
  clientId: 'your-client-id',
  clientSecret: 'your-client-secret',
  redirectUri: 'https://app.example.com/api/auth/callback/frappe',
  scope: ['openid'],           // optional, default ['openid']
  usePkce: true,               // optional, default true
  allowInsecureHttp: false,    // optional, default false
  timeoutMs: 10_000,           // optional, default 10000
});

Returns a FrappeSSOClient with the methods below.

createAuthorizationUrl()

Generates the authorization URL, state, and PKCE pair. Use this when your backend owns the redirect (e.g. Nuxt Nitro, Express).

const { url, state, codeVerifier } = await sso.createAuthorizationUrl();

// Store state + codeVerifier in a server-side session
req.session.frappeState = state;
req.session.frappeVerifier = codeVerifier;

// Redirect the user
res.redirect(url);

exchangeCode({ code, codeVerifier })

Exchanges the authorization code for an access token. Server-only — requires clientSecret.

const token = await sso.exchangeCode({
  code: 'auth-code-from-frappe',
  codeVerifier: 'verifier-from-session',
});

// token.accessToken  — string
// token.tokenType    — string (usually 'Bearer')
// token.expiresIn    — number | undefined
// token.refreshToken — string | undefined
// token.raw          — full response from Frappe

Throws FrappeSSOTokenError on failure.

getUserProfile(token)

Fetches the user profile from Frappe's userinfo endpoint.

const profile = await sso.getUserProfile(token);

// profile.subject  — stable user ID (the `sub` claim)
// profile.email    — email address
// profile.name     — full name
// profile.username — Frappe username
// profile.image    — avatar URL
// profile.roles    — string[] (e.g. ['System User', 'Employee'])
// profile.raw      — full userinfo response

Throws FrappeSSOProfileError on failure.

discover()

Fetches the OIDC discovery document. Called automatically by exchangeCode and getUserProfile if explicit endpoints aren't configured.

const metadata = await sso.discover();

// metadata.authorization_endpoint
// metadata.token_endpoint
// metadata.userinfo_endpoint
// ...

Throws FrappeSSODiscoveryError if the endpoint is unreachable or malformed.

validateState(stored, received)

Validates that the state returned by Frappe matches the one you stored. Exported from both sso-frappe and sso-frappe/server.

import { validateState } from 'sso-frappe/server';

validateState(storedState, receivedState);  // throws FrappeSSOStateError on mismatch

Configuration reference

Field Required Default Description
baseUrl Yes Frappe instance URL
clientId Yes OAuth client ID
clientSecret Yes OAuth client secret (server-only)
redirectUri Yes Consumer callback URL
scope No ['openid'] OAuth scopes
usePkce No true PKCE S256
allowInsecureHttp No false Allow http:// for local dev
timeoutMs No 10000 HTTP timeout
authorizationEndpoint No discovery Explicit override
tokenEndpoint No discovery Explicit override
userInfoEndpoint No discovery Explicit override
discoveryEndpoint No ${baseUrl}/.well-known/openid-configuration Explicit override

Errors

All errors extend FrappeSSOError. Error messages never include secrets.

Error When
FrappeSSOConfigError Invalid config (missing field, bad URL, etc.)
FrappeSSOStateError State mismatch or missing state/code/verifier
FrappeSSOTokenError Token exchange failed
FrappeSSOProfileError Userinfo fetch failed
FrappeSSODiscoveryError Discovery endpoint unreachable/malformed