Skip to content

feat: add token page - #273

Open
olblak wants to merge 2 commits into
updatecli:mainfrom
olblak:feat/tokens-page
Open

feat: add token page#273
olblak wants to merge 2 commits into
updatecli:mainfrom
olblak:feat/tokens-page

Conversation

@olblak

@olblak olblak commented Aug 14, 2026

Copy link
Copy Markdown
Member

Description

Udash now issues its own long-lived API tokens, because an identity provider access token always expires while an unattended CI pipeline needs a credential it can keep. The frontend is where a user creates one.

This also closes a gap that already existed: GetStartedSteps.vue told users to export UPDATECLI_UDASH_ACCESS_TOKEN="your_token_here" with no way to obtain one.

Nothing here changes how the SPA authenticates. It already uses oidc-client-ts with generic OAUTH_* runtime config, so it is provider-agnostic as it stands.

Follows the house style confirmed from the existing components: Options API with setup() only bridging composables (there is no <script setup> anywhere), Vuetify 4, MDI icons.
t

  • v-data-table-virtual listing name, scopes, created, last used, expires — modelled on src/components/pipeline/reports.vue, including its empty-state convention (96px grey icon + text-h5 heading).
  • Create dialog with a name field and scope checkboxes. This is new ground: the codebase had no POST-from-a-form precedent, only POST-search.
  • One-time reveal in a dismissible v-alert, reusing the clipboard pattern from GetStartedSteps.vue (copiedKey + 1.5s mdi-check swap). It does not auto-dismiss, because the token cannot be recovered afterwards.
  • Revoke confirmation dialog.
  • The New token button is hidden unless GET /whoami reports publisher or admin. This is UX only — POST /api/tokens is what actually enforces it.

All calls go through the existing apiFetch(path, { method, body }) in src/composables/api.js, which already attaches the bearer. No new HTTP layer.

/profile/tokensTokensView, with beforeEnter: authGuard.

Registered only in the isAuthEnabled branch, exactly like /profile. The routes array is duplicated across both branches of an if, and API tokens are meaningless on an open instance.

<v-list-item prepend-icon="mdi-key" title="Tokens" to="/profile/tokens"> in the account menu, between Profile and Logout.

A line under the Authenticate step linking to /profile/tokens, shown only when auth is enabled.

Two things worth knowing if you touch this:

  • The link is a <router-link> in the template, not an <a href> inside the step's v-html description. The app uses createWebHistory, not hash routing, so a raw anchor would trigger a full page reload and ignore getAppBasePath().
  • isAuthEnabled is a module-level constant, so it had to be exposed through computed for the template to see it under the Options API.

Also updated the sample env var from your_token_here to udash_pat_..., and noted that udash login prompts for the token or takes --token.

Test

To test this pull request, you can run the following commands:

cp <to_package_directory>
go test

Additional Information

Tradeoff

Potential improvement

Udash now issues its own long-lived API tokens, because an identity provider
access token always expires while an unattended CI pipeline needs a credential it
can keep. The frontend is where a user creates one.

This also closes a gap that already existed: `GetStartedSteps.vue` told users to
`export UPDATECLI_UDASH_ACCESS_TOKEN="your_token_here"` with **no way to obtain
one**.

Nothing here changes how the SPA authenticates. It already uses `oidc-client-ts`
with generic `OAUTH_*` runtime config, so it is provider-agnostic as it stands.

Follows the house style confirmed from the existing components: Options API with
`setup()` only bridging composables (there is no `<script setup>` anywhere),
Vuetify 4, MDI icons.

- `v-data-table-virtual` listing name, scopes, created, last used, expires —
  modelled on `src/components/pipeline/reports.vue`, including its empty-state
  convention (96px grey icon + `text-h5` heading).
- Create dialog with a name field and scope checkboxes. This is **new ground**:
  the codebase had no POST-from-a-form precedent, only POST-search.
- One-time reveal in a dismissible `v-alert`, reusing the clipboard pattern from
  `GetStartedSteps.vue` (`copiedKey` + 1.5s `mdi-check` swap). It does not
  auto-dismiss, because the token cannot be recovered afterwards.
- Revoke confirmation dialog.
- The **New token** button is hidden unless `GET /whoami` reports `publisher` or
  `admin`. This is UX only — `POST /api/tokens` is what actually enforces it.

All calls go through the existing `apiFetch(path, { method, body })` in
`src/composables/api.js`, which already attaches the bearer. No new HTTP layer.

`/profile/tokens` → `TokensView`, with `beforeEnter: authGuard`.

Registered **only in the `isAuthEnabled` branch**, exactly like `/profile`. The
routes array is duplicated across both branches of an `if`, and API tokens are
meaningless on an open instance.

`<v-list-item prepend-icon="mdi-key" title="Tokens" to="/profile/tokens">` in the
account menu, between Profile and Logout.

A line under the Authenticate step linking to `/profile/tokens`, shown only when
auth is enabled.

Two things worth knowing if you touch this:

- The link is a `<router-link>` in the template, **not** an `<a href>` inside the
  step's `v-html` description. The app uses `createWebHistory`, not hash routing,
  so a raw anchor would trigger a full page reload and ignore `getAppBasePath()`.
- `isAuthEnabled` is a module-level constant, so it had to be exposed through
  `computed` for the template to see it under the Options API.

Also updated the sample env var from `your_token_here` to `udash_pat_...`, and
noted that `udash login` prompts for the token or takes `--token`.

Signed-off-by: Olivier Vernin <olivier@vernin.me>
@olblak olblak added the enhancement New feature or request label Aug 14, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant