Skip to content

Federated Login Errors (Browser Redirect Surface)

Surface: browser redirect — 302 Found / 303 See Other, no response body
Carrier: the error and ref query parameters on the console login URL
Not an RFC 7807 problem type — see How this differs from every other page in this reference

When an external identity-federation login fails, the browser is redirected to the ANKASecure console login screen carrying a machine-readable cause and a support reference:

HTTP/1.1 302 Found
Location: https://<edge-host>/admin/login?error=access_not_configured&ref=7c9e6679-7425-40de-944b-e07fc1f90ae7
Content-Length: 0

The console renders a human-readable explanation for the error code and displays the ref. Quote the ref when contacting an operator or support — it is the correlation identifier that ties the failure to the server-side log line and to the audit record.


How this differs from every other page in this reference

Every other error in this reference is an API error: a response body in RFC 7807 Problem Details form, served as application/problem+json, carrying type, title, status, detail, instance, correlationId and timestamp.

This surface is not an API response. It is a browser redirect, and it therefore has:

  • no bodyContent-Length: 0
  • no type — the cause is the error query parameter, not a URI
  • no correlationId — the correlation identifier is the ref query parameter
  • no status beyond the redirect itself302 (OIDC callback, initiate) or 303 See Other (SAML ACS)

Consequently the "Programmatic Error Handling" example on the Error Reference Index, which dispatches on problemDetails.getType(), does not apply here. There is no problem document to dispatch on. A programmatic consumer never reaches this surface: it exists only for a top-level browser navigation.

Why 303 See Other on the SAML assertion consumer service

The SAML ACS is a POST endpoint (the IdP posts SAMLResponse as a form). A conforming client that receives a 302 on a POST may re-issue the request with the same method and body, which would re-post the assertion. 303 See Other requires the client to follow with GET, so the redirect is unambiguous. The OIDC callback and initiate are GET navigations and use 302.


The boundary rule — when a surface may redirect instead of returning a problem document

A surface may use the browser-redirect error projection if and only if all three conditions hold:

  1. the request is a top-level browser navigation that the browser renders as a document;
  2. the caller therefore cannot read a response body, so an interposing CDN or proxy may replace it;
  3. an equivalent typed RFC 7807 refusal remains available on a non-navigation surface for programmatic consumers.

Every non-navigation surface — fetch/XHR, the SDKs, the CLIs and service-to-service calls — keeps RFC 7807 unconditionally. The redirect is an additional projection of refusals that already exist as typed problem documents; it retires none of them and renumbers none of them.

Three federation legs meet all three conditions and therefore redirect:

Leg Method and path Redirect status
Federation initiate GET /api/v3/auth/idp/initiate 302
OIDC federation callback GET | POST /api/v3/auth/idp/callback 302
SAML assertion consumer service POST /api/v3/auth/idp/saml/acs 303

The LDAP credential login leg (POST /api/v3/auth/idp/ldap/login) does not redirect on failure. It fails condition (2): its caller is a JSON-consuming XHR that can read the response body, so the body is never replaced by an interposing CDN. It continues to return 200, 401 and 503 as application/problem+json, byte-identical to before.

This is not a weakening of the RFC 7807 guarantee

Condition (3) is what preserves it. Each of the six codes below is a projection of a typed refusal that a programmatic caller still receives as a problem document on a non-navigation surface. The Error Reference Index states the RFC 7807 guarantee for all API errors; this surface is not one.


Disambiguation from the OAuth 2.0 invalid_request problem type

This reference already documents an error named invalid_request (400). It is a different thing, and the two must not be conflated:

OAuth 2.0 token-endpoint error Federated-login redirect code
What it is An RFC 7807 type URI: https://docs.ankatech.co/errors/invalid_request A value of the error query parameter on a redirect
Where it appears In the body of a 400 response from the OAuth 2.0 token endpoint In the Location header of a 302 / 303 from a federation leg
Which RFC defines the name RFC 6749 §5.2 (token-endpoint error response) RFC 6749 §4.1.2.1 (authorization-endpoint error redirect parameter)
Media type application/problem+json none — there is no body
Page Invalid Request (400) this page

Both are named invalid_request deliberately, because both take the name from RFC 6749. The underscore form used by the ANKASecure OAuth 2.0 error pages exists precisely so the RFC 7807 type suffix matches the RFC 6749 error code exactly; the federated-login redirect uses the RFC 6749 name in the redirect-parameter sense the RFC defines for it, which is the closer of the two readings. No page named invalid_request.md is created for this surface — every code is documented here, on one page.


The six codes

The set is closed. A code is never derived from upstream text, so an identity provider cannot inject a value; a value outside this set is treated by the console as unrecognised and rendered generically.

