Project

General

Profile

Actions

Feature #44430

open

Fine-grained Personal Access Tokens

Added by Marius BĂLTEANU 1 day ago. Updated 1 day ago.

Status:
New
Priority:
Normal
Assignee:
-
Category:
Accounts / authentication
Resolution:

Description

Extracted from #43881 only the part that implements the fine grained personal access tokens:

Bogdan Egikov wrote in #note-11:

I have implemented pillar 1 (personal access tokens) together with pillars 2 (scoped permissions) and 4 (structured audit logging) as a single patch against current trunk. Attached: personal-access-tokens-43881.patch.

Personal access tokens

  • New personal_access_tokens table (one migration) and model: multiple named tokens per user, each with a mandatory expiration date. Only the SHA256 digest of the value is stored (same strategy as the existing Doorkeeper hash_token_secrets configuration); the rmpat_-prefixed value is displayed exactly once, at creation.
  • Self-service UI under My account (list / create / revoke), protected by sudo mode; throttled last-used tracking; a new personal_access_token_max_lifetime setting (Administration -> Settings -> Integrations tab, format: int, 0 = no limit, security-notified). The new-token form clamps its default expiration to this policy.
  • Fully backward compatible: find_current_user tries the token digest first and falls back to the unchanged legacy API key, across all three transports (header, key parameter, HTTP Basic username). Existing keys and integrations are not affected - same coexistence approach as the OAuth provider introduction in 6.1.0.
  • A tested PersonalAccessToken.import_legacy_api_tokens! is included but deliberately not invoked: legacy plaintext keys can be converted to hashed tokens server-side without invalidating them (lookup is by digest), so a future release can retire plaintext keys with a data migration in the style of SaltUserPasswords (20110223180953).

Scoped permissions

  • Optional scopes column (space-separated permission names, blank = full access). Enforcement reuses the existing OAuth machinery: scopes are assigned to User#oauth_scope, so the Role#allowed_to? intersection and the admin pseudo-scope apply unchanged, and public permissions are force-included like OAuth applications.
  • Note: this inherits the open defect #44271 (issue attribute editing goes through Issue#user_tracker_permission?, bypassing the scope intersection). The patch pins that behavior with a clearly-labelled test that will start failing once #44271 is fixed, so both mechanisms pick up the fix together.

Holger Just wrote in #note-12:

Most of the functionality of your new personal access tokens seems to mirror what we already have with the oauth applications (e.g. restricted scopes or token live times) as implemented in #24808. As such, I'm not sure if it's actual a good idea to duplicate most of this again. Instead, I would prefer an approach where we adapt / extend the existing oauth applications so that we could issue long-lived access tokens from there.


Files


Related issues

Related to Redmine - Feature #43881: Strengthen API authentication: API tokens with expiration, scopes, rate limiting and audit loggingNewActions
Actions #1

Updated by Marius BĂLTEANU 1 day ago

  • Description updated (diff)
Actions #2

Updated by Marius BĂLTEANU 1 day ago

  • Related to Feature #43881: Strengthen API authentication: API tokens with expiration, scopes, rate limiting and audit logging added
Actions #3

Updated by Marius BĂLTEANU 1 day ago

I extracted from the initial patch posted by Bogdan Egikov in #43881 only the part related to Personal Access Tokens to ease the review of the changes.

Actions #4

Updated by Bogdan Egikov 1 day ago

Thanks for splitting this out, Marius BĂLTEANU – happy to keep iterating on it here.

If any changes are needed to get this ready for a release, notify me and I'll prepare them.

Actions #5

Updated by Marius BĂLTEANU 1 day ago

Holger Just wrote:

Most of the functionality of your new personal access tokens seems to mirror what we already have with the oauth applications (e.g. restricted scopes or token live times) as implemented in #24808. As such, I'm not sure if it's actual a good idea to duplicate most of this again. Instead, I would prefer an approach where we adapt / extend the existing oauth applications so that we could issue long-lived access tokens from there.

I took a look at both implementations and I think we can reuse some existing code (UI, shared permission scope definitions, etc.), but I don't think reusing Doorkeeper's tables and models is the best option for us in the long term. Also, conceptually, OAuth tokens are designed for third-party apps (client IDs, secrets, redirect URIs, refresh tokens), whereas PATs are designed directly for users.

I personally prefer having a dedicated table for PATs so we don't depend on, modify, or couple our implementation with an external gem over which we don't have full control. Additionally, PATs require their own specific fields—such as a friendly name, hashed storage, expiration date, and a last-used timestamp—which are not currently provided by Doorkeeper.

Once we agree on the direction (using our own dedicated PAT table vs OAuth tables), we can move forward with discussions regarding which code can be reused, what cannot, and what else is needed to ship this feature in the next major release.

Actions #6

Updated by Marius BĂLTEANU 1 day ago

Bogdan Egikov wrote in #note-4:

If any changes are needed to get this ready for a release, notify me and I'll prepare them.

We should wait first to reach a common consensus regarding the overall architectural direction before changing any code.

Actions

Also available in: Atom PDF