# Authenticators and MFA

> Octelium documentation. Canonical page: <https://octelium.com/docs/octelium/latest/management/core/authenticator>.

Octelium supports *Authenticators* since version `v0.21.0`. There are currently three types of *Authenticators*:

- **FIDO** Used by FIDO/WebAuthn compliant authenticators such as security keys (e.g. Yubikey), hardware-backed platform authenticators (e.g. Windows Hello, Android) and software-based keys (e.g. Google Password Manager). FIDO *Authenticators* can be used for both MFA after a successful *IdentityProvider* authentication, manual re-authentication for existent browser-based  *Sessions*, as well as for direct passwordless login via Passkeys.

- **TOTP** Time-based one-time password (TOTP) *Authenticators* can be using via apps such as Google Authenticator for MFA right after a successful *IdentityProvider* authentication as well as for re-authentication for both browser-based *Sessions* as well as for `CLIENT`-based *Sessions* used by the `octelium` and `octeliumctl` CLIs.

- **TPM** This *Authenticator* uses TPM (Trusted Platform Module) 2.0 to provide hardware-based re-authentication for `CLIENT`-based *Sessions* used by the `octelium` and `octeliumctl` CLIs. This *Authenticator* currently works for `LINUX` and `WINDOWS` *Devices*.

## Listing Authenticators

You can list the *Cluster*'s *Authenticators* (read more about listing resources [here](https://octelium.com/docs/octelium/latest/management/core/overview.md#listing-resources)) as follows:

```bash
octeliumctl get authenticator
# Or simply
octeliumctl get authn

# List Authenticators filtered by a certain User
octeliumctl get authn --user john

# Show a certain IdentityProvider
octeliumctl get authn <NAME>
```

## Authentication Enforcement Rules

By default, once *Users* authenticate themselves via a web-based *IdentityProvider*, their *Session* is considered valid and can be used to access the authorized *Services* without enforcing MFA via *Authenticators*. You can, however, override that behavior and explicitly require *Authenticator* authentication as MFA right after a successful *IdentityProvider* authentication via the `authenticationEnforcementRules` list. Each rule has a condition and an effect that is set to either `ENFORCE` to enforce/require the *Authenticator* authentication, `RECOMMEND` or `IGNORE` to require/ignore enforcement on the condition match. Here is an example:

```yaml
kind: ClusterConfig
metadata:
  name: cluster-config
spec:
  authenticator:
    authenticationEnforcementRules:
    - condition:
        match: ctx.user.spec.email.endsWith("@example.com")
      effect: ENFORCE
    - condition:
        match: '"friends" in ctx.user.spec.groups'
      effect: IGNORE
    - condition:
        match: ctx.session.status.isBrowser
      effect: ENFORCE
    - condition:
        match: ctx.identityProvider.status.type in ["SAML", "OIDC"]
      effect: RECOMMEND
```

## Registration Enforcement Rules

By default, the *Cluster* does not try to force *Users* to enroll *Authenticators* after a successful web-based login via an *IdentityProvider*. You can, however, override that behavior and dynamically require an *Authenticator* registration based on identity and/or context via the `registrationEnforcementRules` list. Each rule has a condition and an effect that is set to either `ENFORCE`, `RECOMMEND` or `IGNORE` to require/ignore enforcement on the condition match. Here is an example:

```yaml
kind: ClusterConfig
metadata:
  name: cluster-config
spec:
  authenticator:
    registrationEnforcementRules:
    - condition:
        match: ctx.user.spec.email.endsWith("@example.com")
      effect: ENFORCE
    - condition:
        match: size(ctx.authenticatorList.items) == 0
      effect: ENFORCE
    - condition:
        match: '"friends" in ctx.user.spec.groups'
      effect: IGNORE
    - condition:
        match: ctx.identityProvider.status.type in ["SAML", "OIDC"]
      effect: RECOMMEND
```

## Post Authentication Rules

By default, a *Session* is created on a valid login and re-validated on a successful re-authentication. However, you can override that behavior and deny a successful *Authenticator* authentication based on identity and context via the `postAuthenticationRules` list. For example, you might want to prevent more privileged *Users* from using TOTP *Authenticators* and only allow them to authenticate via hardware-based FIDO2 security keys. You can also prevent authentications on a certain time and day. Here is an example:

```yaml
kind: ClusterConfig
metadata:
  name: cluster-config
spec:
  authenticator:
    postAuthenticationRules:
    - condition:
        match: ctx.authenticator.status.type == "TOTP"
      effect: DENY
    - condition:
        all:
          of:
          - match: ctx.user.spec.groups.hasAny(["dev", "ops"])
          - match: ctx.session.status.isBrowser
          - not: ctx.authenticator.status.info.fido.isAttestationVerified
      effect: DENY
    - condition:
        all:
          of:
          - match: ctx.session.status.isBrowser
          - not: ctx.authenticator.status.info.fido.isHardware
      effect: DENY
```

