← VisualEyes for your site

VisualEyes as an OpenID Connect provider

If your app speaks OpenID Connect — Nextcloud, OpenProject, Grafana, GitLab, Moodle, and most modern platforms do — you can offer “Sign in with VisualEyes” without writing any code. Point the app at our discovery URL, paste your client credentials, register your callback URL, and your users sign in by recognising their own photos instead of typing a password.

Quick start
  1. Register your site at /client/register (email + one-time code). You get a client_id and a secret — the same credentials used by the client API; one registration covers both.
  2. Enable OpenID Connect in your dashboard: open the OpenID Connect card, switch it on, and add your app’s redirect URL. Your app shows (or documents) this callback URL in its OIDC provider settings — copy it exactly. URLs are matched character-for-character and must be https (plain http://localhost is allowed for development).
  3. Configure your app with:
    Discovery / issuer URL:  https://aqa.com/.well-known/openid-configuration
    Client ID:               <your client_id>
    Client secret:           <your secret>
    Scopes:                  openid            (add email only if you need it — see Claims)
  4. Test it. Click your app’s new “Sign in with VisualEyes” button: it sends the browser to our hosted sign-in gate, the user types their VisualEyes name and solves their photo challenge, and lands back in your app signed in.
What the flow looks like

Standard authorization-code flow — your app never sees photos or secrets, only standard OIDC artifacts:

  1. Your app redirects the browser to /oidc/authorize with its client_id, registered redirect_uri, state and nonce (PKCE S256 supported and encouraged).
  2. We show the VisualEyes gate, run the user’s photo-recognition challenge — including all of our risk step-up, lockout and duress machinery — then redirect back to your redirect_uri with a single-use, 60-second authorization code.
  3. Your app’s server exchanges the code at /oidc/token (HTTP Basic or POST-body client authentication) for an ID token (RS256-signed JWT, 5-minute lifetime) and an opaque access token (1 hour) usable only at /oidc/userinfo.

Replaying a consumed code revokes every token minted from it, redirect URLs are exact-match only, and alg is pinned to RS256 — the sharp edges of OAuth are handled for you.

Endpoints
EndpointURL
Discoveryhttps://aqa.com/.well-known/openid-configuration
Authorizationhttps://aqa.com/oidc/authorize
Tokenhttps://aqa.com/oidc/token
UserInfohttps://aqa.com/oidc/userinfo
JWKShttps://aqa.com/oidc/jwks.json

Supported: response_type=code only · grant_type=authorization_code · PKCE S256 · client_secret_basic / client_secret_post · scopes openid and email · signing RS256 · pairwise subject identifiers.

Claims
ClaimMeaning
subPairwise and stable: the same user always gets the same sub at your app, but a different one at every other VisualEyes-connected site — apps cannot correlate users with each other. Key your accounts on it.
auth_timeWhen the photo challenge was passed (epoch seconds).
nonceEchoed verbatim from your authorization request.
email, email_verifiedReturned in the UserInfo response (per the spec, not in the ID token) — only when you requested scope email and the email scope is enabled for your client in the dashboard. Prefer sub; request email only if your app genuinely needs it.
ve_aliasAdditive: the per-site alias the user signed in with, when one is in play — sign in the matching local account.
ve_duressBoolean. See the warning below.

⚠ ve_duress is server-side only — never show it in any UI

If ve_duress is true, the user completed their login with a panic photo — a silent alarm that they may be acting under coercion. Act on it server-side only: show a completely normal session while limiting sensitive actions, alerting your security team, or whatever your duress playbook says. Anything visible in the browser — a banner, a different page, even a subtly different response time you can avoid — can endanger the person standing next to your user. VisualEyes also runs its own out-of-band escalation independently of your app.

Worked examples

Nextcloud (user_oidc app)

Install the OpenID Connect user backend (user_oidc) app, then Administration → OpenID Connect → register a provider:

Identifier:          VisualEyes
Client ID:           <your client_id>
Client secret:       <your secret>
Discovery endpoint:  https://aqa.com/.well-known/openid-configuration
Scope:               openid

Nextcloud displays the provider’s redirect URL in the same form — add exactly that URL to the OpenID Connect card in your VisualEyes dashboard. Map the account field to the token’s sub (the pairwise ID) unless you granted the email scope.

OpenProject

Administration → Authentication → OpenID providers → add a custom provider:

Display name:   VisualEyes
Client ID:      <your client_id>
Client secret:  <your secret>
Issuer / metadata URL:
                https://aqa.com/.well-known/openid-configuration

OpenProject shows its callback URL when you save the provider (typically https://<your-openproject>/auth/<provider>/callback) — register it in your dashboard’s OpenID Connect card, matching it exactly.

Everything else

Grafana, GitLab, Moodle, Discourse, Keycloak-brokered stacks and most SaaS admin panels ask for the same four values: discovery/issuer URL, client ID, client secret, scopes — plus registering their callback URL with us. If your platform documents “generic OAuth2/OIDC”, that’s the one to use.

Billing & limits
Security checklist
Standards & conformance

Our OpenID Connect endpoints are tested against the OpenID Foundation conformance suite (Basic OP profile) — authorization-code flow, PKCE, UserInfo, code single-use and reuse-revocation, redirect-URI validation, scopes, and request-object handling all behave per the OpenID Connect Core specification.

Two deliberate design choices differ from a session-based provider, on purpose:

We are conformance-tested, not formally certified — by choice: certification would require adding a login session, which would undermine the “always prove it with photos” guarantee that is the whole point of VisualEyes.

Questions, or a platform that won’t cooperate? hello@aqa.com — we’ll help you wire it up.