cmd/tailscale/cli/up.go: add support for OIDC JWT authkeys

Updates #16374

Signed-off-by: Sam Linville <sam@tailscale.com>
This commit is contained in:
Sam Linville 2025-08-02 08:42:21 -04:00
parent 0f15e44196
commit b21873f094

View File

@ -1100,21 +1100,32 @@ func exitNodeIP(p *ipn.Prefs, st *ipnstate.Status) (ip netip.Addr) {
}
// resolveAuthKey either returns v unchanged (in the common case) or, if it
// starts with "tskey-client-" (as Tailscale OAuth secrets do) parses it like
// starts with "tskey-client-" (as Tailscale OAuth secrets do) or jwt:
// (a prefix to indicate an OIDC JWT), parses it like
//
// tskey-client-xxxx[?ephemeral=false&bar&preauthorized=BOOL&baseURL=...]
// or
// jwt:eyJ...[?ephemeral=false&preauthorized=BOOL&baseURL=...]
//
// and does the OAuth2 dance to get and return an authkey. The "ephemeral"
// and does the OAuth or OIDC dance to get and return an authkey. The "ephemeral"
// property defaults to true if unspecified. The "preauthorized" defaults to
// false. The "baseURL" defaults to https://api.tailscale.com.
// The passed in tags are required, and must be non-empty. These will be
// set on the authkey generated by the OAuth2 dance.
// set on the authkey generated by the OAuth2/OIDC dance.
func resolveAuthKey(ctx context.Context, v, tags string) (string, error) {
if !strings.HasPrefix(v, "tskey-client-") {
var authType string
if strings.HasPrefix(v, "tskey-client-") {
authType = "OAuth"
} else if strings.HasPrefix(v, "jwt:") {
authType = "OIDC JWT"
v = strings.TrimPrefix(v, "jwt:")
} else {
// Return unchanged for all other key types (including tskey-auth-*)
return v, nil
}
if tags == "" {
return "", errors.New("oauth authkeys require --advertise-tags")
return "", fmt.Errorf("%s authkeys require --advertise-tags", authType)
}
clientSecret, named, _ := strings.Cut(v, "?")