feat(api): add admin routes to manage user access tokens (#12323)
# Feature Request: Admin API route to manage access tokens for any user
## Problem
The existing API route to create access tokens (POST /api/v1/users/{username}/tokens) requires Basic authentication (username + password) via the reqBasicOrRevProxyAuth() middleware. This is by design: a token should not be created from another token.
However, this creates a blocker for environments where Basic authentication is disabled (ENABLE_BASIC_AUTHENTICATION = false), typically when authentication is delegated to an external SSO provider (e.g., OpenID Connect).
In such setups, bot/service accounts are provisioned by an external system that needs to:
Create a user via POST /api/v1/admin/users (works fine with an admin token)
Create an access token for that user (currently impossible without Basic auth or direct CLI/DB access)
The only workaround today is to SSH into the Forgejo server and run:
This is not suitable when the provisioning system has no direct access to the Forgejo host.
## Proposed solution
Add new admin-only API routes under the existing /api/v1/admin/users/{username} group to manage access tokens:
| Method | Route | Description |
|:-------- |:--------:| --------:|
| GET | /api/v1/admin/users/{username}/tokens | List access tokens for a user|
|POST | /api/v1/admin/users/{username}/tokens | Create an access token for a user|
|DELETE | /api/v1/admin/users/{username}/tokens/{id} | Delete an access token for a user|
These routes would:
Require a site admin token (reqToken() + reqSiteAdmin()) — no Basic auth needed
Use the AccessTokenScopeCategoryAdmin token scope
Reuse the existing handler logic from user.CreateAccessToken / user.ListAccessTokens / user.DeleteAccessToken
Accept the same request/response payloads as the existing user-facing routes
### Why this belongs in the admin API
It follows the existing pattern: admins can already create users, repos, orgs, SSH keys, and emails for any user via the admin API
It does not weaken security: only site administrators can call it, and it requires a valid admin-scoped token
It fills a gap: the admin CLI command forgejo admin user generate-access-token already provides this capability, but only locally
<!--start release-notes-assistant-->
## Release notes
<!--URL:https://codeberg.org/forgejo/forgejo-->
- Features
- [PR](https://codeberg.org/forgejo/forgejo/pulls/12323): <!--number 12323 --><!--line 0 --><!--description ZmVhdChhcGkpOiBhZGQgYWRtaW4gcm91dGVzIHRvIG1hbmFnZSB1c2VyIGFjY2VzcyB0b2tlbnM=-->feat(api): add admin routes to manage user access tokens<!--description-->
<!--end release-notes-assistant-->
Reviewed-on: https://codeberg.org/forgejo/forgejo/pulls/12323
Reviewed-by: Mathieu Fenniak <mfenniak@noreply.codeberg.org>
This commit is contained in:
committed by
Mathieu Fenniak
parent
03312e4f46
commit
ba1c3e0288
Generated
+129
@@ -2043,6 +2043,135 @@
|
||||
}
|
||||
}
|
||||
},
|
||||
"/admin/users/{username}/tokens": {
|
||||
"get": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"admin"
|
||||
],
|
||||
"summary": "List the specified user's access tokens",
|
||||
"operationId": "adminListUserAccessTokens",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "username of user",
|
||||
"name": "username",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "page number of results to return (1-based)",
|
||||
"name": "page",
|
||||
"in": "query"
|
||||
},
|
||||
{
|
||||
"type": "integer",
|
||||
"description": "page size of results",
|
||||
"name": "limit",
|
||||
"in": "query"
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"200": {
|
||||
"$ref": "#/responses/AccessTokenList"
|
||||
},
|
||||
"403": {
|
||||
"$ref": "#/responses/forbidden"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/responses/notFound"
|
||||
}
|
||||
}
|
||||
},
|
||||
"post": {
|
||||
"consumes": [
|
||||
"application/json"
|
||||
],
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"admin"
|
||||
],
|
||||
"summary": "Create an access token for the specified user",
|
||||
"operationId": "adminCreateUserAccessToken",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "username of user",
|
||||
"name": "username",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"name": "body",
|
||||
"in": "body",
|
||||
"schema": {
|
||||
"$ref": "#/definitions/CreateAccessTokenOption"
|
||||
}
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"201": {
|
||||
"$ref": "#/responses/AccessToken"
|
||||
},
|
||||
"400": {
|
||||
"$ref": "#/responses/error"
|
||||
},
|
||||
"403": {
|
||||
"$ref": "#/responses/forbidden"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/responses/notFound"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/admin/users/{username}/tokens/{token}": {
|
||||
"delete": {
|
||||
"produces": [
|
||||
"application/json"
|
||||
],
|
||||
"tags": [
|
||||
"admin"
|
||||
],
|
||||
"summary": "Delete an access token for the specified user",
|
||||
"operationId": "adminDeleteUserAccessToken",
|
||||
"parameters": [
|
||||
{
|
||||
"type": "string",
|
||||
"description": "username of user",
|
||||
"name": "username",
|
||||
"in": "path",
|
||||
"required": true
|
||||
},
|
||||
{
|
||||
"type": "string",
|
||||
"description": "token to be deleted, identified by ID and if not available by name",
|
||||
"name": "token",
|
||||
"in": "path",
|
||||
"required": true
|
||||
}
|
||||
],
|
||||
"responses": {
|
||||
"204": {
|
||||
"$ref": "#/responses/empty"
|
||||
},
|
||||
"403": {
|
||||
"$ref": "#/responses/forbidden"
|
||||
},
|
||||
"404": {
|
||||
"$ref": "#/responses/notFound"
|
||||
},
|
||||
"422": {
|
||||
"$ref": "#/responses/error"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"/gitignore/templates": {
|
||||
"get": {
|
||||
"produces": [
|
||||
|
||||
Reference in New Issue
Block a user