jojo/routers/api/v1/admin/user_token.go
steven.guiheux ba1c3e0288 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>
2026-05-11 16:55:22 +02:00

107 lines
2.9 KiB
Go

// Copyright 2026 The Forgejo Authors. All rights reserved.
// SPDX-License-Identifier: MIT
package admin
import (
"forgejo.org/routers/api/v1/utils"
"forgejo.org/services/context"
)
// ListUserAccessTokens lists all access tokens for a given user.
// This endpoint is admin-only and does not require Basic auth.
func ListUserAccessTokens(ctx *context.APIContext) {
// swagger:operation GET /admin/users/{username}/tokens admin adminListUserAccessTokens
// ---
// summary: List the specified user's access tokens
// produces:
// - application/json
// parameters:
// - name: username
// in: path
// description: username of user
// type: string
// required: true
// - name: page
// in: query
// description: page number of results to return (1-based)
// type: integer
// - name: limit
// in: query
// description: page size of results
// type: integer
// responses:
// "200":
// "$ref": "#/responses/AccessTokenList"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
utils.ListAccessTokens(ctx)
}
// CreateUserAccessToken creates a new access token for a given user.
// This endpoint is admin-only and does not require Basic auth.
func CreateUserAccessToken(ctx *context.APIContext) {
// swagger:operation POST /admin/users/{username}/tokens admin adminCreateUserAccessToken
// ---
// summary: Create an access token for the specified user
// consumes:
// - application/json
// produces:
// - application/json
// parameters:
// - name: username
// in: path
// description: username of user
// required: true
// type: string
// - 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"
utils.CreateAccessToken(ctx)
}
// DeleteUserAccessToken deletes an access token for a given user.
// This endpoint is admin-only and does not require Basic auth.
func DeleteUserAccessToken(ctx *context.APIContext) {
// swagger:operation DELETE /admin/users/{username}/tokens/{token} admin adminDeleteUserAccessToken
// ---
// summary: Delete an access token for the specified user
// produces:
// - application/json
// parameters:
// - name: username
// in: path
// description: username of user
// type: string
// required: true
// - name: token
// in: path
// description: token to be deleted, identified by ID and if not available by name
// type: string
// required: true
// responses:
// "204":
// "$ref": "#/responses/empty"
// "403":
// "$ref": "#/responses/forbidden"
// "404":
// "$ref": "#/responses/notFound"
// "422":
// "$ref": "#/responses/error"
utils.DeleteAccessToken(ctx)
}