Skip to content

Authentication in WebUI#1350

Merged
danielballan merged 39 commits into
bluesky:mainfrom
genematx:webui-auth
Apr 28, 2026
Merged

Authentication in WebUI#1350
danielballan merged 39 commits into
bluesky:mainfrom
genematx:webui-auth

Conversation

@genematx

@genematx genematx commented Apr 21, 2026

Copy link
Copy Markdown
Contributor

This adds a complete authentication flow to the web frontend, enabling the browser UI to work with authenticated Tiled servers. Supports both password-based (internal) and OIDC (external) authentication providers (NOTE: currently, with the tag-based authz, only a single provider is fully supported at a time).

Login & Routing

  • Login page (routes/login.tsx) — renders provider-specific UI: username/password form for internal providers, redirect button for external (OIDC) providers
  • OIDC callback (routes/auth-callback.tsx) — captures tokens from the OAuth redirect, restores the user's original URL via the state parameter
  • Route protection — RequireAuth guard redirects unauthenticated users to /login when the server requires authentication
  • Root redirect — navigating to / now redirects to /browse/
  • App bar — shows logged-in user identity with a logout menu, or a "Log in" button when unauthenticated

Token Lifecycle (auth/ module)

  • token-manager.ts — TokenManager class with client-side JWT decoding, expiry tracking, localStorage persistence, and window.__TILED_ACCESS_TOKEN__ global (for use by spec view plugins)
  • auth-provider.tsx — AuthProvider component that manages axios interceptors with proper cleanup on unmount, transparent 401 → refresh → retry with request deduplication, and proactive token refresh (scheduled at half the token's remaining TTL, capped at 5 min before expiry) -- ported from Aditi's work
  • auth-context.tsx — React context with typed AuthContextType interface and useAuth hook ported from Aditi's work
  • types.ts — shared AuthTokens and UserIdentity interfaces -- ported from Aditi's work

Client Improvements

  • Link URL relativization — response interceptor converts absolute URLs in API links fields to relative paths, so the UI works regardless of the origin the server reports -- ported from Aditi's work
  • Authenticated data fetching — array-nd and download-core components fetch via axiosInstance (with Bearer token) instead of raw URLs -- ported from Aditi's work
  • Axios auth interceptors moved from module-level globals into AuthProvider with proper lifecycle management (eject on unmount)

Server-side

  • OIDC authorization requests now include prompt=login to ensure a fresh login prompt
  • Favicon endpoint serves the Tiled icon

Branding

  • Tiled logo and icon SVG assets
  • App bar displays logo alongside "TILED" title
Screenshot 2026-04-21 at 5 13 57 PM Screenshot 2026-04-21 at 5 14 12 PM Screenshot 2026-04-21 at 5 14 43 PM

Testing

To test, use the following:
  1. Start a local OIDC server:
docker run --rm -p 9000:9000 -v $(pwd)/config:/config -e CONFIG_FILE=/config/oidc_qlik_config.json -e USERS_FILE=/config/oidc_qlik_users.json qlik/simple-oidc-provider:0.2.4

using the following config files:

{
  "idp_name": "http://simple-oidc-provider",
  "port": 9000,
  "client_config": [
    {
      "client_id": "tiled_oidc_client",
      "client_secret": "tiled_oidc_secret",
      "redirect_uris": [
        "http://localhost:8000/api/v1/auth/provider/oidc/code"
      ]
    }
  ],
  "claim_mapping": {
    "openid": [ "sub" ],
    "email": [ "email", "email_verified" ],
    "profile": [ "name", "nickname" ]
  }
}
[
  {
    "id": "qlikuser",
    "email": "qlikuser@example.com",
    "email_verified": true,
    "name": "Qlik User",
    "nickname": "qlikuser",
    "password": "password",
    "groups": []
  }
]
  1. Start the Tiled server with the following config file:
authentication:
  single_user_api_key: "secret"
  allow_anonymous_access: false
  secret_keys: ["SECRET"]
  tiled_admins:
    - {"provider": "toy", "id": "admin"}
    - {"provider": "oidc", "id": "dallan"}
  providers:
    - provider: "toy"
      authenticator: "tiled.authenticators:DictionaryAuthenticator"
      args:
        users_to_passwords:
          alice: "alice"
          bob: "bob"
          chris: "chris"
          admin: "admin"
    - provider: oidc
      authenticator: "tiled.authenticators:OIDCAuthenticator"
      args:
        client_id: "tiled_oidc_client"
        client_secret: "tiled_oidc_secret"
        well_known_uri: "http://localhost:9000/.well-known/openid-configuration"
        audience: "tiled_oidc_client"
        redirect_on_success: "http://localhost:8000/ui/auth/callback"
        redirect_on_failure: "http://localhost:8000/ui/login"
database:
  uri: "sqlite:///storage/auth/authn.sqlite"
  init_if_not_exists: true
access_control:
  access_policy: tiled.access_control.access_policies:TagBasedAccessPolicy
  args:
    provider: ["toy", "oidc"]
    tags_db:
      uri: "sqlite:///storage/auth/authz.sqlite"
    access_tags_parser: tiled.access_control.access_tags:AccessTagsParser

Co-authored-by: Aditi Chikkali achikkali1@bnl.gov

Depends on #1351 and #1352.

Checklist

  • Add a Changelog entry

@genematx genematx requested a review from danielballan April 21, 2026 21:29
Allow configuring multiple identity providers in TagBasedAccessPolicy
by accepting either a single string or a list of strings for the
provider parameter.
Add authentication to the React web frontend:
- Login page with password and OIDC provider support
- Token persistence (localStorage) with automatic refresh
- Authenticated image loading and file downloads via axios
- User menu with logout in app bar
- OAuth callback route for OIDC redirect flow
- Favicon route on the server
- Add prompt=login to OIDC authorize params
Support authenticating WebSocket connections by sending credentials in
the first JSON message instead of exposing tokens in query parameters.

- Add authenticate_websocket_first_message() helper
- Add get_decoded_access_token_websocket() for JWT on WebSocket
- Update get_current_principal_websocket to return None (instead of 401)
  when no credentials provided, enabling the first-message fallback
- Update streaming handler to accept already_accepted parameter
- Add tests for first-message auth (valid key, wrong key, invalid msg)
genematx and others added 12 commits April 28, 2026 07:36
…agement

Restructure the web UI authentication code into a dedicated auth/ module
with proper separation of concerns:

- auth/types.ts: shared AuthTokens and UserIdentity interfaces
- auth/token-manager.ts: TokenManager class with JWT decode, expiry
  tracking, and localStorage management
- auth/auth-context.tsx: React context definition and useAuth hook
- auth/auth-provider.tsx: AuthProvider with axios interceptors (with
  cleanup on unmount), proactive token refresh scheduling, and
  deduplicated 401 retry logic

Also:
- Add link URL relativization in client.ts so the UI works regardless
  of the origin the server reports in links
- Add root route redirect (/ → /browse/)
- Remove module-level axios interceptors from client.ts (now managed
  by AuthProvider lifecycle)
- Type selectMetadata as string | null instead of any

Co-authored-by: Aditi Chikkali <achikkali1@bnl.gov>
- Wrap DynamicSpecView in ErrorBoundary so a crashing plugin doesn't
  take down the entire UI
- Remove failed <script> elements from the DOM so retries are possible
- Add comments explaining why loaded scripts are cached and not removed

Co-authored-by: Aditi Chikkali <achikkali1@bnl.gov>
…tches, and dev proxy

- Export axiosInstance and add response interceptor to transform absolute
  URLs in 'links' fields to relative paths, so the UI works regardless of
  the origin the server reports
- Fix about() to use /api/v1/ instead of / (the correct API endpoint)
- Fix selectMetadata type from any to string | null
- Use axiosInstance for image loading in array-nd (blob fetch with auth)
- Use axiosInstance for download/open in download-core (blob fetch with auth)
- Add WebSocket proxy and /custom proxy to vite dev server config
- Add Tiled logo SVG, favicon (.ico and .svg) to public assets
- Update index.html to use new Tiled favicon
- Add /favicon.ico endpoint to serve the icon from the server
- Replace text-only app bar with clickable Tiled logo + title linking to /browse/
- Fix CHANGELOG typo (compfixedliant -> compliant)
# Conflicts:
#	web-frontend/src/components/tiled-app-bar/tiled-app-bar.tsx
@danielballan danielballan merged commit a27354e into bluesky:main Apr 28, 2026
8 of 10 checks passed
@danielballan danielballan mentioned this pull request Apr 28, 2026
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.

2 participants