# Authentication Servers

> Let several DS Server instances accept each other's access tokens: register authentication servers in the admin portal, with the Web API or the Admin package.

- **Product:** DS Server
- **Section:** Documentation
- **Kind:** Topic
- **Path:** DS Server > Administration and Settings > Authentication Servers
- **2671 words**
- **Web URL:** https://docs.textcontrol.com/ds-server/administration/authentication-servers/
- **LLMs URL:** https://docs.textcontrol.com/ds-server/administration/authentication-servers/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/ds-server/administration/authentication-servers/llms-full.txt

---

When several DS Server instances serve the same applications, for example behind a load balancer or in several regions, each instance checks access tokens against its own security profiles. A token issued by one instance is unknown to the others, and every instance would need a copy of every profile. Authentication servers solve this: an instance can register other DS Server instances as authentication servers and ask them whether a token is valid. Since version 3.1.0, you register them on the **Authentication** page of the admin portal or with the Web API.

### How It Works

Take two instances: *ds1* processes documents and has no security profiles, and *auth* holds the profiles of your applications. On *ds1*, you register *auth* as an authentication server.

```text
 Application                      ds1 (OAuth enabled)              auth (security profiles)
     |                                  |                                  |
     |-- POST /oauth/token ------------>|-- client unknown: forwarded ---->|
     |                                  |<---------------- token issued ---|
     |<-- token issued by auth ---------|                                  |
     |                                  |                                  |
     |-- request with the token ------->|-- token unknown: is it valid? -->|
     |                                  |<----------------------- valid ---|
     |<-- result, processed by ds1 -----|                                  |
```