error code What it means Who fixes it Remedy
access_not_configured The identity authenticated at the provider, but the platform will not grant it access — no admissible role matched, the matched role is not assignable, nothing matched at all, or the email address the provider asserted is already owned by another account in the tenant Operator Fix the admission rules; if the address is already owned, the operator resolves the duplicate account
identity_provider_email_unverified The identity authenticated at the provider, but the provider did not confirm that the email address it asserted belongs to that identity. ANKASecure will not provision an account from an unverified address The administrator of the identity provider Verify the address at the identity provider, then sign in again
identity_provider_not_configured No enabled identity provider exists for the requested kind or tenant. A configuration state: there is nothing to authenticate against Operator Bind and enable a provider
identity_provider_unavailable The upstream identity provider could not be reached or did not answer in time Operator, then the provider Retry; if it persists, check egress, TLS and timeouts toward the provider
internal_error A platform-internal contract defect between ANKASecure services. Not an identity-provider problem and not retryable ANKASecure engineering Raise a platform-defect ticket, quoting the ref
invalid_request The request that started the login was not usable — for example the "Sign in with SSO" flow was started without a login address, or the federated callback could not be completed with the identity that was asserted Depends — see below Retry the login from the console login screen; if it recurs, quote the ref to an operator

What each code projects

Each code is the browser projection of one or more typed RFC 7807 refusals that are unchanged on every non-navigation surface:

error code Projected from
access_not_configured 403 admission-denied; 422 federated-role-not-assignable; 422 federated-role-not-found; 422 federated-role-s2s-only; 409 federated-email-conflict
identity_provider_email_unverified 422 federated-email-not-verified
identity_provider_not_configured 503 idp-not-bound
identity_provider_unavailable 503 idp-unavailable
internal_error 502 federation-internal-error
invalid_request 401 federation-callback-invalid; the 400 caller-input refusal on initiate

The type a programmatic caller sees is the one the authentication service renders

The types in the right-hand column are the ones raised on the internal service-to-service provisioning call. A programmatic caller on a non-navigation surface receives the equivalent refusal from the authentication service: 422 federated-email-not-verified is re-rendered as 422 identity-provider-email-unverified, and every other row in the table above is re-rendered as 403 admission-denied — so the four role and email-conflict causes remain indistinguishable to that caller, exactly as they are in the browser.

invalid_request is a deliberate collapse

Two causes with different owners share this code. On initiate it is the caller's own input (a missing or over-long login address). On the callback it is federation-callback-invalid — the federated login could not be completed with the identity the provider asserted, most commonly because the identity carries no email claim. Collapsing them is intentional: distinguishing them in the browser would tell an unauthenticated caller whether a given login address reached a real provider, which is the enumeration the collapse exists to prevent. The specific cause is recorded server-side against the ref, where an operator can read it and an attacker cannot.

Why the unverified email is told apart and the email collision is not

Two refusals arise from the same place — the identity slot, after the platform has already agreed the identity's roles are grantable — and they are deliberately projected differently.

  • The provider asserted an email it did not confirm. That is a fact about the provider's own assertion, and it discloses nothing about ANKASecure: whether an account exists here, whether the address is in use, and whether the tenant has any users are all unchanged by it. So it gets its own code, identity_provider_email_unverified, because the person who can fix it is the administrator of the identity provider and telling them "access is not configured" sends them to the wrong administrator.
  • The address is already owned by another account in the tenant. Telling an unauthenticated caller that would confirm an account exists for that address — the account-existence oracle the collapse exists to prevent. It therefore keeps access_not_configured, byte-identical to a plain admission denial.

The order of the two checks is what keeps the split safe

The unverified-email check runs before the collision check. If it ran second, a caller controlling an identity provider could assert an unverified address and read the returned code as an answer to "does an account already exist for this address?" — access_not_configured for yes, identity_provider_email_unverified for no. Because it runs first, an unverified address always yields the same code regardless of what the platform holds, and the collision branch is only reachable once the address is verified.

Codes never reveal which user, tenant or rule was involved

Every code is oracle-safe. The redirect reveals nothing about whether a user, a tenant, a provider or an admission rule exists. In particular access_not_configured is byte-identical whether the user matched no rule at all, matched a rule naming a role that does not exist, matched a rule naming a role that cannot be assigned in the owning tenant type, was stopped by a domain guard, or presented an email address already owned in the tenant. identity_provider_email_unverified reports only what the provider did not assert, and is likewise byte-identical whatever the platform holds. Diagnosis is performed from the server-side record keyed by ref, never from the response.


Using the ref

ref is a server-authored UUID. It is never taken from a client-supplied value on this surface, and the console validates its shape before rendering it.

For an end user: quote the ref verbatim to whoever administers your ANKASecure deployment.

For an operator: the same value appears in the auth-api log line for the failure, in the admin-api log line for the specific cause, and in the correlationId of the audit event recording the refusal — so one lookup resolves the whole path. The internal troubleshooting runbook describes that lookup.


Verifying on a non-SaaS deployment

initiate's invalid_request arm is reachable only on SaaS deployments. On a private-cloud or on-premise deployment, initiate resolves the deployment-default provider directly and never takes the branch that validates a submitted login address, so the code it can emit is identity_provider_not_configured (or identity_provider_unavailable), not invalid_request.

Measured on the development environment: GET https://dev.ankatech.co/api/v3/auth/idp/initiate returns 503 upstream and therefore redirects with error=identity_provider_not_configured. That is correct behaviour, not a defect — do not file it as one. To exercise the invalid_request arm, use a SaaS deployment.