# Authentication

> Request an OAuth access token at /oauth/token with the client credentials or authorization code grant, send it as a Bearer header and renew it in curl or .NET.

- **Product:** DS Server
- **Section:** Documentation
- **Kind:** Topic
- **Path:** DS Server > Document Processing > Authentication
- **2943 words**
- **Web URL:** https://docs.textcontrol.com/ds-server/document-processing/authentication/
- **LLMs URL:** https://docs.textcontrol.com/ds-server/document-processing/authentication/llms.txt
- **LLMs-Full URL:** https://docs.textcontrol.com/ds-server/document-processing/authentication/llms-full.txt

---

Once OAuth is enabled in the admin portal, every request to the DocumentProcessing Web API must carry an OAuth access token. A backend gets the token from DS Server itself with the client ID and client secret of a security profile, sends it in the `Authorization` header and requests a new one when it expires. The same steps apply to curl, to any HTTP client and to the .NET package `TXTextControl.DocumentServices.DocumentProcessing`, which runs them for you.

### How Authentication Works

DS Server contains its own OAuth 2.0 server. An admin creates a *security profile* for each application in the admin portal, as [Creating Security Profiles](https://docs.textcontrol.com/ds-server/administration/security-profiles/llms-full.txt) shows. The profile holds the credentials that your application exchanges for an access token:

| Part of the profile | Used for |
|---|---|
| Client ID and client secret | Requesting an access token at `POST /oauth/token` |
| **Allow "Client Credentials" grant type** | Lets a backend get a token with the client ID and secret alone; off by default |
| Authorized redirect URIs | The addresses DS Server may send an authorization code to, for the authorization code flow |
| Expiry date | Optional. Once it has passed, DS Server refuses every token of the profile |

Two grant types give your application a token:

- **Client credentials.** A backend, a script or a service sends the client ID and secret and receives an access token in one request. This is the flow for server-to-server calls, and the examples in this section use it. Enable **Allow "Client Credentials" grant type** in the profile first, as [OAuth Grant Types](https://docs.textcontrol.com/ds-server/administration/oauth-grant-types/llms-full.txt) shows.
- **Authorization code.** The client requests a code that DS Server sends to a registered redirect URI, and exchanges the code for an access token and a refresh token. It is meant for applications with a callback address, such as the ASP.NET Core client packages.

All endpoints sit below the *service URL* of your DS Server. It is `http://localhost:5000` when DS Server runs on its own on the default port, and it contains the path of the IIS site behind IIS, such as `https://yourdomain.com/DocumentServices`. [Architecture](https://docs.textcontrol.com/ds-server/overview/architecture/llms-full.txt) explains the parts behind the address. The samples below keep the service URL in the variable `DS`.

### Request an Access Token

Send a `POST` request to `/oauth/token` with the form field `grant_type=client_credentials`. The client ID and the client secret go into an HTTP Basic `Authorization` header, which curl builds from `--user`:

```bash
DS=http://localhost:5000

curl --request POST "$DS/oauth/token" \
  --user "<client ID>:<client secret>" \
  --data-urlencode grant_type=client_credentials
```

DS Server answers with a JSON object:

```json
{
  "access_token": "<access_token>",
  "token_type": "Bearer",
  "expires_in": "3600"
}
```

What the request and the response contain:

- **The Basic header is required.** The client ID and secret as the form fields `client_id` and `client_secret` alone are refused with `400` and `"error_description": "Missing authorization header."`. A request with both the header and the fields works.
- **The body is form data**, `application/x-www-form-urlencoded`, as `--data-urlencode` sends it. A JSON body is refused with `405 Method Not Allowed`.
- **`expires_in`** is the lifetime of the token in seconds, 3,600 seconds or one hour. DS Server returns it as a string.
- **`access_token`** is the value you send with every request to the Web API.

To keep the token in a shell variable for the next requests, extract it with the JSON processor `jq`:

```bash
TOKEN=$(curl --silent --request POST "$DS/oauth/token" \
  --user "<client ID>:<client secret>" \
  --data-urlencode grant_type=client_credentials | jq -r .access_token)
```

### Send the Token With a Request

Pass the token in a `Bearer` authorization header. The request below converts *document.docx* to PDF, as [Converting Documents](https://docs.textcontrol.com/ds-server/document-processing/converting/llms-full.txt) explains:

```bash
base64 -w 0 document.docx | jq -Rs . > document.json

curl --request POST "$DS/documentprocessing/document/convert?returnFormat=PDF" \
  --header "Authorization: Bearer $TOKEN" \
  --header "Content-Type: application/json" \
  --data-binary @document.json
```

DS Server also accepts the token in the `access_token` query parameter, for example `?returnFormat=PDF&access_token=<access_token>`. Prefer the header: a query string ends up in server logs and browser histories.

A request without a token is answered with `401 Unauthorized` and the header `WWW-Authenticate: Bearer`. An invalid token, or a token of a security profile whose expiry date has passed, also gets `401`, with an empty body. [Errors and Status Codes](https://docs.textcontrol.com/ds-server/document-processing/errors/llms-full.txt) lists the other answers.

### Renew the Token

A token is valid for the `expires_in` seconds of its response. Tokens outlive a restart of DS Server, but not the expiry date of their security profile: once that date has passed, every request with a token of the profile gets `401`, although `/oauth/token` still issues new tokens.

- **Client credentials.** Request a new token with the same call before the old one expires, or when a request answers `401`. This grant type has no refresh token.
- **Authorization code.** The token response also contains a `refresh_token`. Exchange it for a new access token and a new refresh token with `grant_type=refresh_token`:

    ```bash
    curl --request POST "$DS/oauth/token" \
      --data-urlencode grant_type=refresh_token \
      --data-urlencode "refresh_token=<refresh_token>" \
      --data-urlencode "client_id=<client ID>" \
      --data-urlencode "client_secret=<client secret>"
    ```

### Use the Authorization Code Flow

For an application that has a callback address, the authorization code flow keeps the client secret out of the first request:

1. Your application redirects to, or calls, `GET /oauth/authorize` with `response_type=code`, its `client_id`, a `redirect_uri` registered in the security profile and a `state` value.
2. DS Server redirects to the redirect URI with the parameters `code` and `state`. It shows no sign-in or consent page of its own. A redirect URI that is not registered in the profile is refused with `400` and the text "Missing, invalid or mismatching redirection URI.".
3. Your application sends the code to `/oauth/token` as form fields:

    ```bash
    curl --request POST "$DS/oauth/token" \
      --data-urlencode grant_type=authorization_code \
      --data-urlencode "code=<code>" \
      --data-urlencode "client_id=<client ID>" \
      --data-urlencode "client_secret=<client secret>" \
      --data-urlencode "redirect_uri=https://your.app/callback"
    ```
4. The response holds `access_token`, `token_type`, `expires_in` and `refresh_token`.

The ASP.NET Core packages of the Document Editor and the Document Viewer run this flow and provide the callback endpoints themselves, as [ASP.NET Core](https://docs.textcontrol.com/ds-server/client-libraries/aspnet-core/#authorization) lists them. The endpoints are described in [GET /oauth/authorize](https://docs.textcontrol.com/ds-server/reference/web-api/oauth/authorize/get/llms-full.txt) and [POST /oauth/token](https://docs.textcontrol.com/ds-server/reference/web-api/oauth/token/post/llms-full.txt).

### Authenticate With the .NET Package

`TXTextControl.DocumentServices.DocumentProcessing` requests the token for you. Pass the credentials in an `OAuthSettings` object when you create the `DocumentProcessing` instance:

```csharp
using TXTextControl.DocumentServices.DocumentProcessing;
using TXTextControl.DocumentServices.DocumentProcessing.OAuth;

// Client ID and secret of a profile that allows the client credentials grant type
var oauthSettings = new OAuthSettings("<client ID>", "<client secret>");

// The constructor requests the access token from DS Server
var documentProcessing = new DocumentProcessing("http://localhost:5000", oauthSettings);

byte[] pdf = await documentProcessing.Convert(
    await File.ReadAllBytesAsync("document.docx"), ReturnFormat.PDF);
```

The code works as follows:

- `OAuthSettings` with a client ID and a secret uses the client credentials grant type. A third argument, the redirect URI, is for the authorization code flow in an ASP.NET Core application; the package provides the callback at `/textcontrol/documentprocessing/auth/callback` of your application.
- The `DocumentProcessing` constructor requests the access token right away. Wrong credentials throw an `AggregateException` there, with the OAuth error in its message, such as `"invalid_client"` and `"Invalid client secret."`.
- The class also has the static method `CreateInstanceAsync`. In version 5.0.0 of the package, it requests an access token but doesn't send it with the requests that follow, so they fail with `401` when OAuth is enabled. The samples on these pages use the constructor.
- Every method, such as `Convert`, sends the token with its request. A refused request throws a `ServiceRequestException`, whose `StatusCode` holds the HTTP status.
- `RefreshAccessToken` requests a new token with the refresh token of the authorization code flow. With client credentials, it throws an exception that says the grant type does not support refresh tokens; create a new instance to get a new token.

The classes are listed in the [DocumentProcessing namespace](https://docs.textcontrol.com/ds-server/reference/dotnet-api/txtextcontrol-documentservices-documentprocessing/llms-full.txt) of the .NET API.

### When OAuth Is Disabled

The admin can switch OAuth off on the **Security Profiles** page of the admin portal, as [Enable OAuth](https://docs.textcontrol.com/ds-server/administration/oauth/llms-full.txt) describes. A fresh installation starts with OAuth disabled. DS Server then checks no token at all: requests without a token, with a valid token or with an invalid one reach the endpoint alike.

Without OAuth, leave out the `Authorization` header, and create the .NET instance with the service URL only, `new DocumentProcessing("http://localhost:5000")`. Run DS Server this way only where the network or another application protects it, and enable OAuth before the server can be reached from outside.

### Next Steps

- [Converting Documents](https://docs.textcontrol.com/ds-server/document-processing/converting/llms-full.txt): the first request with a token, a document conversion.
- [Creating Security Profiles](https://docs.textcontrol.com/ds-server/administration/security-profiles/llms-full.txt): create the client ID and secret.
- [OAuth Grant Types](https://docs.textcontrol.com/ds-server/administration/oauth-grant-types/llms-full.txt): allow the client credentials grant type for a profile.
- [Errors and Status Codes](https://docs.textcontrol.com/ds-server/document-processing/errors/llms-full.txt): what a `401` or `400` answer means.

---

## Nearby

- **Previous:** [Document Processing](https://docs.textcontrol.com/ds-server/document-processing/llms-full.txt)
- **Next:** [Converting Documents](https://docs.textcontrol.com/ds-server/document-processing/converting/llms-full.txt)