- **Tokens.** When `ds1` receives a request with a token that it didn't issue, it asks its registered authentication servers with [POST /oauth/validatetoken](https://docs.textcontrol.com/ds-server/reference/web-api/oauth/validatetoken/post/llms-full.txt). If one of them knows the token, `ds1` processes the request; if none does, it answers with status 401.
- **Token requests.** When [POST /oauth/token](https://docs.textcontrol.com/ds-server/reference/web-api/oauth/token/post/llms-full.txt) or [GET /oauth/authorize](https://docs.textcontrol.com/ds-server/reference/web-api/oauth/authorize/get/llms-full.txt) can't authorize a client with its own profiles, `ds1` forwards the request to the authentication servers. An application can request its token from `ds1` with the client ID and secret of a profile that exists only on `auth`, and `ds1` returns the token that `auth` issued.
- **The work.** Documents are processed by the instance that received the request. The authentication server only answers whether a token is valid.

The check applies to instances with OAuth [enabled](https://docs.textcontrol.com/ds-server/administration/oauth/llms-full.txt). On the authentication server, the OAuth switch doesn't matter for this: it answers for the tokens of its own profiles either way. A registration takes effect at once, without a restart.

### Plan the Setup

The blog article [DS Server: Announcing Distributed OAuth Authentication for Scalable Applications](https://www.textcontrol.com/blog/2022/11/28/ds-server-announcing-distributed-oauth-authentication-for-scalable-applications/) describes two arrangements:

| Setup | How | Consider |
|---|---|---|
| Central authentication server | One instance holds all security profiles. Every other instance registers it as its authentication server and has no profiles of its own. | You manage the profiles in one place. When the central instance can't be reached, the others refuse tokens they didn't issue themselves. |
| Distributed authentication servers | Several instances hold the same profiles, and each registers the others. | When one instance fails, applications still get tokens from the others. The tokens that the failed instance issued are no longer accepted, because only the issuing instance knows a token, even when another instance has a copy of the same profile. The profiles have to be copied to each authentication server, as [Multiple Servers with Load Balancing](https://docs.textcontrol.com/ds-server/installation/load-balancing/#clone-the-security-profiles) describes. |

An instance can register several authentication servers. A registered server that can't be reached doesn't block the others: a token that another registered server knows is still accepted.

### Register an Authentication Server in the Portal

Sign in to the admin portal of the instance that should use the authentication server, `ds1` in the example above.

1. Click **Authentication** in the navigation bar, or the **Authentication Servers** tile on the welcome page. The **Authentication Servers** list is empty on a new installation.

    ![The Authentication page with an empty list of authentication servers](https://docs.textcontrol.com/assets/img/ds-server/portal-authentication.db09be19.webp)
2. Click **+ Register**.
3. On the **Register Authentication Server** form, enter a **Name** that identifies the server and its address in **Authentication Server Url**, such as `https://auth.example.com`. This is the service URL of the other DS Server instance, the address its admin portal and Web API answer at.

    ![The Register Authentication Server form](https://docs.textcontrol.com/assets/img/ds-server/portal-register-auth-server.4d83f08a.webp)
4. Click **Register**. The list shows the server with its **Name** and **Url**.

    ![The Authentication page with one registered authentication server](https://docs.textcontrol.com/assets/img/ds-server/portal-auth-server-list.27462780.webp)

To remove a server, click its delete icon (tooltip "Deregister Authentication Server") and confirm **Are you sure you want to deregister the authentication server?** with **Deregister**.

As long as no authentication server is registered, DS Server with OAuth enabled writes "OAuthTokenAuthenticationService.IsAuthorized called but no authentication servers are registered." to its log for each token that it didn't issue itself, before it refuses the request with status 401. The message is informational: the instance has no other server to ask.

### Register an Authentication Server With the Web API

The administration Web API has endpoints for the registered servers since version 3.1.0. Like the other administration endpoints, they take the admin user name and password in a Basic `Authorization` header.

| Task | Request | Body and answer |
|---|---|---|
| List the IDs | [GET /admin/authenticationservers](https://docs.textcontrol.com/ds-server/reference/web-api/administration/authenticationservers/get/llms-full.txt) | Returns an array such as `[1, 2]` |
| Register a server | [POST /admin/authenticationservers](https://docs.textcontrol.com/ds-server/reference/web-api/administration/authenticationservers/post/llms-full.txt) | `{"name": "...", "serverUrl": "..."}`; returns the new ID |
| Read a server | [GET /admin/authenticationservers/{id}](https://docs.textcontrol.com/ds-server/reference/web-api/administration/authenticationservers-id/get/llms-full.txt) | Returns `{"name": "...", "serverUrl": "..."}` |
| Change the URL | [PATCH /admin/authenticationservers/{id}/serverurl](https://docs.textcontrol.com/ds-server/reference/web-api/administration/authenticationservers-id-serverurl/patch/llms-full.txt) | The new URL as a JSON string |
| Deregister a server | [DELETE /admin/authenticationservers/{id}](https://docs.textcontrol.com/ds-server/reference/web-api/administration/authenticationservers-id/delete/llms-full.txt) | None |

This request registers `auth` on `ds1`:

```bash
curl --request POST 'https://ds1.example.com/admin/authenticationservers' \
    --user 'admin:<admin password>' \
    --header 'Content-Type: application/json' \
    --data-raw '{"name": "Central authentication server", "serverUrl": "https://auth.example.com"}'
```

DS Server answers with the ID of the registration, such as `1`. An ID that doesn't exist gets status 404.

### Register an Authentication Server With the Admin Package

The [TXTextControl.DocumentServices.Admin](https://docs.textcontrol.com/ds-server/reference/dotnet-api/txtextcontrol-documentservices-admin/llms-full.txt) package has a method for each endpoint. This console application registers `auth` on `ds1` and lists the registered servers:

```csharp
using TXTextControl.DocumentServices.Admin;

// Connect to the instance that should use the authentication server
Admin admin = new Admin("https://ds1.example.com", "<admin password>");

// Register the other instance
AuthenticationServer server = new AuthenticationServer()
{
    Name = "Central authentication server",
    ServerUrl = "https://auth.example.com"
};

int id = await admin.AddAuthenticationServer(server);

// List every registered authentication server
foreach (int serverId in await admin.GetAuthenticationServerIds())
{
    AuthenticationServer registered = await admin.GetAuthenticationServer(serverId);
    Console.WriteLine($"{serverId}: {registered.Name} ({registered.ServerUrl})");
}
```

The code works as follows:

- `AddAuthenticationServer` registers the server described by `Name` and `ServerUrl` and returns the ID of the registration.
- `GetAuthenticationServerIds` returns the IDs, and `GetAuthenticationServer` the name and URL of one registration. Take the ID from the list: the object that `GetAuthenticationServer` returns doesn't fill its `ID`.
- `UpdateServerUrl(id, url)` changes the address, and `DeleteAuthenticationServer(id)` removes the registration.

### The Endpoints for Tokens

Version 3.1.0 also added OAuth endpoints that DS Server instances use among each other and that help you test a setup.

| Endpoint | What it does |
|---|---|
| [POST /oauth/validatetoken](https://docs.textcontrol.com/ds-server/reference/web-api/oauth/validatetoken/post/llms-full.txt) | Takes a token as a JSON string in the body and answers `true` if this instance issued it and it is valid, otherwise `false`. It doesn't ask the registered authentication servers. |
| [GET /oauth/test](https://docs.textcontrol.com/ds-server/reference/web-api/oauth/test/get/llms-full.txt) | Answers 200 if a request with the token in the `Authorization` header, the `access_token` query parameter or the `access_token` cookie would be admitted, and 401 if not. It asks the registered authentication servers, and it answers 200 to any request when OAuth is disabled. |
| [POST /authenticationserver/token](https://docs.textcontrol.com/ds-server/reference/web-api/authenticationserver/token/post/llms-full.txt) | Issues a token like [POST /oauth/token](https://docs.textcontrol.com/ds-server/reference/web-api/oauth/token/post/llms-full.txt), but without forwarding a failed request to the registered authentication servers |
| [GET /authenticationserver/authorize](https://docs.textcontrol.com/ds-server/reference/web-api/authenticationserver/authorize/get/llms-full.txt) | Authorizes like [GET /oauth/authorize](https://docs.textcontrol.com/ds-server/reference/web-api/oauth/authorize/get/llms-full.txt), but without forwarding a failed request |

To check a new setup, request a token from the authentication server and call `GET /oauth/test` on the other instance with it:

```bash
curl --include 'https://ds1.example.com/oauth/test' \
    --header 'Authorization: Bearer <token issued by auth>'
```

`HTTP/1.1 200 OK` means that `ds1` accepts the token of `auth`. Without the registration, the same request gets `HTTP/1.1 401 Unauthorized` on an instance with OAuth enabled.

### Next Steps

- [Multiple Servers with Load Balancing](https://docs.textcontrol.com/ds-server/installation/load-balancing/llms-full.txt): run several instances behind a load balancer.
- [Creating Security Profiles](https://docs.textcontrol.com/ds-server/administration/security-profiles/llms-full.txt): create the profiles on the authentication server.
- [Enable OAuth](https://docs.textcontrol.com/ds-server/administration/oauth/llms-full.txt): switch on the token check on the instances that use the authentication server.

---

## Nearby

- **Previous:** [OAuth Grant Types](https://docs.textcontrol.com/ds-server/administration/oauth-grant-types/llms-full.txt)
- **Next:** [Client-Side Libraries](https://docs.textcontrol.com/ds-server/client-libraries/llms-full.txt)
