feat: add multi-scope support to API keys (#19917)

# Canonicalize API Key Scopes

This PR introduces canonical API key scopes with a `coder:` namespace prefix to avoid collisions with low-level resource:action names. It:

1. Renames special API key scopes in the database:
   - `all` → `coder:all`
   - `application_connect` → `coder:application_connect`

2. Adds support for a new `scopes` field in the API key creation request, allowing multiple scopes to be specified while maintaining backward compatibility with the singular `scope` field.

3. Updates the API documentation to reflect these changes, including the new endpoint for listing public API key scopes.

4. Ensures backward compatibility by mapping between legacy and canonical scope names in relevant code paths.
This commit is contained in:
Thomas Kosiewski
2025-09-26 11:56:34 +02:00
committed by GitHub
parent 4bda39585d
commit d0db9ec88f
35 changed files with 332 additions and 113 deletions
+17 -5
View File
@@ -1570,10 +1570,21 @@ func userOrganizationIDs(ctx context.Context, api *API, user database.User) ([]u
}
func convertAPIKey(k database.APIKey) codersdk.APIKey {
// Derive a single scope from arrays for response compatibility.
scope := database.APIKeyScopeAll
if k.Scopes.Has(database.APIKeyScopeApplicationConnect) {
scope = database.APIKeyScopeApplicationConnect
// Derive a single legacy scope name for response compatibility.
// Historically, the API exposed only two scope strings: "all" and
// "application_connect". Continue to return those for clients even
// though the database stores canonical values (e.g. "coder:all")
// and may include low-level scopes.
var legacyScope codersdk.APIKeyScope
if k.Scopes.Has(database.ApiKeyScopeCoderApplicationConnect) {
legacyScope = codersdk.APIKeyScopeApplicationConnect
} else if k.Scopes.Has(database.ApiKeyScopeCoderAll) {
legacyScope = codersdk.APIKeyScopeAll
}
scopes := make([]codersdk.APIKeyScope, 0, len(k.Scopes))
for _, s := range k.Scopes {
scopes = append(scopes, codersdk.APIKeyScope(s))
}
return codersdk.APIKey{
@@ -1584,7 +1595,8 @@ func convertAPIKey(k database.APIKey) codersdk.APIKey {
CreatedAt: k.CreatedAt,
UpdatedAt: k.UpdatedAt,
LoginType: codersdk.LoginType(k.LoginType),
Scope: codersdk.APIKeyScope(scope),
Scope: legacyScope,
Scopes: scopes,
LifetimeSeconds: k.LifetimeSeconds,
TokenName: k.TokenName,
}