Skip to content

mcoulombe/tailscale-authz-code-flow-tester

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

3 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Hydralisk

A small utility for testing the OAuth authorization code flow to programmatically provision Tailscale nodes on a tailnet using tsnet.

Hydralisk runs a local web server that walks through the full OAuth consent flow, exchanges the authorization code for an access token, and uses it as an auth key to spin up a tsnet node that joins the tailnet.

Hydralisk UI

Getting started

1. Enable feature flags on your tailnet

Your tailnet must have the following feature flags enabled:

  • stateless-access-token
  • oauth-authorization-flow

Contact your Tailscale account team or enable them via the admin console if available.

2. Create an OAuth app

OAuth apps can currently only be created via the API. You must be a tailnet admin.

curl -X POST https://login.tailscale.com/api/v2/tailnet/-/oauth-apps \
  --header 'Authorization: Bearer <api-access-token>' \
  -H "Content-Type: application/json" \
  -d '{
    "name": "your-oauth-app-name",
    "redirectUris": ["http://localhost:31545/callback"],
    "scopes": ["auth_keys:create", "auth_keys:create:once"]
  }'

The response contains the clientSecret you'll need in the next step:

{
  "id": "your-app-id",
  "name": "your-oauth-app-name",
  "clientSecret": "tskey-app-<secret>",
  "redirectURIs": ["http://localhost:31545/callback"],
  "scopes": ["auth_keys:create", "auth_keys:create:once"],
  ...
}

If using a custom --listen address, make sure the redirectUris in the OAuth app match your --redirect-uri.

3. Build and start Hydralisk

make dev
make run ARGS="--secret tskey-app-<secret>"

Or using an environment variable:

export HYDRALISK_SECRET="tskey-app-<secret>"
make run

Hydralisk starts on http://localhost:31545 by default.

4. Provision nodes

  1. Open http://localhost:31545 in your browser.
  2. Enter a user login (e.g. alice@example.com) in the text field.
  3. Click Provision (single use) or Provision (new consent) to start the OAuth consent flow.
  4. A new tab opens where you log in and approve the authorization request.
  5. After approval, you're redirected back to Hydralisk. A new tsnet node (hydralisk-N) begins connecting to your tailnet.
  6. The dashboard updates in real time as the node connects. Once ready, the node appears in your tailnet's machine list.

5. Reuse tokens for subsequent provisions

After the first consent flow, the user's tokens appear on the dashboard. You can:

  • Click Reuse access token to provision another node without any consent flow.
  • Click Refresh token to obtain a new access token using the stored refresh token (the refresh token is rotated on each use).

6. Clean up

Nodes removed from the tailnet (via the admin console or API) are automatically detected and stopped by Hydralisk.

Flags and environment variables

All flags can also be set via environment variables. Flags take precedence over env vars.

Flag Env var Default Description
--secret HYDRALISK_SECRET (required) OAuth app secret (tskey-app-<clientID>-<secret>). The client ID is extracted automatically.
--listen HYDRALISK_LISTEN localhost:31545 Address the web UI listens on
--control-url HYDRALISK_CONTROL_URL https://login.tailscale.com Tailscale control server URL
--redirect-uri HYDRALISK_REDIRECT_URI http://<listen>/callback OAuth redirect URI

Provisioning modes

Access and refresh tokens are stored per-user. Multiple users can authorize the app, and each user's tokens are tracked independently. Refresh tokens are rotated on every use -- the old token is invalidated and replaced with the new one returned by the token endpoint.

The web UI exposes four provisioning strategies:

Mode Scope Behavior
Single use auth_keys:create:once Runs the full consent flow every time. The token is single-use.
Reuse access token Reuses the stored access token for a specific user. No server round-trip.
Refresh token Uses a specific user's refresh token to obtain a new access token. The refresh token is rotated.
New consent auth_keys:create Runs the full consent flow, stores tokens for the user for future reuse.

The Single use and New consent modes require entering a user login before starting the consent flow. The Reuse access token and Refresh token buttons appear per-user once tokens have been stored.

How it works

  1. The user enters their login and clicks a consent-flow button in the web UI.
  2. The browser opens the OAuth authorization URL on the control server.
  3. After the user approves, the control server redirects back to /callback with an authorization code.
  4. Hydralisk exchanges the code for an access/refresh token pair and stores them for the user.
  5. A new tsnet node (hydralisk-<N>) starts in the background using the access token as its auth key.
  6. The node joins the tailnet and begins listening on port 80 with a basic HTTP server.
  7. If the node is removed from the tailnet, Hydralisk detects this and stops the node automatically.

For returning users, the Reuse access token and Refresh token buttons skip the consent flow entirely.

The web UI polls /status to show real-time progress as each node connects.

Endpoints

Path Description
/ Web UI dashboard
/authorize?mode=<mode>&login=<user> Starts a consent flow (once, new) for the given user login
/authorize?mode=<mode>&user=<user> Reuses tokens (access, refresh) for the given user
/callback OAuth redirect handler
/status JSON status of all provisioned nodes
/purge?user=<user>&token=<access|refresh> Clears a user's tokens (omit token to remove user, omit both to purge all)

About

Small utility app to tests the authorization code flow to provision Tailscale nodes.

Resources

License

Stars

Watchers

Forks

Releases

No releases published

Packages