DS Server

Authentication

Topic

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 shows. The profile holds the credentials that your application exchanges for an access token:

Part of the profileUsed for
Client ID and client secretRequesting an access token at POST /oauth/token
Allow "Client Credentials" grant typeLets a backend get a token with the client ID and secret alone; off by default
Authorized redirect URIsThe addresses DS Server may send an authorization code to, for the authorization code flow
Expiry dateOptional. 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 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 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:

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:

{
  "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:

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 explains:

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 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:

    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:

    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 lists them. The endpoints are described in GET /oauth/authorize and POST /oauth/token.

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:

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 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 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