Sign in

This page documents what your users see when they reach Huudis's sign-in screen. The wire-level OIDC details are covered in OIDC overview.

Where sign-in happens

Sign-in always happens on Huudis's domain, never on your app's. That's the whole point of an identity provider: passwords are entered into one trusted UI, not duplicated across every relying party.

When your user clicks "Sign in" in your app, your app redirects to:

https://huudis.com/login?client_id=oc_xxx&...

The query string preserves the OIDC client_id, redirect_uri, and state so Huudis can return the user to your app once they're authenticated.

What the user sees

The sign-in screen has four primary affordances:

  1. Email + password — the default. Two fields, one button.
  2. Social provider buttons — one button per identity provider configured on the workspace (Google, Apple, Facebook). Only shown when the provider is wired and enabled.
  3. "Forgot your password?" — link to the password-reset flow.
  4. "Don't have an account? Sign up" — switches to the sign-up form, which posts to /api/v1/auth/signup with the same client_id.

The branding on the sign-in screen is Huudis's. (Workspace-customised branding is on the roadmap; not shipped yet.)

Email + password

POST /api/v1/auth/login
Content-Type: application/json

{
  "email": "alice@example.com",
  "password": "<password>",
  "clientId": "oc_xxx"
}

Successful response sets an HttpOnly, Secure session cookie on huudis.com, then redirects the browser through the OIDC flow to issue an authorization code for your app.

Errors

Code Meaning
INVALID_CREDENTIALS Wrong email or password. We don't disclose which — both errors return the same response code.
EMAIL_NOT_VERIFIED The user signed up but never clicked the verification link. The response includes a "resend" affordance.
MFA_REQUIRED Password was correct but MFA is enrolled. Response includes an mfaChallengeToken.
ACCOUNT_DISABLED A workspace admin disabled this user. Email support to re-enable.

MFA challenge

When the password is correct but MFA is required, the response is:

{
  "data": {
    "mfaRequired": true,
    "mfaChallengeToken": "mfc_xxxxxxxxxxxx",
    "mfaChallengeExpiresAt": "2026-05-12T03:14:00.000Z",
    "factors": [
      { "id": "fct_xxx", "kind": "totp", "label": "1Password" },
      { "id": "fct_yyy", "kind": "webauthn", "label": "iPhone Touch ID" }
    ]
  }
}

Huudis renders a chooser if the user has more than one factor; otherwise it posts straight to:

POST /api/v1/mfa/verify-login
{
  "mfaChallengeToken": "mfc_xxx",
  "factorId": "fct_xxx",
  "code": "123456"
}

On success, the session cookie is set and the OIDC flow completes. On failure, the challenge token can be retried — but it expires in 5 minutes and is invalidated after 5 failed attempts.

Sign in with an existing Huudis session

If the user already has a Huudis session cookie (because they signed into another Forjio product earlier today), they don't see the sign-in screen at all — Huudis silently issues an authorization code and redirects back to your app. This is single sign-on.

To force a fresh prompt anyway, pass prompt=login on the authorize URL:

/api/v1/oidc/authorize?...&prompt=login

To force a fresh prompt and force MFA, pass prompt=login mfa.

Next