Manage Tokens

To make authentication more flexible, you can create and configure multiple authentication tokens. To that end, we provide a set of token management endpoints that are separate from the login and logout endpoints. The most notable differences are that the login endpoint needs authentication with the user credentials (email address and password) and its purpose is to return a token with a broad range of permissions, whereas the token management endpoints are authenticated using an already issued token, for the purpose of configuring more fine-grained token permissions.

When accessing the token management endpoints using a token without sufficient permission, the server will reply with 403 Forbidden.

Token Field Reference

A JSON object representing a token has the following structure:

{
    "created": "2018-09-06T09:08:43.762697Z",
    "id": "3a6b94b5-d20e-40bd-a7cc-521f5c79fab3",
    "last_used": null,
    "name": "my new token",
    "perm_manage_tokens": false,
    "allowed_subnets": [
        "0.0.0.0/0",
        "::/0"
    ],
    "max_age": "365 00:00:00",
    "max_unused_period": null,
    "token": "4pnk7u-NHvrEkFzrhFDRTjGFyX_S"
}

Field details:

allowed_subnets
Access mode:

read, write

Type:

Array of IPs or IP subnets

Exhaustive list of IP addresses or subnets clients must connect from in order to successfully authenticate with the token. Both IPv4 and IPv6 are supported. Defaults to 0.0.0.0/0, ::/0 (no restriction).

created
Access mode:

read-only

Type:

timestamp

Timestamp of token creation, in ISO 8601 format (e.g. 2018-09-06T09:08:43.762697Z).

id
Access mode:

read-only

Type:

UUID

Token ID, used for identification only (e.g. when deleting a token). This is not the token’s secret value.

is_valid
Access mode:

read-only

Type:

boolean

Indicates whether this token is valid. Currently, this reflects validity based on max_age and max_unused_period.

last_used
Access mode:

read-only

Type:

timestamp or null

Timestamp of when the token was last successfully authenticated, or null if the token has never been used.

In most cases, this corresponds to the last time when an API operation was performed using this token. However, if the operation was not executed because it was found that the token did not have sufficient permission, this field will still be updated.

max_age
Access mode:

read, write

Type:

string (time duration: [DD] [HH:[MM:]]ss[.uuuuuu]) or null

Maximum token age. If created + max_age is less than the current time, the token is invalidated. Invalidated tokens are not automatically deleted and can be resurrected by adjusting the expiration settings (using another valid token with sufficient privileges).

If null, the token is valid regardless of age (setting disabled).

max_unused_period
Access mode:

read, write

Type:

string (time duration: [DD] [HH:[MM:]]ss[.uuuuuu]) or null

Maximum allowed time period of disuse without invalidating the token. If max(created, last_used) + max_unused_period is less than the current time, the token is invalidated. Invalidated tokens are not automatically deleted and can be resurrected by adjusting the expiration settings (using another valid token with sufficient privileges).

If null, the token is valid regardless of prior usage (setting disabled).

name
Access mode:

read, write

Type:

string

Token name. It is meant for user reference only and carries no operational meaning. If omitted, the empty string is assumed. The maximum length is 178.

Certain API operations will automatically populate the name field with suitable values such as “login” or “dyndns”.

perm_manage_tokens
Access mode:

read, write

Type:

boolean

Permission to manage tokens (this one and also all others). A token which does not have this flag set cannot access the auth/tokens/ endpoints.

token
Access mode:

read-once

Type:

string

The token’s secret value that is used to authenticate API requests. It is only returned once, upon creation of the token. The secret value of an existing token cannot be recovered (we store it in irreversibly hashed form). For security details, see Security Considerations.

Creating a Token

To create a new token, issue a POST request to the tokens endpoint:

curl -X POST https://desec.io/api/v1/auth/tokens/ \
    --header "Authorization: Token mu4W4MHuSc0Hy-GD1h_dnKuZBond" \
    --header "Content-Type: application/json" --data @- <<< \
    '{"name": "my new token"}'

Note that the name and other fields are optional. The server will reply with 201 Created and the created token in the response body:

{
    "created": "2018-09-06T09:08:43.762697Z",
    "id": "3a6b94b5-d20e-40bd-a7cc-521f5c79fab3",
    "last_used": null,
    "name": "my new token",
    "perm_manage_tokens": false,
    "allowed_subnets": [
        "0.0.0.0/0",
        "::/0"
    ],
    "token": "4pnk7u-NHvrEkFzrhFDRTjGFyX_S"
}

The new token will, by default, possess fewer permissions than a login token. In particular, the perm_manage_tokens flag will not be set, so that the new token cannot be used to retrieve, modify, or delete any tokens (including itself).

