Add last_used_ips to project_access_tokens and group_access_token
Title: Expose last_used_ips on project and group access token API responses
extends: https://gitlab.com/gitlab-org/gitlab/-/work_items/428577
Type: Feature / API enhancement
Labels (suggest): ~"group::authentication" ~"Category:System Access" ~"feature" ~"backend" ~"API"
Body:
## Problem
The public REST API responses for project and group access tokens omit `last_used_ips`, even though:
1. The data is already collected — project and group access tokens are `PersonalAccessToken` rows owned by `project_bot` users (see
`app/models/personal_access_token.rb`), and the `last_used_ips` association is populated identically to personal tokens.
2. The GitLab web UI already surfaces "Last Used IPs" for these tokens via the internal `ProjectAccessTokenSerializer` / `GroupAccessTokenSerializer`, both of which
inherit from `AccessTokenEntityBase < API::Entities::PersonalAccessTokenWithLastUsedIps`.
3. The public REST entity `API::Entities::ResourceAccessToken` (`lib/api/entities/resource_access_token.rb`) inherits from the IP-less
`API::Entities::PersonalAccessToken` instead. As a result, programmatic auditors cannot see the same information that admins see in the UI.
4. GraphQL has no `ProjectAccessToken` / `GroupAccessToken` types at all, so the REST API is the only path for programmatic access.
## Use case
We run an internal auditor (`repo-scanner audit`) that flags unmanaged or stale access tokens across our GitLab projects and groups. We'd like to additionally flag
tokens last used from IP ranges outside our trusted infrastructure (e.g. outside GCP/runner CIDR ranges) so an unexpected egress can be detected early. Today this is
impossible without scraping internal Rails routes or reading the database directly.
## Proposal
Change `API::Entities::ResourceAccessToken` to inherit from `API::Entities::PersonalAccessTokenWithLastUsedIps`. This is a one-line change that adds `last_used_ips:
[...]` to the responses of:
- `GET /projects/:id/access_tokens`
- `GET /projects/:id/access_tokens/:token_id`
- `POST /projects/:id/access_tokens/:token_id/rotate`
- `GET /groups/:id/access_tokens`
- `GET /groups/:id/access_tokens/:token_id`
- `POST /groups/:id/access_tokens/:token_id/rotate`
No model changes, no migrations, no new endpoints. Field shape and semantics match the existing personal access tokens implementation.
## Backwards compatibility
Additive only — adds a single new field to existing JSON responses. No existing field changes type or is removed. Clients that ignore unknown fields are unaffected.
task