Skip to main content

A user cannot sign in

Wrong password, inactive account, lockout, MFA, a policy rule, a network zone or the wrong region — each with the exact response you will see and the fix.

Written for whoever runs IT9 min readUpdated

Somebody cannot get in. This article works through the seven things that stop a sign-in, in the order the platform checks them, with the exact response for each. If all you have is a screenshot, use the table in the second section — it is arranged by what the message says rather than by what caused it.

The order the checks run in

Knowing the order saves time, because a failure at step two tells you nothing about steps three to seven. POST /api/v1/auth/login evaluates, in this sequence:

  1. The password, against a bcrypt hash. A failure increments the failed-attempt counter.
  2. Account state — the active flag, which is what every authentication path checks and is separate from the lifecycle status field.
  3. Lockout — five consecutive failures locks the account.
  4. Email verification, unless a policy has turned the requirement off.
  5. Network zone — a blocklisted address is refused here, before any policy rule runs.
  6. Behavioural risk scoring, producing a risk level and a list of signals.
  7. The ordered sign-on policy, which decides allow, step up or deny. A second factor is demanded here, not before.

Note that the network-zone check runs after the password has been verified. It is a policy control, not a firewall.

Match the message you were shown

Every refusal the sign-in endpoint can return, with its HTTP status and what to do. Strings are verbatim.
What you seeStatusCause and fix
invalid email or password401A wrong password, an unknown address, or a deactivated account — the three are indistinguishable by design. Confirm the account exists and is active before assuming the password is wrong.
account temporarily locked due to too many failed attempts429Five consecutive failures. Note the status: a lockout is a 429, not a 401 or a 403. It clears itself after a fixed interval, or an administrator can clear it by setting the account's status.
invalid MFA code401The one-time code did not verify. Usually clock drift — see MFA enrolment problems.
email verification required403The address has never been confirmed. Resend the verification mail, or invite the person instead.
Sign-in from your current network is not permitted.403A blocklisted network zone matched the client address.
Sign-in blocked by policy rule "<name>".403A DENY rule matched. The rule name is in the message and in extra.rule.
Sign-in blocked: no authentication policy rule applies to you. Contact your administrator.403A policy exists, no rule matched, and it has no default rule. The engine fails closed. Add a catch-all rule.
Sign-in is temporarily unavailable. Please try again.503The policy engine could not be reached. An outage on our side, not a permission problem; Retry-After: 10 is sent.
rate limit exceeded429More than ten requests a minute from that address to the public sign-in routes. No Retry-After is sent; wait a minute.
tenant home_region does not match this cell451The right credentials at the wrong regional address.

Two error shapes, and a trap in the `error` field

Most of the platform answers with {"error":"<the human message>"}. Sign-in is one of the few endpoints that does not — it uses a coded envelope, and there the error field carries the machine code, not the message.

What a locked account actually returns from POST /api/v1/auth/login
HTTP/1.1 429 Too Many Requests
Content-Type: application/json; charset=utf-8

{
  "code": "auth.account_locked",
  "message": "account temporarily locked due to too many failed attempts",
  "error": "auth.account_locked"
}

The codes on this path are auth.invalid_credentials, auth.account_locked, auth.mfa_required, auth.forbidden, common.service_unavailable and common.internal_error. Match on code; read message for humans.

What to check when the message cannot tell you

Because a wrong password, an unknown address and a deactivated account all produce the same sentence, the diagnosis has to come from the record rather than from the response. Three places, in this order:

  1. The person's record in the console

    Confirm the account exists at the address they are typing, and that it is active. The lifecycle status is one of STAGED, PROVISIONED, ACTIVE, RECOVERY, PASSWORD_EXPIRED, LOCKED_OUT, SUSPENDED or DEPROVISIONED — upper case with underscores, unlike every other status in the product. A separate active flag is what authentication actually checks, so an account can read ACTIVE and still be refused.

  2. The audit log

    Each failure writes auth.login_failed, and crossing the threshold writes auth.account_locked. A run of failures then a lockout is somebody mistyping; a single failure with no run is more likely a deactivated account or a wrong address. auth.login_email_unverified and auth.login_step_up_required name the other two outcomes.

  3. Whether they belong to the organisation at all

    Somebody with an account but no membership is refused for reasons that look nothing like a membership problem. Users created by our bulk import have none — see A bulk import was rejected.

