> For the complete documentation index, see [llms.txt](https://docs.fylr.io/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://docs.fylr.io/for-developers/api/oauth2.md).

# OAuth2

User authentication in a fylr instance is done by obtaining an **Access Token** through one of the implemented OAuth2 flows. fylr is also an **OpenID Connect (OIDC)** provider: requesting the `openid` scope additionally returns an `id_token`, and the instance exposes a [UserInfo endpoint](#openid-connect) and an [OIDC discovery document](#openid-connect).

{% hint style="info" %}
The exact request and response of every endpoint below — `/api/oauth2/auth`, `/api/oauth2/token`, `/api/oauth2/revoke`, `/api/oauth2/introspect`, `/api/oauth2/userinfo` — is also available as interactive reference panels under [API → Endpoints → /api/oauth2](/for-developers/api/endpoints/api-oauth2.md).
{% endhint %}

## Using the Access Token

After a successful login, the response contains an `access_token`. Send it with every following request to the fylr API in one of these ways:

**`Authorization` header (recommended):**

```
Authorization: Bearer <access_token>
```

**URL query parameter:**

```
?access_token=<access_token>
```

**`X-Fylr-Authorization` header** — a fallback for Safari, which can strip the `Authorization` header on cross-origin redirects:

```
X-Fylr-Authorization: Bearer <access_token>
```

An access token is valid for **24 hours** (86400 seconds) by default; the lifetime is configurable in the base configuration.

## Configuring Client ID and Secret

The descriptions of the OAuth2 flows below use `my-client` and `my-secret` as placeholders for configured Client IDs and Client Secrets. Replace these with the OAuth2 client information of your fylr instance.

Configure the pair(s) of Client ID and Secret in the [config file](/for-system-administrators/configuration/fylr.example.yml.md) `fylr.yml`:

{% code title="" %}

```yaml
fylr:
  services:
    api:
      oauth2Server:
        clients:
          my-client:
            secret: 'my-secret'
            redirectURIs:
              - http://my-callback-server/oauth2/callback
```

{% endcode %}

The default clients in fylr are **public** and thus neither need nor have a secret. A public client must omit `client_secret`.

Alternatively, add the Client ID and Secret pair(s) in the [Base Configuration](https://github.com/programmfabrik/fylr-gitbook/blob/main/for-developers/api/for-administrators/readme/user-management/README.md#oauth-service).

## Scopes

fylr understands four scopes, passed as a **space-separated** list in the `scope` parameter (e.g. `openid offline`):

| Scope           | Meaning                                                                                                                                                                          |
| --------------- | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `offline`       | Requests a **refresh token** (the OIDC alias `offline_access` works too). Without it no refresh token is issued. Only the Authorization Code and Password grants can return one. |
| `openid`        | Marks an [OpenID Connect](#openid-connect) request: the token response then also contains an `id_token`.                                                                         |
| `read`, `write` | Accepted labels only — they do **not** govern API access. What a user may do is decided by fylr's per-user / per-pool **ACL rights**, not by the token scope.                    |

A client with no `scopes` configured may request any of the four; a scope outside the client's allowed set is rejected. The `scope` value in the token response **echoes the scopes that were granted** (for example `read write openid offline`), it is not a fixed value.

## OAuth2 Flows

fylr implements the **Authorization Code**, **Password**, **Client Credentials** and **Refresh Token** grants. We recommend the [Authorization Code Grant](#authorization-code-grant) (with [PKCE](#authorization-code-grant-with-pkce-code-challenge) for public clients) or the [Password Grant](#password-grant).

{% hint style="warning" %}
The **Implicit Grant** (`response_type=token`) was **removed in fylr 6.33.0**. `/api/oauth2/auth` now accepts only `response_type=code`; a request with `response_type=token` is rejected.
{% endhint %}

### Authorization Code Grant

{% hint style="info" %}
External documentation: <https://www.oauth.com/oauth2-servers/server-side-apps/authorization-code/>
{% endhint %}

This flow requires a **Client ID** (and a **Secret** for confidential clients), and a fylr **login** and **password** for each user. It offers a high level of security.

#### **Step 1**: client calls fylr

<mark style="color:blue;">`GET`</mark> `fylr-instance/api/oauth2/auth`

**Query Parameters**

| Name                                              | Type   | Description                                                                                                         |
| ------------------------------------------------- | ------ | ------------------------------------------------------------------------------------------------------------------- |
| `response_type`<mark style="color:red;">\*</mark> | string | fixed value: `"code"`                                                                                               |
| `client_id`<mark style="color:red;">\*</mark>     | string | **Client ID**, e.g. `"my-client"`                                                                                   |
| `state`<mark style="color:red;">\*</mark>         | string | Client state string (min. 8 characters), e.g. `"Authorization_Code_Grant_Login"`                                    |
| `scope`                                           | string | Space-separated [scopes](#scopes). Include `offline` to receive a refresh token, `openid` to receive an `id_token`. |
| `auth_method`                                     | string | Login method (see [auth\_method](#auth_method) below). Defaults to `auto`.                                          |
| `redirect_uri`                                    | string | Callback URL; must match a `redirectURIs` entry of the client. Required if the client has more than one.            |

{% tabs %}
{% tab title="200 OK" %}
This redirects to the fylr login page. The user enters **login** and **password** directly into fylr.
{% endtab %}

{% tab title="400 Error" %}
Problems with the parameters, for example an invalid **Client ID**. The endpoint returns an error description explaining what is wrong.
{% endtab %}
{% endtabs %}

#### `auth_method`

`auth_method` selects the login method. It is **optional** and accepts a single value or a comma-separated list of methods, tried in order:

| Value                                | Meaning                                                                                                                                              |
| ------------------------------------ | ---------------------------------------------------------------------------------------------------------------------------------------------------- |
| `easydb`                             | Login + password against the fylr user database — the typical choice, and the fallback for unrecognized values.                                      |
| `ldap`                               | LDAP login (if an LDAP connection is configured).                                                                                                    |
| `email`, `collection`, `action_code` | Email-link, collection-share and action-code logins.                                                                                                 |
| `saml`                               | SAML single sign-on.                                                                                                                                 |
| `anonymous`                          | Guest login (requires guest access to be enabled).                                                                                                   |
| `auto`                               | The default. A server-side selector used by the web app: anonymous when guest login is enabled and no `login` is supplied, otherwise password login. |

For an interactive end-user login, use `easydb` (or omit the parameter).

#### **Step 2**: callback from fylr to the local HTTP server

This flow requires a local HTTP server that can handle the callback from fylr. Its URL must be listed in `fylr.yml` (`redirectURIs`) for the client.

fylr calls `my-callback-server/oauth2/callback` as a `GET` request with these parameters:

| Name                                      | Type   | Description                                         |
| ----------------------------------------- | ------ | --------------------------------------------------- |
| `state`<mark style="color:red;">\*</mark> | string | Client state, identical to Step 1                   |
| `code`<mark style="color:red;">\*</mark>  | string | **Authorization Code** — store it for the next step |

{% hint style="info" %}
If the callback redirects to a **cross-origin** destination, that destination must be allow-listed in `fylr.services.webapp.loginAllowRedirects` (new in 6.33.0). Same-origin destinations and the configured `externalURL` work without extra configuration.
{% endhint %}

#### **Step 3**: exchange the Authorization Code for a token

<mark style="color:green;">`POST`</mark> `fylr-instance/api/oauth2/token`

**Body Parameters** (`application/x-www-form-urlencoded`)

| Name                                           | Type   | Description                                       |
| ---------------------------------------------- | ------ | ------------------------------------------------- |
| `grant_type`<mark style="color:red;">\*</mark> | string | fixed value: `"authorization_code"`               |
| `code`<mark style="color:red;">\*</mark>       | string | **Authorization Code** from the callback          |
| `client_id`<mark style="color:red;">\*</mark>  | string | **Client ID**                                     |
| `client_secret`                                | string | **Client Secret** — omit for public clients       |
| `redirect_uri`                                 | string | Same `redirect_uri` as in Step 1, if one was used |

{% tabs %}
{% tab title="200 OK" %}
fylr returns a JSON object:

| Key             | Description                                                                        |
| --------------- | ---------------------------------------------------------------------------------- |
| `access_token`  | **Access Token**                                                                   |
| `token_type`    | `"bearer"`                                                                         |
| `scope`         | The granted scopes, e.g. `"read write openid offline"`                             |
| `expires_in`    | Seconds until the **Access Token** expires                                         |
| `refresh_token` | **Refresh Token** — only when the `offline` scope was granted                      |
| `id_token`      | OpenID Connect **ID Token** (RS256 JWT) — only when the `openid` scope was granted |
| {% endtab %}    |                                                                                    |

{% tab title="400 Error" %}
Problems with the parameters, for example an invalid **Client ID** or an expired code.
{% endtab %}
{% endtabs %}

### Authorization Code Grant with PKCE Code Challenge

{% hint style="info" %}
External documentation: <https://www.oauth.com/oauth2-servers/oauth-native-apps/pkce/>
{% endhint %}

This is an extension of the [Authorization Code Grant](#authorization-code-grant) that adds a Proof Key for Code Exchange (PKCE), so a public client can use the flow securely without a client secret. All other parameters and keys are the same.

The client generates a **Code Verifier** and a **Code Challenge** per [RFC 7636](https://datatracker.ietf.org/doc/html/rfc7636):

* The **Code Verifier** is a random string of `A-Z`, `a-z`, `0-9`, `-`, `.`, `_`, `~`, 43–128 characters long.
* The **Code Challenge** is the `SHA256` hash of the Code Verifier, Base64-URL encoded.

For **Step 1**, add to the URL:

| Parameter               | Value    | Description                  |
| ----------------------- | -------- | ---------------------------- |
| `code_challenge`        |          | Generated **Code Challenge** |
| `code_challenge_method` | `"S256"` | fixed value                  |

For **Step 3**, add to the body:

| Parameter       | Value | Description                 |
| --------------- | ----- | --------------------------- |
| `code_verifier` |       | Generated **Code Verifier** |

fylr checks that the **Code Verifier** matches the **Code Challenge** from Step 1.

### Password Grant

{% hint style="info" %}
External documentation: <https://www.oauth.com/oauth2-servers/access-tokens/password-grant/>
{% endhint %}

Log directly into fylr with a user **login** and **password**.

<mark style="color:green;">`POST`</mark> `fylr-instance/api/oauth2/token`

**Body Parameters** (`application/x-www-form-urlencoded`)

| Name                                           | Type   | Description                                                              |
| ---------------------------------------------- | ------ | ------------------------------------------------------------------------ |
| `grant_type`<mark style="color:red;">\*</mark> | string | fixed value: `"password"`                                                |
| `username`<mark style="color:red;">\*</mark>   | string | fylr **login** of the user                                               |
| `password`<mark style="color:red;">\*</mark>   | string | fylr **password** of the user                                            |
| `client_id`<mark style="color:red;">\*</mark>  | string | **Client ID**                                                            |
| `client_secret`                                | string | **Client Secret** — omit for public clients                              |
| `scope`                                        | string | Space-separated [scopes](#scopes); include `offline` for a refresh token |

{% tabs %}
{% tab title="200 OK" %}
Same response as the Authorization Code Grant: `access_token`, `token_type` (`"bearer"`), `scope`, `expires_in`, plus `refresh_token` (with `offline`) and `id_token` (with `openid`).
{% endtab %}

{% tab title="400 Error" %}
Invalid **Client ID**, or wrong user **login** / **password**.
{% endtab %}
{% endtabs %}

**Example (CURL)**:

```bash
curl 'fylr-instance/api/oauth2/token' \
  --data-urlencode 'grant_type=password' \
  --data-urlencode 'scope=offline' \
  --data-urlencode 'client_id=my-client' \
  --data-urlencode 'username=Login' \
  --data-urlencode 'password=Password'
```

### Refresh Token Grant

When a token response contained a `refresh_token` (i.e. the `offline` scope was granted), exchange it for a fresh access token without asking the user to log in again.

<mark style="color:green;">`POST`</mark> `fylr-instance/api/oauth2/token`

**Body Parameters** (`application/x-www-form-urlencoded`)

| Name                                              | Type   | Description                                 |
| ------------------------------------------------- | ------ | ------------------------------------------- |
| `grant_type`<mark style="color:red;">\*</mark>    | string | fixed value: `"refresh_token"`              |
| `refresh_token`<mark style="color:red;">\*</mark> | string | The refresh token                           |
| `client_id`<mark style="color:red;">\*</mark>     | string | **Client ID**                               |
| `client_secret`                                   | string | **Client Secret** — omit for public clients |

The response is a new `access_token` (and a new `refresh_token`), with the same fields as above.

### Client Credentials Grant

{% hint style="info" %}
External documentation: <https://www.oauth.com/oauth2-servers/access-tokens/client-credentials/>
{% endhint %}

{% hint style="warning" %}
The behaviour of this grant **changed in fylr 6.33.0**.
{% endhint %}

As of 6.33.0 the client-credentials grant returns a token for the **anonymous user** bound to the browser client — so a public frontend can obtain an anonymous session without any user credentials. This requires **guest access** to be enabled in the base configuration (`login.guest`); with guest access off the request is rejected and a warning is written to the fylr log. Anonymous logins are recorded as `USER_LOGIN` / `USER_LOGIN_FAILED` events.

<mark style="color:green;">`POST`</mark> `fylr-instance/api/oauth2/token`

**Body Parameters** (`application/x-www-form-urlencoded`)

| Name                                           | Type   | Description                                 |
| ---------------------------------------------- | ------ | ------------------------------------------- |
| `grant_type`<mark style="color:red;">\*</mark> | string | fixed value: `"client_credentials"`         |
| `client_id`<mark style="color:red;">\*</mark>  | string | **Client ID**                               |
| `client_secret`                                | string | **Client Secret** — omit for public clients |

The response contains `access_token`, `token_type` (`"bearer"`), `scope` and `expires_in`. This grant does not return a refresh token.

## OpenID Connect

fylr is also an **OpenID Connect** provider. Request the `openid` scope (alongside `offline` etc.) and the token response additionally contains an **`id_token`** — a JWT signed with `RS256`.

**Discovery document** — a public, unauthenticated metadata document served at the server root (not under `/api`):

<mark style="color:blue;">`GET`</mark> `fylr-instance/.well-known/openid-configuration`

It advertises the issuer and the absolute URLs of the authorization, token, introspection, revocation and userinfo endpoints, plus the supported response types, grant types, scopes and claims — so an OIDC client can configure itself automatically. See the [discovery reference panel](/for-developers/api/endpoints/api-well-known.md).

**UserInfo endpoint** — returns claims about the user identified by the bearer access token:

<mark style="color:blue;">`GET`</mark> / <mark style="color:green;">`POST`</mark> `fylr-instance/api/oauth2/userinfo`

It requires a valid access token and returns `sub` plus whatever claims (email, display name, …) are enabled in the base-config `openid.userinfo` allow-list.

## Revoking a token (Logout)

Revoke an access or refresh token ([RFC 7009](https://datatracker.ietf.org/doc/html/rfc7009)). This is what the web app uses to log out; a `USER_LOGOUT` event is recorded for the token's user.

<mark style="color:green;">`POST`</mark> `fylr-instance/api/oauth2/revoke`

**Body Parameters** (`application/x-www-form-urlencoded`)

| Name                                      | Type   | Description                                 |
| ----------------------------------------- | ------ | ------------------------------------------- |
| `token`<mark style="color:red;">\*</mark> | string | The access or refresh token to revoke       |
| `token_type_hint`                         | string | Optional: `access_token` or `refresh_token` |

Per RFC 7009 the endpoint returns `200` even for an unknown token.

## Token Introspection

Inspect the state of a token ([RFC 7662](https://datatracker.ietf.org/doc/html/rfc7662)).

<mark style="color:green;">`POST`</mark> `fylr-instance/api/oauth2/introspect`

**Body Parameters** (`application/x-www-form-urlencoded`)

| Name                                      | Type   | Description                                 |
| ----------------------------------------- | ------ | ------------------------------------------- |
| `token`<mark style="color:red;">\*</mark> | string | The token to introspect                     |
| `token_type_hint`                         | string | Optional: `access_token` or `refresh_token` |

The response reports `active` (boolean) and, when active, `scope`, `client_id`, `sub`, `exp`, `iat` and `token_type`.


---

# 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 dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://docs.fylr.io/for-developers/api/oauth2.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
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.