## FIDO

### Attestation

During the registration process of a FIDO *Authenticator*, the *Authenticator* attestation, if provided in the registration ceremony response, is verified against the [MDSv3](https://fidoalliance.org/metadata/) blob maintained by FIDO Alliance (read more about WebAuthn attestation [here](https://developers.yubico.com/WebAuthn/Concepts/Securing_WebAuthn_with_Attestation.html)). You can use such information in your *Policies*' access control decisions as shown below [here](#access-control), for example, to verify that the authentication is made by a genuine hardware-backed *Authenticator* and additionally force a list of allowed manufacturer AAGUIDs.

### Passkey Login

By default, the ability to login with Passkeys is not enabled. You can explicitly enable it in the *ClusterConfig* as follows:

```yaml
kind: ClusterConfig
metadata:
  name: cluster-config
spec:
  authenticator:
    # !mark
    enablePasskeyLogin: true
```

Now any registered FIDO *Authenticator* that supports resident key credentials can be used for Passkey login. Once enabled by setting `enablePasskeyLogin`, your *Cluster*'s login page will show a "Login with a Passkey" button as shown below.

![Octelium Passkey](docs/passkey.webp)

### Attestation Conveyance Preference

By default, Octelium uses `DIRECT` attestation (read more [here](https://www.w3.org/TR/webauthn-2/#enum-attestation-convey)). You can, however, override that default preference and use `INDIRECT`, `ENTERPRISE` or `NONE` attestation conveyance preferences. Here is an example:

```yaml
kind: ClusterConfig
metadata:
  name: cluster-config
spec:
  authenticator:
    fido:
      # !mark
      attestationConveyancePreference: INDIRECT
```

## Access Control

You can use the current *Authenticator* information in your *Policies*' rules to control access to *Services*. Here is an example that only allows access when the *Session* was authenticated via a hardware-backed, attestation-verified FIDO *Authenticator* with a specific AAGUID, verified attestation, etc...:

```yaml
kind: Policy
metadata:
  name: allowed-authenticators
spec:
  rules:
    - effect: ALLOW
      condition:
        all:
          of:
          - match: ctx.session.status.authentication.info.type == "AUTHENTICATOR"
          - match: ctx.session.status.authentication.info.authenticator.info.fido.isAttestationVerified
          - match: ctx.session.status.authentication.info.authenticator.info.fido.isHardware
          - match: ctx.session.status.authentication.info.authenticator.info.fido.userVerified
          - match: ctx.session.status.authentication.info.authenticator.info.fido.userPresent
          - match: ctx.session.status.authentication.info.authenticator.info.fido.aaguid == "cb69481e-8ff7-4039-93ec-0a2729a154a8"
```

## State

An *Authenticator* has one of 3 states at a time, namely `ACTIVE`, `REJECTED` and `PENDING` as follows:

- `ACTIVE` indicates an active *Authenticator*.
- `REJECTED` means that the *Authenticator* is simply deactivated and cannot be used for authentication unless the *Authenticator* is set to `ACTIVE` again.
- `PENDING` indicates that the *Authenticator* is still pending for a decision whether to be activated or deactivated or `REJECTED`.

The *Cluster* by default automatically sets the state for a newly created *Device* to `ACTIVE`. You can explicitly set a default *Device* state for a specific *User* (read more [here](https://octelium.com/docs/octelium/latest/management/core/user.md#device)) as follows:

```yaml
kind: User
metadata:
  name: john
spec:
  type: HUMAN
  # !mark(1:2)
  authentication:
    authenticatorDefaultState: ACTIVE
```

You can also set the default state at the *Cluster* level via *ClusterConfig* (read more [here](https://octelium.com/docs/octelium/latest/management/core/cluster-config.md#authenticator)), as follows:

```yaml
kind: ClusterConfig
metadata:
  name: cluster-config
spec:
  authenticator:
    # !mark
    defaultState: PENDING
```

### Approving Authenticators

You can approve an *Authenticator* to set its state to `ACTIVE` simply as follows:

```bash
octeliumctl update authn --approve <AUTHENTICATOR_NAME>
```

### Rejecting Authenticators

You can reject an *Authenticator* to set its state to `REJECTED` simply as follows:

```bash
octeliumctl update authn --reject <AUTHENTICATOR_NAME>
```

## API Reference

[Authenticator JSON Schema (Octelium core/v1, latest documentation)](https://octelium.com/schemas/octelium/latest/Authenticator.schema.json)
