You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
test(coderd/oauth2provider): cover the negotiated scope end to end
Assert the issued access token against the live API rather than only
against the api_keys row: coder:workspaces.access reads a template and is
refused the deletion. Drive every name the scope catalog offers through
the conversion so a name added to the catalog but not the api_key_scope
enum fails here instead of leaving a client holding an unredeemable code.
Refresh a token row seeded the way migration 000569 leaves a pre-existing
grant to confirm the backfilled coder:all still means unrestricted.
Document the scope parameter, the registration-time allowlist, and the
two limitations that remain: only Dynamic Client Registration can declare
an allowlist, and a scope parameter on refresh is ignored.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Copy file name to clipboardExpand all lines: docs/admin/integrations/oauth2-provider.md
+24-1Lines changed: 24 additions & 1 deletion
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -220,6 +220,28 @@ confidential clients must include PKCE parameters:
220
220
"$CODER_URL/oauth2/tokens"
221
221
```
222
222
223
+
## Scopes
224
+
225
+
An access token is bounded by the scope negotiated when the user authorized it, on top of that user's own permissions. A token can never do more than its user can.
226
+
227
+
Scope names come from the same vocabulary as [API key scopes](../users/sessions-tokens.md#api-key-scopes): individual `resource:action` names such as `workspace:ssh`, and `coder:` composites such as `coder:workspaces.access` that stand for a set of them. `coder:all` records an unrestricted grant.
228
+
229
+
A client asks for a scope with the `scope` parameter on the authorization request, space separated:
230
+
231
+
```txt
232
+
https://coder.example.com/oauth2/authorize?
233
+
client_id=your-client-id&
234
+
response_type=code&
235
+
scope=coder:workspaces.access&
236
+
code_challenge=$CODE_CHALLENGE&
237
+
code_challenge_method=S256&
238
+
redirect_uri=https://yourapp.example.com/callback
239
+
```
240
+
241
+
An application registered through [Dynamic Client Registration](#dynamic-client-registration) can declare a `scope` field, which acts as an allowlist. The client may then request anything that allowlist covers, and is granted the whole allowlist if it requests nothing. Applications created through the web UI or the management API declare no allowlist, so any requested scope is honored and a request that names no scope is granted `coder:all`.
242
+
243
+
The consent page states the scope being granted before the user approves it, and refreshing a token keeps the scope originally granted.
244
+
223
245
## Discovery Endpoints
224
246
225
247
Coder provides OAuth2 discovery endpoints for programmatic integration:
As an experimental feature, the current implementation has limitations:
369
391
370
-
- No scope system - all tokens have full API access
392
+
- A scope allowlist can only be declared at [Dynamic Client Registration](#dynamic-client-registration); applications created through the web UI or the management API cannot restrict which scopes a client may request
393
+
- A `scope` parameter on a refresh request is ignored, and the refreshed token keeps the scope originally granted
371
394
- No client credentials grant support
372
395
- Implicit grant (`response_type=token`) is not supported; OAuth 2.1
373
396
deprecated this flow due to token leakage risks, and requests return
0 commit comments