If the record checks out and the person is simply certain they have forgotten the password, do not assume Forgot password? will fix it on its own — read Nothing arrived after "Forgot password?" before you send them there, because the confirmation screen looks identical whether an email actually went out or not.

Lockout: five attempts, then a wait

The threshold is five consecutive failed attempts, and the counter resets on any successful sign-in. The refusal is HTTP 429, not 401 or 403 — deliberate, because a locked account is a rate condition rather than a credential one. Two sentinels exist: account temporarily locked is the internal value you may see in a log, and account temporarily locked due to too many failed attempts is what the person is shown.

When multi-factor authentication is the blocker

A sign-in needing a second factor does not fail. It returns HTTP 200 with "mfa_required": true, a short-lived mfa_token and a step_up object, and no access tokens. The client then calls POST /api/v1/auth/mfa/verify with the token and the code.

The step_up object is the useful part: the acceptable factors, the reason, a human message, the risk level, the signals that fired, and an evaluation_id you can hand to us. An empty factors list means the policy wants a factor the person has not enrolled — sending them back to the code screen will loop forever. Route them to enrolment.

When a sign-on policy rule is the blocker

Policy is an ordered list, first match wins, and every evaluation is recorded — including those that allowed the sign-in. A denial returns 403 with the rule's own message plus rule and reason in extra. The seeded rules carry these messages:

  • Sign-in from anonymising networks is not permitted at this risk level. — the high-risk anonymiser rule.
  • Administrative access requires a passkey or security key. — the administrator hardening rule, which ships switched off.
  • Additional verification is required for this sign-in. — the elevated-risk or new-device rule.
  • Sign-in blocked by your organization's authentication policy. — the generic adaptive refusal.

To find out why a rule matched, look up the evaluation. Every decision is persisted with a per-rule trace naming each rule, whether it matched, whether it was skipped for being inactive, and the first condition that rejected it — one of users, groups, user_type, network, auth_method, risk or device. That last field is the most useful thing in the record when somebody is locked out and nobody knows why. Sign-on policies and the decision trace explains how to read one.

When a network zone is the blocker

A zone marked BLOCKLIST is enforced before any rule runs and cannot be overridden by a later allow rule. The person sees 403 and Sign-in from your current network is not permitted.; the recorded reason names the zone, as client address 203.0.113.7 is in blocklisted network zone "Anonymising Proxies"; blocklists are enforced before rule evaluation.

On a fresh tenant this cannot be the cause. Both seeded zones — Corporate Network and Anonymising Proxies — ship inactive, and Corporate Network ships with the placeholder range 198.51.100.0/24, a documentation range that matches no real address. If you activated it without replacing that range, it matches nothing and any rule referring to it can never fire.

The residency check, for completeness

Your organisation is bound to a home region, and a request landing on a different region is refused rather than served — HTTP 451. This is not a live cause of a real sign-in failure today: Qorionix runs one production cell, hosted in the EU (Ireland), so there is no second region for a request to land on by mistake. It is documented here only because the response shape is fixed and you may see it referenced elsewhere:

The shape of a cross-region refusal, were there a second region to refuse into
HTTP/1.1 451 Unavailable For Legal Reasons
X-Qorionix-Home-Region: eu-central-1

{
  "error": "tenant home_region does not match this cell",
  "home_region": "eu-central-1",
  "cell_region": "eu-central-1"
}

If you are seeing a sign-in failure, look at the other causes on this page first — see Organisation settings and your home region for what the home-region binding actually guarantees.

If none of these fit

A 500 with login failed is ours. So is a 503 with Sign-in is temporarily unavailable. Please try again. — that one specifically means the policy engine could not be consulted, and it is answered 503 rather than 403 because an outage is not an authorisation verdict. Before that branch existed, an unreachable policy engine answered every sign-in with a 403 telling operators they lacked permission while the service was simply down.

When you contact us, send the exact message and the evaluation_id from the step_up object if there was one. With those we can look up the whole decision without guessing.

Was this article wrong?

If a procedure here does not match what you see, or a limit we described has changed, tell us and we will fix the page. Email us about this article, or see how to get help if you need an answer rather than a correction.

Everything in troubleshooting