With the default set of permissions, tokens qualify for carrying out all API operations related to DNS management (i.e. managing both domains and DNS records). Note that it is always possible to use the Log Out endpoint to delete a token.

If you require tokens with extra permissions, you can provide the desired configuration during creation:

  • allowed_subnets: In this field, you can list the IP addresses (or subnets) that clients must connect from in order to use the token. If not provided, access is not restricted based on the IP address. Both IPv4 and IPv6 are supported.

  • perm_manage_tokens: If set to true, the token can be used to authorize token management operations (as described in this chapter).

Additionally, you can configure an expiration policy with the following fields:

  • max_age: Force token expiration when a certain time period has passed since its creation. If null, the token does not expire due to age.

  • max_unused_period: Require that the token is used a least once within the given time period to prevent it from expiring. If null, the token does not expire due to it not being used.

If a field is provided but has invalid content, 400 Bad Request is returned, with error details in the body.

Modifying a Token

To modify a token, send a PATCH or PUT request to the auth/tokens/{id}/ endpoint of the token you would like to modify:

curl -X PATCH https://desec.io/api/v1/auth/tokens/{id}/ \
    --header "Authorization: Token mu4W4MHuSc0Hy-GD1h_dnKuZBond" \
    --header "Content-Type: application/json" --data @- <<< \
    '{"name": "my new token"}'

The ID given in the URL is the ID of the token that will be modified. Upon success, the server will reply with 200 OK.

The token given in the Authorization header requires the perm_manage_tokens permission. If permissions are insufficient, the server will return 403 Forbidden.

name and all other fields are optional. The list of fields that can be given is the same as when Creating a Token. If a field is provided but has invalid content, 400 Bad Request is returned, with error details in the body.

Note: As long as the perm_manage_tokens permission is in effect, it is possible for a token to grant and revoke its own permissions. However, if the perm_manage_tokens permission is removed, the operation can only be reversed by means of another token that has this permission.

Listing Tokens

To retrieve a list of all known tokens, issue a GET request as follows:

curl -X GET https://desec.io/api/v1/auth/tokens/ \
    --header "Authorization: Token mu4W4MHuSc0Hy-GD1h_dnKuZBond"

The server will respond with a list of token objects. Up to 500 items are returned at a time. If you have a larger number of tokens configured, the use of Pagination is required.

Retrieving a Specific Token

To retrieve information about a specific token, issue a GET request to the token’s endpoint:

curl -X GET https://desec.io/api/v1/auth/tokens/{id}/ \
    --header "Authorization: Token mu4W4MHuSc0Hy-GD1h_dnKuZBond"

The response will contain a token object as described under Token Field Reference. You can use it to check a token’s properties, such as name, timestamps of creation and last use, or permissions.

Note: The response does not contain the token’s secret value!

Deleting a Token

To delete an existing token by its ID via the token management endpoints, issue a DELETE request on the token’s endpoint, replacing {id} with the token id value:

curl -X DELETE https://desec.io/api/v1/auth/tokens/{id}/ \
    --header "Authorization: Token mu4W4MHuSc0Hy-GD1h_dnKuZBond"

The server will reply with 204 No Content, even if the token was not found.

If you do not have the token ID, but you do have the token secret, you can use the Log Out endpoint to delete it.

Token Scoping: Policies

Tokens by default can be used to authorize arbitrary actions within the user’s account, including DNS operations on any domain and some administrative tasks. As such, tokens are considered privileged when no further configuration is done. (This applies to v1 of the API and may change in a later version.)

Tokens can be restricted using Token Policies, which narrow down the scope of influence for a given API token. Using policies, the token’s power can be limited in two ways:

  1. the type of access control (allow-by-default or deny-by-default) for DNS write operations, such as dynDNS updates or general RRset management;

  2. explicit access control for specific RRsets through the policy’s domain, subname, and type fields.

All tokens can, regardless of their policy configuration, read any RRset (for all domains in the account). This is because essentially the same information is also available through the DNS. Note that the API in addition exposes some metadata, such as the RRset’s created or touched timestamps.

Write permissions can be configured on a per-RRset basis. When attempting to manipulate an RRset, the applicable policy is identified by matching the RRset against existing policies in the following order:

Priority

domain

subname

type

1

match

match

match

2

match

match

null

3

match

null

match

4

match

null

null

5

null

match

match

6

null

match

null

7

null

null

match

8

null

null

null

Taking the (domain, subname, type) tuple as a path, this can be considered a longest-prefix match algorithm. Wildcards are not expanded and match only RRsets with an identical wildcard subname.

