Skip to content

Add AUTH_TOKENS, per-account API token limits, and a main-site API token menu - #1164

Merged
tariqksoliman merged 13 commits into
developmentfrom
devin/1794333952-user-api-tokens
Nov 11, 2026
Merged

tariqksoliman merged 13 commits into
developmentfrom
devin/1794333952-user-api-tokens

Conversation

@devin-ai-integration

@devin-ai-integration devin-ai-integration Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

Purpose

  • Let a site allow or prevent non-admin accounts from creating API (long-term) tokens.
  • Give signed-in users a way to create and manage their own token from the main site's account menu, instead of only from Configure.
  • Rebuild that account menu as React design-system components.

Proposed Changes

  • [ADD] AUTH_TOKENS=all|admins (default all).

    • siteAccess.validate() stops startup on any other value.
    • With admins, policy.can(p, "tokens.create") is false unless policy.isAdministrator(p), so /api/access/me, /api/longtermtoken/generate and the UI all follow the same rule.
  • [CHANGE] tokenPrincipal refuses a token whose owner no longer holds tokens.create. The token isn't deleted, so it works again if the setting changes back. Its owner can still see and revoke it: /get and /clear now only need a signed-in session with an account (own tokens; everyone's with tokens.manage). A former admin's tokens keep working, with only the access the account has now.

  • [CHANGE] /api/longtermtoken/generate limits:

    • Accounts without an admin role can hold one unexpired token, lasting at most 90 days. never and longer periods are refused.
    • Admins have no limit on count or length.
    • The account's user row is locked (SELECT ... FOR UPDATE) inside a transaction, so concurrent requests can't get past the limit.
    • New replace_id deletes the old token and creates the new one in the same transaction.
    • A limited account's expired tokens are removed when it creates a new one.
  • [FIX] The name part of a token is capped at 32 characters (MAX_LABEL), so names up to 64 characters no longer overflow token_prefix (varchar(64)). The full name is still stored.

  • [CHANGE] /api/longtermtoken/get also returns user_id, limits (unlimited, max_tokens, max_period) and can_create for the UI.

  • [CHANGE] The account menu is now React, under UserInterface_/components/AccountMenu/:

    • AccountMenu wraps AccountSection (a profile header with the avatar, name and provider, then ApiTokensRow when signed in or a Sign in item for guests), ServicesSection and SignOutItem. A shared useLogin hook loads Login and follows mmgis:loginchange / mmgis:accountprovider.
    • Layout follows the "profile header + list" design: the token, Sign in and Sign out rows are full-width <button> items (Tab and Enter work), services show a status dot with their state, and only Connect is a filled button.
    • TopBar opens it and owns whether it's open. It closes on an outside click or Escape.
    • Connections_ no longer draws the menu. It keeps the services list (new menuServices() and toolsUsing() exports), connect/disconnect, and the "connect first" cards inside tools. toggleMenu, closeMenu and avatarColor were removed from it; avatarColor moved to AccountMenu/avatarColor.js.
  • [ADD] ApiTokensRow in the account menu, shown for accounts holding tokens.create, or holding a paused token (marked Paused; the window then offers only Revoke). It loads after the menu opens and opens the new ApiTokens modal, which can:

    • create a token: name, expiry presets limited by limits, read-only or all access, all or selected missions;
    • show the new token once, with Copy and a curl example;
    • show token details, and replace or revoke (revoke asks for confirmation through ConfirmationModal).
    • Replace starts from the old token's mission list and access, so it never widens them.
    • Escape and a focusable X close the modal.
  • [ADD] Design-system Menu (src/design-system/components/Menu), built on Base UI's menu: keyboard navigation, Escape closes only the menu, and focus goes back to the trigger.

  • [CHANGE] Admins (limits.unlimited) see a compact token list in a 520px window. Each token is one row with its name, access · missions · expiry, a status, and a ⋯ menu (Replace, Revoke/Remove). Clicking a row expands its prefix and details. Live tokens come first, soonest expiry first; expired tokens are dimmed at the bottom. Regular users keep the single card.

  • [CHANGE] Token window layout ("polished" design):

    • A header with an icon tile, the title and a subtitle for each view; the footer stays fixed with the primary action on the right, and only the body scrolls.
    • Each token is a card: name, prefix, status (Active / Expires soon / Expired), a two-column details grid, the expiry date with days left, a lifetime bar, and its own Replace/Revoke.
    • The create form uses sentence-case labels with help text (the resulting expiry date, "Up to N days"). Accounts with only one access choice see "Read-only" as text instead of a one-option radio.
    • The shown-once screen keeps the token, Copy and the warning together. It closes the same way as before.
  • [ADD] Design-system components built on @base-ui/react:

    • Radio: a list of choices one above the other, on Base UI radio-group;
    • TextInput: on Base UI Input.

    The account menu and token modal use these together with the existing Button, Checkbox and RadioGroup.

  • [CHANGE] The segmented RadioGroup is now built on Base UI radio-group, which gives it arrow-key navigation. Its default look is unchanged; a new opt-in variant="segmented" draws it as a full-width segmented control (used for the token window's expiry choice).

  • [FIX] Modal now stacks modals in the order they open, not by id, and adds Modal.top(). Before this, ConfirmationModal (id 0) always drew behind any modal with a string id. ConfirmationModal now closes on Escape when it's on top.

  • [ADD] AUTH_TOKENS in sample.env and docs/pages/Setup/ENVs/ENVs.md. The account menu bullet in docs/pages/Contributing/Connections/Connections.md now mentions the token row.

Issues

Testing

  • New plugins/core/backend/LongTermToken/tests/user-tokens.spec.js (e2e, SITE_ACCESS=required AUTH_PROVIDERS=local) covers:
    • the limits returned by /get;
    • refusal of never and anything over 90 days;
    • a second token refused;
    • replace_id, including another account's token id and a nonexistent id;
    • 5 concurrent generate requests leaving exactly one token;
    • an expired token not counting toward the limit;
    • an admin creating many tokens, including never;
    • a 64-character name;
    • bearer tokens refused on /get and /clear;
    • with AUTH_TOKENS=admins (needs AUTH_TOKENS_SETUP_URL pointing at an all server to make the token first): a paused token is listed with can_create: false, doesn't authenticate, and its owner can revoke it.
  • New unit tests for AUTH_TOKENS validation, can("tokens.create") under admins, and refusing and re-enabling tokens in tokenPrincipal.
  • npx playwright test tests/unit/: 1778 passed. LongTermToken plugin tests: all passed (the older longtermtokens.spec.js skipped itself).
  • tests/e2e/auth/ + tests/e2e/security/: 3 failures. The same 3 fail on unmodified development (permission-matrix dataset listings, configure-export viewing user, missions-viewing restricted list).
  • npm run build compiles. eslint shows no new problems; the one no-restricted-globals error in TopBar.jsx was already there.
  • Tested in a browser on a production build, side by side with development's jQuery menu. These match development:
    • where the menu sits and how it closes;
    • avatar, name and colours;
    • sign in/out;
    • SITE_ACCESS=public;
    • Services, checked with temporary local stand-in connections: status dots, the detail text, Connect opening its popup, Disconnect, the "connect first" card inside a tool, and the account button's attention state.
  • Token flows checked in the browser:
    • a regular user creating, being shown the token once, copying it, managing, replacing and revoking it;
    • an admin creating several tokens, including 1-year/full-access and Never;
    • AUTH_TOKENS=admins hiding the row for a regular user;
    • the fixes above.
  • Account menu restyle (profile header + list): checked on a production build as admin, regular user with a token, with a connected and a needed service (temporary local connection plugin, not committed), and as a guest with SITE_ACCESS=optional; Tab and Enter reach and activate the token, Sign in and Sign out items.
  • Token window polish: checked on a production build. As a regular user: manage, replace with a mission selected, and create. As an admin: several tokens, create with all expiry options, and the shown-once screen. Arrow keys and Tab in the expiry control, Escape closing, and the revoke prompt on top were rechecked.
  • Admin compact list: checked on a production build with 7 live tokens and 1 expired. Row expand/collapse, the ⋯ menu (Replace keeps a token's missions, Revoke prompt on top), Remove on the expired token, and keyboard use (Tab/Enter/Space on rows, arrow keys in the menu, Escape closing the menu, then the window). The regular user's card is unchanged.
  • Not checked: a real service login, and long mission lists. Token values are masked in the screenshots.

Admin account menu
Revoke prompt on top
Account menu with services
Account menu, regular user
Account menu, guest
Token window, regular user
Replace form
Create form, admin
Shown once (value masked)
Admin token list
Admin row expanded
Admin row menu

Link to Devin session: https://nasa-jpl-demo.devinenterprise.com/sessions/86df24187c6a4fe7b88708c46ab61d0d
Open in Devin Desktop: https://nasa-jpl-demo.devinenterprise.com/desktop/session/86df24187c6a4fe7b88708c46ab61d0d?variant=devin
Requested by: @tariqksoliman


🟢 Devin Review found nothing to fix · View findings · Follow Devin's work

Devin Review

…ken menu

Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
@devin-ai-integration

Copy link
Copy Markdown
Contributor Author

I'll fix CI failures and address comments from users with write access. I'll skip comments containing "(aside)".

  • Disable automatic comment, CI, and merge conflict monitoring

@github-actions

github-actions Bot commented Nov 10, 2026 •

Copy link
Copy Markdown
Contributor

✅ Version Already Updated

This PR includes a manual version update to 5.5.3-20261110

No automatic version bump needed.

devin-ai-integration Bot and others added 9 commits November 10, 2026 18:32
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
Co-Authored-By: tariq.k.soliman <tariqksoliman@gmail.com>
@tariqksoliman
tariqksoliman marked this pull request as ready for review November 11, 2026 00:29
@github-actions

Copy link
Copy Markdown
Contributor

🤖 Version Auto-Bumped

The version has been automatically incremented to 5.5.4-20261111

This commit was added to your PR branch. When you merge this PR, the new version will be included.


If you want a different version, update package.json manually and push to this PR.

@devin-ai-integration devin-ai-integration Bot left a comment •

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no bugs or issues to report.

Devin Review

@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant