> For the complete documentation index, see [llms.txt](https://docs.openiam.com/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.openiam.com/administration-guide/security/authentication-providers-and-mfa/authentication-provider-reference.md).

# Authentication levels, rules and capability flags

Every authentication level OpenIAM can route to, the five tables that make up a rule, the shipped rules including the passwordless ones, the provider capability flags and the Authentication property c

## What this covers

The exhaustive values behind [Authentication providers and MFA](/administration-guide/security/authentication-providers-and-mfa.md): the authentication levels OpenIAM routes on, the tables a rule is built from, the rules that ship, the capability flags on a provider type, and the `Authentication` category of **Administration → System Configuration**. Scan it; do not read it. Every procedure is on the task pages that link here.

## The authentication step types

These are the values the IdP routes on. Each is an `AUTH_LEVEL_ID`, with a matching `AUTH_LEVEL_GROUPING_ID` of the same name, and each is named by a fixed property you cannot change. `REQUIRES_AUTHENTICATION` decides whether reaching that step demands an already-authenticated session.

| Property                        | Value                     | `AUTH_LEVEL_NAME`                          | IdP page                      | `REQUIRES_AUTHENTICATION` |
| ------------------------------- | ------------------------- | ------------------------------------------ | ----------------------------- | ------------------------- |
| `...type.password.id`           | `PASSWORD_AUTH`           | Password Authentication                    | `/idp/auth-password`          | — (legacy `LEVEL 1` row)  |
| `...type.cert.id`               | `CERT_AUTH`               | Certificate Authentication                 | `/idp/auth-cert`              | `Y`                       |
| `...type.sms.id`                | `SMS_AUTH`                | SMS 2-Factor Authentication                | `/idp/auth-otp`               | `Y`                       |
| `...type.email.id`              | `EMAIL_AUTH`              | Email 2-Factor Authentication              | `/idp/auth-otp`               | `Y`                       |
| `...type.voice.id`              | `VOICE_AUTH`              | Voice authentication                       | `/idp/auth-otp`               | `N`                       |
| `...type.user.verification.id`  | `USER_VERIFICATION_AUTH`  | User Verification                          | `/idp/auth-user-verification` | `N`                       |
| `...type.totp.id`               | `TOTP_AUTH`               | TOTP 2-Factor Authentication               | `/idp/auth-totp`              | `Y`                       |
| `...type.push.id`               | `PUSH_AUTH`               | Push Notification 2-Factor Authentication  | `/idp/auth-push`              | `Y`                       |
| `...type.web.id`                | `WEB_AUTH`                | Web authentication (FIDO)                  | `/idp/auth-web`               | `N`                       |
| `...type.sas.id`                | `SAS_AUTH`                | SAS Authentication                         | `/idp/auth-sas`               | `Y`                       |
| `...type.challenge.response.id` | `CHALLENGE_RESPONSE_AUTH` | Challenge Response Question authentication | `/idp/auth-questions`         | `N`                       |
| `...type.captcha.id`            | `CAPTCHA_AUTH`            | Captcha authentication                     | `/idp/auth-captcha`           | `Y`                       |
| `...type.criipto.id`            | `CRIIPTO_AUTH`            | Criipto authentication                     | `/idp/auth-criipto`           | `N`                       |
| `...type.duo.id`                | `DUO_AUTH`                | DUO authentication                         | `/idp/auth-duo`               | `N`                       |
| `...auth.level.oauth`           | `OAUTH`                   | OAuth Authentication                       | —                             | `N`                       |
| `...auth.level.external.id`     | `EXTERNAL_AUTH`           | External authentication                    | `/idp/auth-external-redirect` | `N`                       |
| `...auth.level.type.accept.id`  | `AUTHORIZATION_ACCEPT`    | Authorization accept                       | — (terminal)                  | `N`                       |
| `...auth.level.type.decline.id` | `AUTHORIZATION_DECLINE`   | Authorization decline                      | — (terminal)                  | `Y`                       |

One property in that list is the odd one out. `org.openiam.auth.provider.type.oauth.id` holds `OAUTH_CLIENT`, which is an `AUTH_PROVIDER_TYPE`, not an `AUTH_LEVEL`; the OAuth *level* is `OAUTH`, named by `org.openiam.auth.level.oauth`. The two sit in the same block of properties and are not interchangeable.

The IdP pages in the fourth column are fixed application routes, not templates you can add, rename or re-point. The `/idp` prefix comes from `org.openiam.idp.context.path`, default `/idp`.

Three further levels ship with no `org.openiam.auth.provider.type.*` property at all, so they cannot be reached through the routing above: `KERB_AUTH` ("Kerberos Authentication", since 3.1.0.0), `QUESTION_AUTH` ("Knowledge questions authentication", since 4.2.1.4) and `NONE` ("No Authentication Required", since 3.1.0.0). You will still see them offered when you build an authentication rule.

## How a rule chains steps

The flow lives in five tables, all present since 4.2.0.0:

| Table                           | Columns that matter                                                       | Role                                                                                                             |
| ------------------------------- | ------------------------------------------------------------------------- | ---------------------------------------------------------------------------------------------------------------- |
| `AUTH_LEVEL`                    | `AUTH_LEVEL_ID`, `AUTH_LEVEL_NAME`, `REQUIRES_AUTHENTICATION`             | The kind of proof                                                                                                |
| `AUTH_LEVEL_GROUPING`           | `AUTH_LEVEL_GROUPING_ID`, `AUTH_LEVEL_ID`, `NAME`, `AUTH_PROVIDER_ID`     | A named, selectable instance of a level; `AUTH_PROVIDER_ID`, added in 4.2.1.11, lets a grouping pin one provider |
| `AUTH_RULE`                     | `RULE_ID`, `NAME`, `DESCRIPTION`                                          | The flow                                                                                                         |
| `RULE_AUTHENTICATION_STEP`      | `ID`, `RULE_ID`, `AUTH_LEVEL_GROUPING_ID`, `ADAPTIVE_RISK_ID`, `LIFETIME` | One node in the flow                                                                                             |
| `RULE_AUTHENTICATION_STEP_XREF` | `ID`, `PARENT_ID`, `CHILD_ID`, `PARENT_RISK_RESULT`                       | The edges                                                                                                        |

A rule is a directed graph of steps. Each step names one `AUTH_LEVEL_GROUPING_ID` and, optionally, one `ADAPTIVE_RISK_ID`. Edges in `RULE_AUTHENTICATION_STEP_XREF` carry `PARENT_RISK_RESULT` — `'Y'` is the "parent step succeeded / risk matched" branch. Every complete path must end at a step whose grouping is the terminal `AUTHORIZATION_ACCEPT`; without it the user is never granted the session. `AUTHORIZATION_DECLINE` is the explicit reject terminal.

The simplest shipped rule is two steps and one edge. These are the rows as they ship, shown so you can recognise the shape — build rules on **Policy → Authentication Rule**, not with `INSERT`:

{% code overflow="wrap" lineNumbers="true" %}

```sql
INSERT INTO RULE_AUTHENTICATION_STEP (ID, RULE_ID, AUTH_LEVEL_GROUPING_ID, ADAPTIVE_RISK_ID)
VALUES ('DEF_PASSWD_AUTH_RULE_1','DEF_PASSWD_AUTH_RULE','PASSWORD_AUTH',NULL);
INSERT INTO RULE_AUTHENTICATION_STEP (ID, RULE_ID, AUTH_LEVEL_GROUPING_ID, ADAPTIVE_RISK_ID)
VALUES ('DEF_PASSWD_AUTH_RULE_2','DEF_PASSWD_AUTH_RULE','AUTHORIZATION_ACCEPT',NULL);
INSERT INTO RULE_AUTHENTICATION_STEP_XREF (ID, PARENT_ID, CHILD_ID, PARENT_RISK_RESULT)
VALUES ('DEF_PASSWD_AUTH_RULE_XREF','DEF_PASSWD_AUTH_RULE_1','DEF_PASSWD_AUTH_RULE_2','Y');
```

{% endcode %}

That is `DEF_PASSWD_AUTH_RULE`, shipped since 4.2.0.0: password, then accept.

A **two-factor** rule adds a third step between them — `PASSWORD_AUTH` → `TOTP_AUTH` → `AUTHORIZATION_ACCEPT`. An **any-of** rule instead gives several root steps a single shared terminal. `DEF_ANY_AUTH_RULE` does exactly that: `CRIIPTO_AUTH`, `PASSWORD_AUTH`, `WEB_AUTH` and `CERT_AUTH` steps each edge straight to the one `DEF_ANY_AUTH_RULE_2` accept step, and later releases add `DUO_AUTH`, `KERB_AUTH`, `PUSH_AUTH`, `SMS_AUTH`, `TOTP_AUTH`, `EMAIL_AUTH`, `CHALLENGE_RESPONSE_AUTH` and `VOICE_AUTH` roots to the same rule. Chained steps mean *and*; sibling roots mean *or*. Open `DEF_ANY_AUTH_RULE` in the rule editor to see the fan-in for your release.

**20 rules ship.** The ones you will meet first are `DEF_PASSWD_AUTH_RULE`, `DEF_ANY_AUTH_RULE`, `DEF_NONE_AUTH_RULE`, `DEF_OAUTH_AUTH_RULE`, one `DEF_*_AUTH_RULE` per second factor, plus `STEPUP_AUTH_RULE` ("Def. stepup auth") and `UNLOCK_ACCOUNT_RULE` ("Def. account unlock" — the flow used to unlock an account).

**Most of those single-factor rules are passwordless as they stand.** `DEF_WEB_AUTH_RULE`, `DEF_CERT_AUTH_RULE`, `DEF_KERB_AUTH_RULE`, `DEF_CRIIPTO_AUTH_RULE`, `DEF_DUO_AUTH_RULE`, `DEF_CHALLENGE_RESPONSE_AUTH`, `DEF_VOICE_AUTH`, `DEF_SAS_AUTH_RULE`, `DEF_QUESTION_AUTH_RULE`, `DEF_CAPTCHA_AUTH_RULE`, `DEF_SMS_AUTH_RULE` and `DEF_TOTP_AUTH_RULE` each contain exactly one factor step and no `PASSWORD_AUTH`, so attaching one of them means the user is never shown a password page. Whether that is a security improvement or a hole depends entirely on which factor it is — see [Passwordless sign-in](/administration-guide/security/authentication-model.md#passwordless-sign-in).

A rule reaches live traffic through `CP_RULE_XREF` (content provider) or `URI_RULE_XREF` (URI pattern), both with an `EXEC_ORDER`. When rules arrived in 4.2.0.0 **every** content provider that had no rule was given `DEF_PASSWD_AUTH_RULE` at `EXEC_ORDER` 0. That row is why password sign-in works on a fresh install, and it is the row to leave alone; you can see it as the first entry in the **Authentication rules** list on the content provider.

`RULE_AUTHENTICATION_STEP.LIFETIME` is an integer added in 4.2.2.0, edited on the **Auth Provider** panel of a selected step under the label **Lifetime (minutes)**. A step with `LIFETIME` `15` counts as satisfied for fifteen minutes, so a user who has just completed it is not asked again inside that window. It ships empty, which means no reuse. See [Step-up authentication](/administration-guide/security/step-up-authentication.md).

## Capability flags on the provider type

`AUTH_PROVIDER_TYPE` carries these `CHAR(1)` flags. None is editable from the Webconsole.

| Column                       | Meaning when `Y`                                                | Since   | Types set to `Y` by shipped SQL                                                        |
| ---------------------------- | --------------------------------------------------------------- | ------- | -------------------------------------------------------------------------------------- |
| `SUPPORTS_TOTP`              | The type may offer TOTP as a second factor                      | 4.0.0.0 | `OPENIAM_POLICY` only                                                                  |
| `SUPPORTS_SMS_OTP`           | The type may offer SMS OTP                                      | 4.0.0.0 | `OPENIAM_POLICY` only                                                                  |
| `SUPPORTS_EMAIL_OTP`         | The type may offer email OTP                                    | 4.2.0.0 | `OPENIAM_POLICY` only                                                                  |
| `SUPPORTS_CERT_AUTH`         | The type may offer certificate authentication                   | 4.0.0.0 | `OPENIAM_POLICY` only                                                                  |
| `SUPPORTS_CRIIPTO_AUTH`      | The type may reference a Criipto provider                       | 4.2.1.6 | `OPENIAM_POLICY` only                                                                  |
| `SUPPORTS_DUO_AUTH`          | The type may reference a Duo provider                           | 4.2.1.8 | `OPENIAM_POLICY` only                                                                  |
| `SUPPORTS_JUST_IN_TIME_AUTH` | The type may create the user on first successful authentication | 4.0.0.0 | `SAML_SP_PROVIDER`                                                                     |
| `HAS_AUTHN_POLICY`           | The type has an **Authentication policy** field                 | 4.0.0.0 | `OPENIAM_POLICY`, `SAML_SP_PROVIDER`, `CRIIPTO_AUTH_PROVIDER`, `DUO_AUTH_PROVIDER`     |
| `AUTHN_POLICY_REQUIRED`      | That field cannot be left empty                                 | 4.0.0.0 | the same four                                                                          |
| `IS_LINKABLE_TO_CP`          | The type can be selected on a content provider                  | 4.0.0.0 | `OPENIAM_POLICY`, `GOOGLE_AUTH_PROVIDER`, `FACEBOOK_AUTH_PROVIDER`, `SAML_SP_PROVIDER` |

The first seven also exist **on the provider row** — `AUTH_PROVIDER.SUPPORTS_TOTP` and its siblings. The type flag decides whether the checkbox is rendered; the provider flag *is* the checkbox, and it is the runtime gate. `HAS_AUTHN_POLICY`, `AUTHN_POLICY_REQUIRED` and `IS_LINKABLE_TO_CP` have no counterpart column on `AUTH_PROVIDER`.

The type flags are not shown anywhere in the Webconsole. The read-only break-glass query is:

{% code overflow="wrap" %}

```sql
SELECT PROVIDER_TYPE, SUPPORTS_TOTP, SUPPORTS_SMS_OTP, SUPPORTS_EMAIL_OTP,
       SUPPORTS_CERT_AUTH, SUPPORTS_DUO_AUTH, SUPPORTS_CRIIPTO_AUTH
  FROM AUTH_PROVIDER_TYPE;
```

{% endcode %}

It changes nothing and is not audited as a read; its only cost is that you are reading the schema rather than the product's own view of it.

## The `Authentication` property category

The `Authentication` category of **Administration → System Configuration** is **15** keys. Read the current values there; the shipped defaults are:

| Property                                                    | Shipped default         | Type    | What it does                                                                                                                                                                                                   |
| ----------------------------------------------------------- | ----------------------- | ------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `org.openiam.session.max.concurrent`                        | `0`                     | Integer | `0` means unlimited. Above `0`, a successful authentication evicts the oldest active sessions until the count fits, setting `AUTH_STATE` to `0.0` and deleting the matching `OAUTH_TOKEN` rows by `SESSION_ID` |
| `org.openiam.saml.time.threshold.minutes`                   | `10`                    | Integer | Clock-skew tolerance accepted on a SAML response                                                                                                                                                               |
| `org.openiam.send.user.activation.link`                     | `true`                  | Boolean | New users are emailed an activation link                                                                                                                                                                       |
| `org.openiam.approve.new.device.message.info`               | `false`                 | Boolean | Email the user when they sign in from an unrecognised device                                                                                                                                                   |
| `org.openiam.approve.new.device.message.approve.link`       | `false`                 | Boolean | Include an approval link in that email                                                                                                                                                                         |
| `org.openiam.approve.new.ip.message.info`                   | `false`                 | Boolean | Same, for a new client IP                                                                                                                                                                                      |
| `org.openiam.approve.new.ip.message.approve.link`           | `false`                 | Boolean | Same, approval link                                                                                                                                                                                            |
| `org.openiam.approve.new.location.message.info`             | `false`                 | Boolean | Same, for a new city or country                                                                                                                                                                                |
| `org.openiam.approve.new.location.message.approve.link`     | `false`                 | Boolean | Same, approval link                                                                                                                                                                                            |
| `org.openiam.password.policy.resolver.script`               | *(empty)*               | Groovy  | A `DefaultPasswordPolicyResolver` subclass deciding which policy applies to an identity. Empty means the built-in resolver                                                                                     |
| `org.openiam.challenge.response.custom.validator`           | *(empty)*               | Groovy  | A `ChallengeResponseValidator` implementation. Empty means the Spring-wired default                                                                                                                            |
| `org.openiam.api.calls.base.domain`                         | `http://localhost:8080` | String  | Base domain used when building API call URLs                                                                                                                                                                   |
| `org.openiam.webconsole.defaultBaseDomain`                  | *(empty)*               | String  | Default base domain for the Webconsole                                                                                                                                                                         |
| `org.openiam.include.oauth.authorize.url.in.consent.scopes` | `false`                 | Boolean | Include the OAuth authorize URL in the consent-scope display                                                                                                                                                   |
| `org.openiam.idm.fido.auth.rp.name`                         | `OpenIAM`               | String  | WebAuthn relying-party display name                                                                                                                                                                            |

The six `approve.new.*` flags all feed one comparison. On a user's first sign-in OpenIAM records the client IP, the device fingerprint and — when `org.openiam.adaptive.risk.geo.location.use` is on — the city and country into `USER_AUTH_PARAM`; on later sign-ins it compares what arrives against what is stored, and those six properties decide whether the user is merely informed or asked to approve. The approval link lands on `/idp/approve-auth-param`. To see the mechanism working, sign in as a test identity, then sign in again from a different browser or network: the second sign-in is the one that sends mail. Both of the Groovy properties above, and their built-in fallbacks, are cached, so a change to either needs the cache to turn over before it takes effect.

## Audit actions for authentication changes

Filter the audit log on `LOGIN`, `DO_LOGIN`, `DO_LOGIN_AGAINST_MANAGED_SYSTEM`, `GENERATE_TOTP_SECRET`, `SEND_SMS_OTP_TOKEN` / `SEND_EMAIL_OTP_TOKEN` / `SEND_OTP_TOKEN`, `CONFIRM_SMS_OTP_TOKEN`, `INCREMENT_FAIL_AUTH_COUNT`, `AUTH_PUSH_COMPLETE` / `AUTH_PUSH_DECISION`, `CAPTCHA_AUTHENTICATION` / `QUESTION_AUTHENTICATION`, `LOGIN_PKS12`, `LOGIN_VIA_SOCIAL`, and `SAVE_AUTH_RULE` / `DELETE_AUTH_RULE`.

## Related

* [Authentication providers and MFA](/administration-guide/security/authentication-providers-and-mfa.md) — the decision this page supports
* [The authentication model](/administration-guide/security/authentication-model.md) — how a rule reaches live traffic
* [System property reference](/administration-guide/reference/system-properties.md) — every category, not just `Authentication`

*Applies to OpenIAM 2026.8.1. Last reviewed 7 August 2026.*


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation by asking a question.

Perform an HTTP GET request on the following URL with the `ask` and `goal` query parameters:

```
GET https://docs.openiam.com/administration-guide/security/authentication-providers-and-mfa/authentication-provider-reference.md?ask=<question>&goal=<user_goal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is what the user is ultimately trying to achieve, the reason they need the answer. Sharing it helps GitBook give you a better, more relevant answer. A goal is most helpful when it describes the outcome the user wants rather than restating the question. For example, with `ask=how do I create an API token`, a goal like `automate deployments from our CI pipeline` lets GitBook tailor the answer to that use case.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
