Authentication overview
Huudis is the identity provider — it's the thing other Forjio products delegate sign-in to. Where the Plugipay docs describe being a relying party, this page describes what's actually happening on the other side of the redirect.
If you're integrating an app with Huudis, you're a relying party (a "client" in OIDC terms). Huudis speaks standard OpenID Connect plus a few extensions documented below.
OIDC, not a bespoke protocol. Huudis implements OIDC 1.0 with PKCE (RFC 7636), device flow (RFC 8628), and refresh-token rotation. Any compliant OIDC library — Auth.js, oidc-client-js, authlib, go-oidc — works against Huudis without modification.
Discovery
Every Huudis instance publishes an OIDC discovery document and a JWKS:
GET https://huudis.com/.well-known/openid-configuration
GET https://huudis.com/.well-known/jwks.json
Point your OIDC library at the discovery URL and it will find every endpoint it needs.
The three flows
Huudis supports three OAuth 2.0 grant types, one for each audience:
| Audience | Grant type | When to use |
|---|---|---|
| A user in a web/mobile app | authorization_code + PKCE |
Default. The user's browser does the redirect dance. |
| A CLI on the user's terminal | urn:ietf:params:oauth:grant-type:device_code |
No browser-on-localhost trickery; the CLI prints a code and a URL. |
| A long-lived session | refresh_token |
Mint a fresh access token without re-prompting the user. |
There is no client_credentials grant (yet). For service-to-service auth, use a long-lived access token minted from a service-account user — see API authentication.
Authorization code flow (the short version)
The five steps:
- Your app redirects the user to
huudis.com/api/v1/oidc/authorizewith aclient_id,redirect_uri,scope,state, and PKCEcode_challenge. - Huudis prompts for credentials — email/password, Google, Apple, or Facebook depending on workspace config.
- After successful sign-in, Huudis redirects the browser to your
redirect_uri?code=…&state=…. - Your backend POSTs the
code(plus the original PKCEcode_verifier) to/api/v1/oidc/token. - Huudis returns an
access_token,id_token, andrefresh_token. You're signed in.
Authorization code flow (the longer version)
Step 1 — Authorize
GET /api/v1/oidc/authorize
?response_type=code
&client_id=oc_xxx
&redirect_uri=https://myapp.com/callback
&scope=openid%20profile%20email
&state=<csrf-token>
&code_challenge=<sha256(verifier) base64url>
&code_challenge_method=S256
Standard parameters. PKCE is required — Huudis rejects requests without code_challenge, even for confidential clients, because the cost is near zero and it neutralises a whole class of auth-code-interception attacks.
Step 2 — User authenticates
Huudis renders its own sign-in screen. The user picks their method:
- Email + password — covered in Sign in.
- Google / Apple / Facebook — covered in Social providers.
- Existing session — if Huudis already has a cookie for this user (because they signed into another Forjio product), step 2 is skipped silently. This is single sign-on.
If the user has MFA enrolled, Huudis presents a second factor challenge before completing.
Step 3 — Redirect with code
HTTP/1.1 302 Found
Location: https://myapp.com/callback?code=auc_xxxxxxxxxxxxxxxxxxxxxxxxx&state=<csrf-token>
The code is short (30 chars), single-use, and expires in 60 seconds.
Step 4 — Token exchange
POST /api/v1/oidc/token
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code&
code=auc_xxx&
redirect_uri=https://myapp.com/callback&
client_id=oc_xxx&
client_secret=ocs_xxx&
code_verifier=<the verifier you saved>
For public clients (mobile, SPA), omit client_secret — PKCE alone is sufficient.
Step 5 — Tokens
{
"access_token": "eyJhbGciOiJFUzI1NiIsImtpZCI6...",
"id_token": "eyJhbGciOiJFUzI1NiIsImtpZCI6...",
"refresh_token": "rft_xxxxxxxxxxxxxxxxxxxxxxxxxxxx",
"token_type": "Bearer",
"expires_in": 21600,
"scope": "openid profile email"
}
Both access_token and id_token are JWTs signed with ES256, verifiable against the published JWKS. Access tokens live six hours; refresh tokens live 30 days but rotate on every use.
Refresh-token rotation
Every time you call /oidc/token with grant_type=refresh_token, Huudis:
- Validates the presented refresh token.
- Issues a new access token and a new refresh token.
- Revokes the presented refresh token.
If Huudis ever sees the same refresh token presented twice (because it was leaked, or because two browser tabs raced each other), Huudis treats it as a stolen-token signal and revokes the entire token family — every refresh token derived from the original. The user is forced back through sign-in.
Single-flight your refreshes. Two simultaneous refresh calls with the same token will both succeed at the first call but the second will get rejected and kill the family. Always serialise refresh through one in-process mutex. The Huudis SDKs do this for you.
Device flow
For CLIs and TVs where there's no browser-on-localhost:
POST /api/v1/oidc/device_authorization
Content-Type: application/x-www-form-urlencoded
client_id=oc_xxx&scope=openid%20profile
Response:
{
"device_code": "dev_xxxxxxxxxxxxxxxxxxxxxxxx",
"user_code": "A3F7-9C2D",
"verification_uri": "https://huudis.com/device",
"verification_uri_complete": "https://huudis.com/device?user_code=A3F7-9C2D",
"expires_in": 600,
"interval": 5
}
Show the user the URL + code. They open it on any device already signed into Huudis, approve, and your CLI polls /oidc/token with grant_type=urn:ietf:params:oauth:grant-type:device_code until it sees an access token.
The huudis CLI uses this flow end-to-end.
What can go wrong
invalid_client— Theclient_iddoesn't match a registered client, or the redirect URI isn't in the client's allow-list. Check the OIDC clients page.invalid_grant— The authorization code is expired (60s window), already used, or PKCE verifier didn't match. Codes are one-shot — never retry the exchange.mfa_required— The user has MFA enrolled and the workspace policy requires it for this client. Your app needs to honour the MFA prompt; the SDKs handle this automatically.token family revoked— Refresh-token reuse was detected. Force a fresh sign-in via the authorize endpoint.
Next
- Sign in — the user-facing flow with the screens they see.
- Forgot password — password reset.
- Social providers — Google, Apple, Facebook setup.