tailscale_rest/models/key.rs
1//! Auth keys, API access tokens, OAuth clients and federated identities.
2//!
3//! The control plane calls all four a "key" and tells them apart by
4//! [`Key::key_type`], so one model covers them and most fields apply to only
5//! some of the four. Which is which is in [`KEY_TYPES`].
6
7use std::collections::BTreeMap;
8
9use crate::Secret;
10use crate::model;
11use crate::models::KnownValues;
12
13/// The kinds of key the description knows.
14///
15/// `auth` registers machines, `client` is an OAuth client, `federated` is a
16/// workload identity, and `api` is an access token — a user's own, or one
17/// minted by either of the other two.
18pub const KEY_TYPES: &[&str] = &["auth", "client", "api", "federated"];
19
20/// What [`KEY_TYPES`] narrows to on the way in.
21///
22/// The description gives three different lists for one field: a response may
23/// say `api`, a create may not ask for one, and an update may not change a key
24/// into an `auth` key. A tool that quoted the response list on a create
25/// parameter would be offering values the control plane rejects.
26pub const CREATE_KEY_TYPES: &[&str] = &["auth", "client", "federated"];
27
28/// What an update accepts, which is narrower still.
29pub const UPDATE_KEY_TYPES: &[&str] = &["client", "federated"];
30
31pub const KNOWN_VALUES: &[KnownValues] = &[("Key.keyType", KEY_TYPES),
32 ("POST /tailnet/{tailnet}/keys body.keyType", CREATE_KEY_TYPES),
33 ("PUT /tailnet/{tailnet}/keys/{keyId} body.keyType", UPDATE_KEY_TYPES),
34];
35
36model! {
37 /// An auth key, an API access token, an OAuth client or a federated
38 /// identity.
39 Key {
40 id: "id" => String,
41 /// The secret itself, sent only in the answer that creates it. There
42 /// is no second chance to read it.
43 key: "key" => Secret,
44 /// One of [`KEY_TYPES`].
45 key_type: "keyType" => String,
46 /// Auth keys only.
47 expiry_seconds: "expirySeconds" => i64,
48 created: "created" => String,
49 updated: "updated" => String,
50 expires: "expires" => String,
51 revoked: "revoked" => String,
52 capabilities: "capabilities" => KeyCapabilities,
53 /// OAuth clients and federated identities: what the tokens they mint
54 /// are allowed to do.
55 scopes: "scopes" => Vec<String>,
56 tags: "tags" => Vec<String>,
57 description: "description" => String,
58 invalid: "invalid" => bool,
59 user_id: "userId" => String,
60 /// Federated identities: the audience the JWT must claim.
61 audience: "audience" => String,
62 /// Federated identities: the issuer whose JWTs are accepted.
63 issuer: "issuer" => String,
64 /// Federated identities: the subject the JWT must claim.
65 subject: "subject" => String,
66 /// Federated identities: claims mapped to values, for narrowing which
67 /// JWTs from the issuer are accepted.
68 custom_claim_rules: "customClaimRules" => BTreeMap<String, String>,
69 }
70
71 /// What a key may do, by resource.
72 KeyCapabilities {
73 /// Populated for auth keys only.
74 devices: "devices" => DeviceCapabilities,
75 }
76
77 /// A key's permissions over devices.
78 DeviceCapabilities as "KeyCapabilities.devices" {
79 create: "create" => CreateCapability,
80 }
81
82 /// What registering a device with this key produces.
83 CreateCapability as "KeyCapabilities.devices.create" {
84 /// A reusable key registers more than one device.
85 reusable: "reusable" => bool,
86 /// An ephemeral device is cleaned up when it goes away.
87 ephemeral: "ephemeral" => bool,
88 /// Devices registered with this key skip admin approval.
89 preauthorized: "preauthorized" => bool,
90 /// The tags every device registered with this key is given.
91 tags: "tags" => Vec<String>,
92 }
93
94 // -----------------------------------------------------------------------
95 // The shapes the routes carry, which the description spells out where they
96 // are used rather than naming in `components/schemas`.
97 // -----------------------------------------------------------------------
98
99 /// Every key the credential may see.
100 KeyList as "GET /tailnet/{tailnet}/keys 200" {
101 keys: "keys" => Vec<Key>,
102 }
103
104 /// What creating a key sends.
105 ///
106 /// Not [`Key`] with the read-only fields dropped: the two disagree about
107 /// `keyType`, which narrows to [`CREATE_KEY_TYPES`] here, and about
108 /// `capabilities`, which a create must send and a listing never returns.
109 CreateKeyRequest as "POST /tailnet/{tailnet}/keys body" {
110 /// One of [`CREATE_KEY_TYPES`]. An `api` token cannot be minted here.
111 key_type: "keyType" => String,
112 /// Up to 50 characters of letters, digits, hyphens and spaces.
113 description: "description" => String,
114 capabilities: "capabilities" => KeyCapabilities,
115 /// Auth keys only; how long the key stays usable.
116 expiry_seconds: "expirySeconds" => i64,
117 /// OAuth clients and federated identities: what their tokens may do.
118 scopes: "scopes" => Vec<String>,
119 /// Required when `scopes` includes `devices:core` or `auth_keys`.
120 tags: "tags" => Vec<String>,
121 /// Federated identities only, all four of these.
122 issuer: "issuer" => String,
123 subject: "subject" => String,
124 audience: "audience" => String,
125 custom_claim_rules: "customClaimRules" => BTreeMap<String, String>,
126 }
127
128 /// What reconfiguring an OAuth client or a federated identity sends.
129 ///
130 /// The same fields as a create minus the three an existing key cannot
131 /// change: its capabilities, its expiry, and being an auth key at all.
132 UpdateKeyRequest as "PUT /tailnet/{tailnet}/keys/{keyId} body" {
133 /// One of [`UPDATE_KEY_TYPES`]: an auth key or an API token cannot be
134 /// reconfigured here.
135 key_type: "keyType" => String,
136 description: "description" => String,
137 scopes: "scopes" => Vec<String>,
138 tags: "tags" => Vec<String>,
139 issuer: "issuer" => String,
140 subject: "subject" => String,
141 audience: "audience" => String,
142 custom_claim_rules: "customClaimRules" => BTreeMap<String, String>,
143 }
144}