OAuth clients

OAuth clients provide a framework for delegated and scoped access to the Tailscale API. An OAuth client creates access tokens for scoped API access, instead of using a fully-permitted access token which expires after 90 days (or less if the fully-permitted access token has a shorter expiry). OAuth clients support the principle of least privilege by allowing fine-grained control on the access granted to the client using scopes, unlike a fully-permitted access token which grants full access to the Tailscale API.

How it works

You create an OAuth client that defines the scopes to allow when your client application uses the Tailscale API. An example scope is dns:read, which grants read-only access to a tailnet’s DNS settings. Another example is the devices scope, which grants access to read and write access to the list of devices on the tailnet, authorize or remove machines, create auth keys, and manipulate tags on devices.

Scopes are restricted to only the necessary Tailscale API endpoints needed for an operation. For example, tokens with the dns:read scope can access only the following Tailscale API endpoints:

  • /api/v2/tailnet/:tailnet/dns/*
  • /api/v2/tailnet/:tailnet/keys/:keyid (for itself only)

An OAuth client consists of a client ID and a client secret. When you create an OAuth client, Tailscale creates these for you. Within your client application, use the client ID and client secret to request an API access token from the Tailscale OAuth token endpoint. You use the access token to make calls to the Tailscale API. The access token grants permission only for the scopes that were defined when you created the OAuth client.

An API access token expires after one hour. For continuous access, shortly before an API access token expires, request a new API access token from the Tailscale OAuth token endpoint.

OAuth client libraries in popular programming languages can handle the API access token generation and renewal.

The Tailscale OAuth implementation is based on the OAuth 2.0 protocol.

Prerequisites

You need to be an Owner, Admin, Network admin, or IT admin of a tailnet in order to create, revoke, or delete OAuth clients.

Owners and Admins can create an OAuth client with any scope and any ACL tag that is in the tailnet. Other users can create an OAuth client with only the scopes and tags for which they have permissions. For example, a Network Admin cannot grant the devices scope, but an IT admin can.

Setting up an OAuth client

  1. Open the OAuth clients page of the admin console.

  2. In the Generate OAuth client page, select the set of operations that can be performed with tokens created by the new OAuth client. For a given operation, select Read or Write. For a description of the operations, see Scopes.

    The 'Generate OAuth client' page in the admin console
  3. Click Generate client.

  4. In the Generated new OAuth client page, you can see the new OAuth client’s ID and secret. Copy both the client ID and secret, as you need them for your client code. Note that after you close the Generated new OAuth client page, you won’t be able to copy the secret again.

    Store the client secret securely.
    The 'Generate OAuth client' page in the admin console
  5. Click Done.

    Your OAuth client is now configured. Use the client ID and secret when you configure your OAuth client application. Note that Tailscale-generated OAuth client secrets are case-sensitive.

If an OAuth client is created by a user who is later removed or suspended from your tailnet, the OAuth client will continue to function and generate API access tokens. Admins can see all configured OAuth clients in the OAuth clients page of the admin console.

Scopes

Scopes define which operations are permitted in API access tokens that are created by your client application.

You can use the following scopes in Tailscale OAuth clients:

Scope ID Description Allowed endpoints
all The client has complete access to the tailnet. This scope is not restricted to only access of APIs that existed at the time the client was initially authorized—the all scope also grants access to new APIs created in the future. The all and all:read scopes are the only scopes which can get a list of all access tokens which exist in the tailnet.
  • All endpoints, even endpoints which did not exist when the client was initially authorized.
  • /api/v2/tailnet/:tailnet/keys/:keyid (for any key, not just itself)
all:read The client has read-only access to the tailnet. This scope is not restricted to only read access of APIs that existed at the time the client was initially authorized—the all:read scope also grants read access to new APIs created in the future. The all and all:read scopes are the only scopes which can get a list of all access tokens which exist in the tailnet.
  • All endpoints, even endpoints which did not exist when the client was initially authorized.
  • /api/v2/tailnet/:tailnet/keys/:keyid (for any key, not just itself)
acl The client has access to read and modify the tailnet policy file.
  • /api/v2/tailnet/:tailnet/devices
  • /api/v2/tailnet/:tailnet/acl
  • /api/v2/tailnet/:tailnet/acl/preview
  • /api/v2/tailnet/:tailnet/acl/validate
  • /api/v2/tailnet/:tailnet/keys/:keyid (for itself only)
acl:read The client has access to read and validate the tailnet policy file.
  • /api/v2/tailnet/:tailnet/devices
  • /api/v2/tailnet/:tailnet/acl
  • /api/v2/tailnet/:tailnet/acl/preview
  • /api/v2/tailnet/:tailnet/acl/validate
  • /api/v2/tailnet/:tailnet/keys/:keyid (for itself only)
devices The client has access to read the list of devices on the tailnet, authorize or remove machines, create auth keys, and manipulate tags on devices.You must select one or more ACL tags when you create a client with the devices scope. Auth keys created with this client must have those exact tags, or tags owned by the client's tags.
  • /api/v2/tailnet/:tailnet/devices
  • /api/v2/device/:deviceid
  • /api/v2/device/:deviceID/authorized
  • /api/v2/device/:deviceID/key
  • /api/v2/device/:deviceID/tags
  • /api/v2/tailnet/:tailnet/keys (only auth keys, not access tokens)
  • /api/v2/tailnet/:tailnet/keys/* (only auth keys, not access tokens)
  • /api/v2/tailnet/:tailnet/keys/:keyid (for itself only)
devices:read The client has access to read the list of devices, auth keys, and tags on the tailnet.
  • /api/v2/tailnet/:tailnet/devices
  • /api/v2/device/:deviceid
  • /api/v2/device/:deviceID/key
  • /api/v2/device/:deviceID/tags
  • /api/v2/tailnet/:tailnet/keys (only auth keys, not access tokens)
  • /api/v2/tailnet/:tailnet/keys/* (only auth keys, not access tokens)
  • /api/v2/tailnet/:tailnet/keys/:keyid (for itself only)
dns The client has access to read and manipulate DNS settings.
  • /api/v2/tailnet/:tailnet/dns/*
  • /api/v2/tailnet/:tailnet/keys/:keyid (for itself only)
dns:read The client has access to read DNS settings.
  • /api/v2/tailnet/:tailnet/dns/*
  • /api/v2/tailnet/:tailnet/keys/:keyid (for itself only)
routes The client has access to read or modify subnet router settings, approve and revoke subnet routes, and approve and revoke exit nodes.
  • /api/v2/tailnet/:tailnet/devices
  • /api/v2/device/:deviceID/routes
  • /api/v2/tailnet/:tailnet/keys/:keyid (for itself only)
routes:read The client has access to read subnet router settings and exit node settings.
  • /api/v2/tailnet/:tailnet/devices
  • /api/v2/device/:deviceID/routes
  • /api/v2/tailnet/:tailnet/keys/:keyid (for itself only)
logs:read The client has access to read configuration audit logs.
  • /api/v2/tailnet/:tailnet/logs
  • /api/v2/tailnet/:tailnet/keys/:keyid (for itself only)
network-logs:read The client has access to read network flow logs.
  • /api/v2/tailnet/:tailnet/network-logs
  • /api/v2/tailnet/:tailnet/keys/:keyid (for itself only)

When new Tailscale functionality is provided, we will add it to existing scopes where applicable. That means a scope is not restricted to only access of APIs that existed at the time the client was initially authorized—a scope will contain additional access where it makes sense for new or updated functionality.

You can find documentation for the Tailscale API on GitHub →

Revoking an OAuth client

  1. Open the OAuth clients page of the admin console.

  2. Find the OAuth client that you want to delete and click on Revoke.

  3. Click Revoke OAuth client to confirm you want to revoke the OAuth client.

When you revoke an OAuth client, any active API access tokens that were created by the client are also revoked.

Tailscale OAuth token endpoint

The Tailscale OAuth token endpoint is https://api.tailscale.com/api/v2/oauth/token.

Make requests to the Tailscale OAuth token endpoint when you need an API access token. The Tailscale OAuth token endpoint accepts requests that conform to the OAuth 2.0 client credentials grant request format, and returns responses that conform to the OAuth 2.0 client credentials grant response format.

Requests to the OAuth token endpoint can include scope and tags request parameters, which specify space-delimited lists of requested scopes and ACL tags, respectively. The OAuth client must have permission to grant the requested scopes and tags. For example, an OAuth client with the all scope (which grants all other scopes as well as all device tags) could be used to request an access token with the devices scope and the tag:server tag. Tags are only relevant for tokens with the devices or all scope, and will be ignored if neither scope is present. The tags parameter is not part of the OAuth 2.0 specificaion, and may not be supported by all OAuth clients.

OAuth client libraries

Popular programming languages provide OAuth client libraries to simplify your use of OAuth clients. Here are several:

For example, the following Go code shows how to create an OAuth client object that uses your client ID and client secret to generate an API access token for calls to the Tailscale API:

package main

import (
        "context"
        "fmt"
        "io/ioutil"
        "log"
        "os"

        "golang.org/x/oauth2/clientcredentials"
)

func main() {
        var oauthConfig = &clientcredentials.Config{
                ClientID:     os.Getenv("OAUTH_CLIENT_ID"),
                ClientSecret: os.Getenv("OAUTH_CLIENT_SECRET"),
                TokenURL:     "https://api.tailscale.com/api/v2/oauth/token",
        }

        client := oauthConfig.Client(context.Background())
        // Replace example.com with your tailnet name.
        resp, err := client.Get("https://api.tailscale.com/api/v2/tailnet/example.com/devices")
        if err != nil {
                log.Fatalf("error getting keys: %v", err)
        }

        body, err := ioutil.ReadAll(resp.Body)
        if err != nil {
                log.Fatalf("error reading response body: %v", err)
        }

        fmt.Printf("response: %s", string(body))
}

The example requires that you define environment variables OAUTH_CLIENT_ID and OAUTH_CLIENT_SECRET, with their values set to the client ID and client secret that are created when you set up an OAuth client.

Verifying you can generate API access tokens

After you set up an OAuth client, an easy way to confirm that you can generate API access tokens is to make a curl request to the Tailscale OAuth token endpoint.

 curl -d "client_id=${OAUTH_CLIENT_ID}" -d "client_secret=${OAUTH_CLIENT_SECRET}" \
     "https://api.tailscale.com/api/v2/oauth/token"

The example requires that you define environment variables OAUTH_CLIENT_ID and OAUTH_CLIENT_SECRET, with their values set to your client ID and client secret.

Here’s an example response showing the API access token:

{"access_token":"tskey-0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ","token_type":"Bearer","expires_in":3600,"scope":"devices"}

Generating long-lived auth keys

You cannot generate long-lived auth keys, because they expire after 90 days, or, for one-off keys, once they are used.

Instead of a long-lived auth key, you can generate an OAuth client with the scope devices. Use the OAuth client to generate new auth keys as needed, by making a POST request to the /api/v2/tailnet/:tailnet/keys API method. When you create an OAuth client with the scope devices, you must select one or more ACL tags, which can be any tag or set of tags in your tailnet. Auth keys created with this client must have those exact tags, or tags owned by the client’s tags. Additionally, these tags need to be specified in the API call.

When you use an access token to make an API call, you can optionally use the value - as the tailnet name—you do not need to specify the tailnet name.

For example, this command:

curl -u $ACCESS_TOKEN: "https://api.tailscale.com/api/v2/tailnet/example.com/devices"

could be written as:

curl -u $ACCESS_TOKEN: "https://api.tailscale.com/api/v2/tailnet/-/devices"

as long as the access token is owned by the example.com tailnet.

You can also include the access token in the request header by using the -H option:

curl -H "Authorization: Bearer $ACCESS_TOKEN" "https://api.tailscale.com/api/v2/tailnet/-/devices"

Audit logging of API access token creation

In configuration audit logging, an action will be recorded in your audit log whenever an access token is created. The log entry will show the OAuth client ID as the actor, and the time when the API access token was created.

If you believe your OAuth client has been compromised, revoke the OAuth client (and set up a new client when you’re ready). Review the configuration audit logs to see whether and how the client and API access tokens were used during the period of compromise.

Registering new nodes using OAuth credentials

You can use an OAuth secret directly in tailscale up to register a new node:

tailscale up --auth-key=${OAUTH_CLIENT_SECRET} --advertise-tags=tag:ci

The OAuth client must have the devices scope and the ACL tags passed to --advertise-tags must match the tags you selected creating the client.

The --auth-key flag can accept additional URL-style parameters when used with OAuth secrets:

tailscale up --auth-key=${OAUTH_CLIENT_SECRET}?ephemeral=false&preauthorized=true --advertise-tags=tag:ci

The available parameters are:

  • ephemeral: register as an ephemeral node (defaults to true)
  • preauthorized: skip manual device approval (defaults to false)
  • baseURL: base URL for the Tailscale API (defaults to https://api.tailscale.com)

get-authkey utility

The get-authkey utility returns a new auth key to stdout, based on environment variables that contain values for your OAuth client ID and secret. Use get-authkey to generate auth keys for scripts or other automation.

All auth keys created from an OAuth client are required to have ACL tags. You can optionally pass in arguments to get-authkey to specify whether the key type is ephemeral, reusable, and/or pre-authorized.

get-authkey requires Go 1.21 or later.

To build and run get-authkey directly:

  1. Set the following environment variables:

    • TS_API_CLIENT_ID: The OAuth client ID for your tailnet.
    • TS_API_CLIENT_SECRET: The OAuth client secret for your tailnet.
    export TS_API_CLIENT_ID=<clientID> TS_API_CLIENT_SECRET=<secret>
    
  2. Run:

    go run tailscale.com/cmd/get-authkey@main -tags tag:development
    

You can pass in the following parameters:

  • -tags: Required. Apply the comma-separated list of tags to the auth key.
  • -reusable: Optional. Allocate a reusable auth key. If not set, defaults to false.
  • -ephemeral: Optional. Allocate an ephemeral auth key. If not set, defaults to false.
  • -preauth: Optional. Allocate the auth key as pre-authorized. If not set, defaults to true.

For example:

go run tailscale.com/cmd/get-authkey@main -reusable -tags tag:development

As an alternative, you can download the get-authkey source code and run it from a local repo:

  1. Clone the tailscale/tailscale repository.

  2. Open a command prompt and change directories to the root of the local tailscale/ folder.

  3. Run get-authkey:

    go run ./cmd/get-authkey/main.go -tags tag:development
    

Limitations

  • OAuth clients must be owned by the tailnet, and not by an individual user.
  • An OAuth access token expires after 1 hour—this time cannot be modified.