RRsets for which no more specific policy is configured are eventually caught by the token’s default policy. It is therefore required to create such a default policy before any more specific policies can be created on a given token.

Tokens with at least one policy are considered restricted, with their scope limited to DNS record management. They can neither Retrieve Account Information nor perform Domain Management (such as domain creation or deletion).

Note: Token policies are independent of high-level token permissions that can be assigned when Creating a Token. In particular, a restricted token that at the same time has the perm_manage_tokens permission is able to free itself from its restrictions (see Token Field Reference).

Token Policy Field Reference

A JSON object representing a token policy has the following structure:

{
    "id": "7aed3f71-bc81-4f7e-90ae-8f0df0d1c211",
    "domain": "example.com",
    "subname": null,
    "type": null,
    "perm_write": true
}

Field details:

id
Access mode:

read-only

Type:

UUID

Token policy ID, used for identification only (e.g. when modifying a policy). (Not to be confused with the token’s ID.)

domain
Access mode:

read, write

Type:

string or null

Domain name to which the policy applies. null for the default policy.

subname
Access mode:

read, write

Type:

string or null

Subname to which the policy applies. null for the default policy.

type
Access mode:

read, write

Type:

string or null

Record type to which the policy applies. null for the default policy.

perm_write
Access mode:

read, write

Type:

boolean

Indicates write permission for the RRset specified by (domain, subname, type) when using the general RRset management or dynDNS interface. Defaults to false.

Token Policy Management

Token Policies are managed using the policies/rrsets/ endpoint under the token’s URL. Usage of this endpoint requires that the request’s authorization token has the perm_manage_tokens flag.

Semantics, input validation, and error handling follow the same style as the rest of the API, so is not documented in detail here. For example, to retrieve a list of policies for a given token, issue a GET request as follows:

curl -X GET https://desec.io/api/v1/auth/tokens/{id}/policies/rrsets/ \
    --header "Authorization: Token mu4W4MHuSc0Hy-GD1h_dnKuZBond"

The server will respond with a list of token policy objects.

To create the default policy, send a request like:

curl -X POST https://desec.io/api/v1/auth/tokens/{id}/policies/rrsets/ \
    --header "Authorization: Token mu4W4MHuSc0Hy-GD1h_dnKuZBond" \
    --header "Content-Type: application/json" --data @- <<< \
    '{"domain": null, "subname": null, "type": null}'

This will create a default policy. If the perm_write permission flag is not given, it is assumed to be false.

As an example, let’s create a policy that only allows manipulating all A records for a specific domain:

curl -X POST https://desec.io/api/v1/auth/tokens/{id}/policies/rrsets/ \
    --header "Authorization: Token mu4W4MHuSc0Hy-GD1h_dnKuZBond" \
    --header "Content-Type: application/json" --data @- <<< \
    '{"domain": "example.dedyn.io", "subname": null, "type": "A", "perm_write": true}'

Tip: To authorize dual-stack dynDNS updates, create two policies (for access to the A and AAAA RRsets, respectively).

You can retrieve (GET), update (PATCH, PUT), and remove (DELETE) policies by appending their id to the endpoint:

curl -X DELETE https://desec.io/api/v1/auth/tokens/{token.id}/policies/rrsets/{policy.id}/ \
    --header "Authorization: Token mu4W4MHuSc0Hy-GD1h_dnKuZBond"

When modifying or deleting policies, the API enforces the default policy’s primacy: You cannot create specific policies without first creating a default policy, and you cannot remove a default policy when other policies are still in place.

During deletion of tokens, users, or domains, policies are cleaned up automatically.

Security Considerations

This section is for purely informational. Token length and encoding may change in the future.

Any token secret is generated from 164 bits of randomness at the server and stored in hashed format (PBKDF2-HMAC-SHA256). Guessing the secret correctly or reversing the hash is considered practically impossible.

The token’s secret value is represented by 28 characters using a URL-safe base58 encoding. It is based on a case-sensitive alphanumeric alphabet excluding the characters lIO0 (hence comprising only the symbols a-k, m-z, A-H, J-N, P-Z, and 1-9). This encoding is optimized for maximum clarity and usability: Exclusion of certain letters minimizes visual ambiguity, while the restriction to alphanumeric symbols allows easy selection (double-click) and input, and helps avoid line breaks during display.

Before December 2022, tokens encoded a 21-byte secret using a URL-safe variant of base64 encoding, comprising of the 28 characters A-Z, a-z, 0-9, -, and _. (Base64 padding was not needed as the string length is a multiple of 4.)

Before September 2018, tokens encoded a 20-byte secret using 40 hexadecimal characters.

Legacy tokens are not issued anymore, but remain valid until invalidated by the user.