mirror of
https://github.com/coder/coder.git
synced 2026-09-24 15:04:27 +08:00
feat(cli)!: expire tokens by default (#21783)
## Summary
> NOTE: Calling this out as a breaking change in case existing consumers
of the CLI depend on being able to see expired tokens OR being able to
delete tokens immediately.
Updates the `coder tokens rm` command to immediately expire a token by
ID, preserving the token record for audit trail purposes. Tokens can
still be deleted by passing `--delete`.
## Problem
During an incident on dev.coder.com, operators needed to urgently expire
an API key that was stuck in a hot loop. The only way to do this was via
direct database access:
```sql
UPDATE api_keys SET expires_at = NOW() WHERE id = '...';
```
This is not ideal for operators who may not have direct DB access or
want to avoid manual SQL.
## Solution
This PR adds:
- **API endpoint**: `PUT /api/v2/users/{user}/keys/{keyid}/expire` -
Sets the token's `expires_at` to now
- **SDK method**: `ExpireAPIKey(ctx, userID, keyID)`
- **Updates CLI**: `coder tokens rm <name|id|token>` now _expires_ by
default. You can still delete by passing the `--delete` flag. The `coder
tokens list` command now also hides expired tokens by default. You can
`--include-expired` if needed to include them.
- **Audit logging**: The expire action is logged with old and new key
states
## Test plan
- Tests cover: owner expiring own token, admin expiring other user's
token, non-admin cannot expire other's token, 404 for non-existent token
Closes #21782
🤖 Generated with [Claude Code](https://claude.com/claude-code)
---------
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
co-authored by
Claude Opus 4.5
parent
a5f3acac2f
commit
4a3304fc38
@@ -171,6 +171,20 @@ func (c *Client) DeleteAPIKey(ctx context.Context, userID string, id string) err
|
||||
return nil
|
||||
}
|
||||
|
||||
// ExpireAPIKey expires an API key by id, setting its expiry to now.
|
||||
// This preserves the API key record for audit purposes rather than deleting it.
|
||||
func (c *Client) ExpireAPIKey(ctx context.Context, userID string, id string) error {
|
||||
res, err := c.Request(ctx, http.MethodPut, fmt.Sprintf("/api/v2/users/%s/keys/%s/expire", userID, id), nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer res.Body.Close()
|
||||
if res.StatusCode > http.StatusNoContent {
|
||||
return ReadBodyAsError(res)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// GetTokenConfig returns deployment options related to token management
|
||||
func (c *Client) GetTokenConfig(ctx context.Context, userID string) (TokenConfig, error) {
|
||||
res, err := c.Request(ctx, http.MethodGet, fmt.Sprintf("/api/v2/users/%s/keys/tokens/tokenconfig", userID), nil)
|
||||
|
||||
Reference in New Issue
Block a user