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:core
scope, which grants access to read and write the list of devices in the tailnet, authorize or remove machines, 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:
GET /api/v2/tailnet/:tailnet/dns/nameservers
GET /api/v2/tailnet/:tailnet/dns/preferences
GET /api/v2/tailnet/:tailnet/dns/searchpaths
GET /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 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:core
scope, but an IT admin can.
Setting up an OAuth client
-
Open the OAuth clients page of the admin console.
-
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.
-
Select Generate client.
-
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.
-
Select 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
Our OAuth scopes have changed to have more granular permissions associated with each scope. The previous set of OAuth scopes can be found in the legacy scopes section.
Scopes define which operations are permitted in API access tokens that are created by your client application.
In addition to the endpoints listed below, all scopes allow access to GET /api/v2/tailnet/:tailnet/keys/:keyID
for the key in use.
You can use the following scopes in Tailscale OAuth clients:
Scope ID | Description | Allowed endpoints |
---|---|---|
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 | 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. |
|
dns:read | The client has access to read DNS settings. |
|
dns | The client has access to read and manipulate DNS settings. |
|
policy_file:read | The client has access to read and validate the tailnet policy file.
|
|
policy_file | The client has access to read, validate, and modify the tailnet policy file.
|
|
users:read | The client has access to read user information. |
|
users | The client has access to read user information, modify user roles, and handle user approval, suspension, deletion, and restoration. |
|
devices:core:read | The client has access to read devices in the tailnet. |
|
devices:core | The client has access to read the list of devices in the tailnet, authorize or remove machines, and manipulate tags on
devices. You must select one or more tags when you create a client with the |
|
devices:posture_attributes:read | The client has access to read device posture attributes. |
|
devices:posture_attributes | The client has access to manipulate and read device posture attributes. |
|
devices:routes:read | The client has access to read subnet router settings and exit node settings. |
|
devices:routes | The client has access to read or modify subnet router settings, approve and revoke subnet routes, and approve and revoke exit nodes. |
|
devices_invites:read | The client has access to read device invites. |
|
devices_invites | The client has access to read or modify device invites. |
|
api_access_tokens:read | The client has access to read API access tokens. |
|
api_access_tokens | The client has access to read or modify API access tokens. |
|
auth_keys:read | The client has access to read auth keys. |
|
auth_keys | The client has access to read or modify auth keys. |
|
oauth_keys:read | The client has access to read OAuth clients and keys. |
|
oauth_keys | The client has access to read or modify OAuth clients and keys. |
|
webhooks:read | The client has access to read webhooks. |
|
webhooks | The client has access to read, modify, and test webhooks and to rotate webhook secrets. |
|
log_streaming:read | The client has access to read log streaming configurations and their status. |
|
log_streaming | The client has access to read or modify log streaming configurations and see their status.
|
|
logs:configuration:read | The client has access to read configuration audit logs. |
|
logs:network:read | The client has access to read network flow logs. |
|
logs:network | The client has access to read network flow logs and modify their settings. |
|
account_settings:read | The client has access to read Tailnet contacts. |
|
account_settings | The client has access to read or modify Tailnet contacts, and to resend verification emails. |
|
feature_settings:read | The client has access to read posture integrations and Tailnet feature settings. |
|
feature_settings | The client has access to read or modify posture integrations and Tailnet feature settings. |
|
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 our website
Revoking an OAuth client
-
Open the OAuth clients page of the admin console.
-
Find the OAuth client that you want to delete and select Revoke.
-
Select 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 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:core
scope and the tag:server
tag. Tags are only relevant for tokens with the devices:core
, auth_keys
, or all
scopes, and will be ignored if none of these scopes are 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:
- Go:
clientcredentials
Visit page → - Node:
OAuth2
Visit page → - PHP:
ClientCredentials.php
Visit page → - Ruby:
client_credentials.rb
Visit page → - Rust:
Client Credentials Grant
Visit page →
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 auth_keys
scope. 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 auth_keys
, you must select one or more tags, which can be any tag or set of tags in your tailnet. 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 auth_keys
scope and the 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 totrue
)preauthorized
: skip manual device approval (defaults tofalse
)baseURL
: base URL for the Tailscale API (defaults tohttps://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 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.23 or later.
To build and run get-authkey
directly:
-
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>
-
Run:
go run tailscale.com/cmd/get-authkey@latest -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@latest -reusable -tags tag:development
As an alternative, you can download the get-authkey
source code and run it from a local repo:
-
Clone the tailscale/tailscale repository.
-
Open a command prompt and change directories to the root of the local
tailscale/
folder. -
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.
Legacy scopes
As of November 14, 2024 new OAuth clients will use the scopes listed in the scopes section. Existing OAuth clients using the below scopes, and keys generated using these clients, are still valid.
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: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. |
|
acl | The client has access to read and modify the tailnet policy file. |
|
acl:read | The client has access to read and validate the tailnet policy file. |
|
devices | The client has access to read the list of devices in the tailnet, authorize or remove machines, create auth keys, and manipulate tags and attributes on
devices. You must select one or more tags when you create a client with the |
|
devices:read | The client has access to read the list of devices, auth keys, attributes, and tags in the tailnet. |
|
dns | The client has access to read and manipulate DNS settings. |
|
dns:read | The client has access to read DNS settings. |
|
routes | The client has access to read or modify subnet router settings, approve and revoke subnet routes, and approve and revoke exit nodes. |
|
routes:read | The client has access to read subnet router settings and exit node settings. |
|
logs:read | The client has access to read configuration audit logs. |
|
network-logs:read | The client has access to read network flow logs. |
|
Legacy scope equivalents
The following table maps legacy scope IDs to the newer equivalents.
Legacy Scope ID | New Scope ID |
---|---|
devices (for creating authentication keys) | auth_keys . |
devices (for device posture attributes) | devices:posture_attributes |
devices (for managing device lifecycle and properties) | devices:core |
routes | devices:routes |
acl | policy_file |
logs | logs:configuration |
network-logs | logs:network |