Skip to main content

Where an error shows up

Three different places, and knowing which one you are looking at narrows the cause immediately: The first case never redirects — before we have verified that a redirect_uri belongs to the client, bouncing the user anywhere would make the endpoint an open redirector.

Authorization errors

Token errors

invalid_grant is the one people hit most. In order of likelihood: the code was already used; more than 60 seconds passed; redirect_uri differs by a trailing slash from the authorize request. It must be byte-identical.

BankID hint codes

When a login fails on the BankID side, the reason appears as a hint code — on the login page, and in data.object.reason of a login.failed webhook. Recoverable failures do not fire a webhook: the login page offers a retry inside the same session, and notifying on every mistyped cancel would flood your endpoint.

Session API errors

The Session API returns the same error body as the rest of the sajn API: branch on code, show userMessage to your users, and log requestId. For the full shape, see Errors.
A session or client from another workspace answers 404, not 403. “Exists but isn’t yours” is itself information, so sajn doesn’t confirm it.

Diagnosing by symptom

Production redirect URIs need to be registered too, and https matches exactly. Also check your client was switched to a production certificate — a test client will not accept real BankIDs.
Classic abandoned-browser case: they completed BankID and closed the in-app browser before the redirect. The authentication succeeded on our side. Subscribe to login.completed to see it.
Usually a cached JWKS after a key rotation. Use a client that refetches on an unknown kid rather than pinning a key. Also confirm you are validating iss as exactly https://login.sajn.se, with no trailing slash.
It should not — sub is stable per person per organization. If it changed, you are almost certainly comparing across two different organizations, or between a test and a production environment, which are deliberately separate identifier spaces.