Skip to main content
Version: 14.5.2

Frontend Application

info

This authorization flow is in preview and requires the Advanced or Customization section of the Console Project configuration.

The JavaScript client library @mia-platform-internal/authtool-client-js is the frontend counterpart of the authtool_bff. It abstracts the session lifecycle, token management, and login/logout redirects so that your application only needs to react to authentication state changes.

The library ships two entry points:

Entry pointImport pathDescription
Core@mia-platform-internal/authtool-client-jsFramework-agnostic client (vanilla JS, Svelte, Vue, etc.)
React@mia-platform-internal/authtool-client-js/reactReact Context, hooks, and helper components

Both ESM and CommonJS builds are provided.

Installation

npm install @mia-platform-internal/authtool-client-js
# or
yarn add @mia-platform-internal/authtool-client-js

React bindings require react and react-dom as peer dependencies (v18 or v19).

Core Usage (Framework-Agnostic)

The library exposes an init function that creates a UserManager — the single object your application interacts with.

import { init, isUnauthorized } from '@mia-platform-internal/authtool-client-js'

const manager = init({
// base URL where authtool-bff is reachable (default: '/')
baseUrl: '/bff/',

// 'bff' → cookie-only session
// 'tokenExchange' → cookie session + short-lived AT returned to JS
mode: 'bff',

// whether to call `start()` immediately (default: true)
// NOTE: currently requires an explicit `start()` call in vanilla JS;
// the React hook `useAuthtoolContext()` calls `start()` automatically
autostart: true,

// max random jitter subtracted from token expiry before scheduling a refresh (default: 7000 ms)
maxJitterMs: 7000,
})

UserManager API

Property / MethodTypeDescription
baseUrlstringNormalized BFF base URL.
client.loginUrl(redirectUri?)URLBuilds the login URL. Optionally accepts a redirect_uri so the BFF redirects back after login.
client.logoutUrl(redirectUri?)URLBuilds the logout URL.
current$Observable<User | Error>Emits the current user (or an error) whenever the session state changes.
accessToken$Observable<AccessTokenContext | Error>(token-exchange mode only) Emits the latest access token context.
authorizedFetchtypeof fetchA fetch wrapper that automatically attaches the Authorization: Bearer <AT> header when a valid access token is available.
fetchSession()voidManually triggers a session check against the BFF /session endpoint.
fetchAccessToken()voidManually requests a new access token from the BFF /token endpoint and schedules the next refresh.
start()voidStarts the manager: fetches the session and, in tokenExchange mode, begins the token refresh loop. In React, useAuthtoolContext() calls this automatically. In vanilla JS, you must call start() explicitly.

Subscribing to State

current$ and accessToken$ are lightweight observables. Subscribe to be notified whenever the session or access token changes:

const subscription = manager.current$.subscribe((value) => {
if (isUnauthorized(value)) {
// redirect to login
window.location.href = manager.client.loginUrl(window.location.href).href
return
}

// value is of type User
console.log('User info:', value.userinfo)
console.log('Session valid until:', value.valid_until)
})

// later, to tear down:
subscription.unsubscribe()

Making Authorized API Calls

In tokenExchange mode, the manager keeps a valid access token in memory and exposes authorizedFetch — a drop-in replacement for window.fetch:

const manager = init({ baseUrl: '/bff/', mode: 'tokenExchange' })

// later, once the session is established:
const response = await manager.authorizedFetch('/api/data')
const data = await response.json()

authorizedFetch automatically adds the Authorization header with the current bearer token. Token refresh is handled transparently in the background.

Token Refresh Lifecycle

When mode is set to tokenExchange:

  1. On start(), the manager calls the BFF POST /token endpoint.
  2. The BFF returns an AccessToken containing access_token and expires_in.
  3. The manager schedules the next refresh at expires_in * 1000 - randomJitter milliseconds (with a minimum delay of 1 second).
  4. If the refresh fails with a 401, the manager stops and emits an UnauthorizedError on accessToken$.

React Integration

The React entry point wraps the core UserManager with React Context and hooks.

Setup

Create the manager once and pass it to AuthtoolProvider at the top of your component tree:

import { init } from '@mia-platform-internal/authtool-client-js'
import { AuthtoolProvider } from '@mia-platform-internal/authtool-client-js/react'

const manager = init({ baseUrl: '/bff/', mode: 'tokenExchange' })

function Root() {
return (
<AuthtoolProvider value={manager}>
<App />
</AuthtoolProvider>
)
}

useAuthtoolContext Hook

Inside the provider tree, call useAuthtoolContext() to access authentication state reactively. The hook subscribes to current$ and accessToken$ and triggers re-renders when their values change.

import {
useAuthtoolContext,
isUnauthorized
} from '@mia-platform-internal/authtool-client-js/react'

function Dashboard() {
const { user, accessToken, fetch, loginUrl, logoutUrl } = useAuthtoolContext()

if (user === undefined) {
return <p>Loading…</p>
}

if (isUnauthorized(user)) {
return <p>Not authenticated</p>
}

return (
<div>
<h1>Welcome, {String(user.userinfo.name)}</h1>
<button onClick={() => { window.location.href = logoutUrl().href }}>
Logout
</button>
</div>
)
}

The object returned by useAuthtoolContext() contains:

FieldTypeDescription
userUser | Error | undefinedCurrent session state. undefined while loading.
accessTokenAccessTokenContext | Error | undefinedCurrent access token (token-exchange mode).
fetchtypeof fetchauthorizedFetch — fetch with auto-attached bearer token.
baseUrlstringBFF base URL.
loginUrl(redirectUri?)(redirectUri?: string) => URLBuilds the login URL.
logoutUrl(redirectUri?)(redirectUri?: string) => URLBuilds the logout URL.

RedirectToLogin Component

A convenience component that automatically redirects unauthenticated users to the login page:

import { RedirectToLogin } from '@mia-platform-internal/authtool-client-js/react'

function App() {
return (
<>
<RedirectToLogin />
{/* rest of your application */}
</>
)
}

When the session check returns a 401, RedirectToLogin navigates the browser to the BFF login endpoint, passing the current URL as redirect_uri so the user returns to the same page after authentication.

Error Handling

The library exposes two typed error classes:

  • UnauthorizedError — the BFF returned 401; the session is invalid or expired.
  • UnhandledError — any other non-OK response from the BFF.

Both carry the original Response object for inspection. Use the isUnauthorized helper to discriminate:

import { isUnauthorized } from '@mia-platform-internal/authtool-client-js'

manager.current$.subscribe((value) => {
if (isUnauthorized(value)) {
// handle 401 — e.g., redirect to login
} else if ('error' in value) {
// handle other errors
console.error(value.error)
} else {
// value is User
}
})

Putting It All Together

A minimal end-to-end React application:

import React from 'react'
import { createRoot } from 'react-dom/client'

import { init } from '@mia-platform-internal/authtool-client-js'
import {
AuthtoolProvider,
useAuthtoolContext,
RedirectToLogin,
isUnauthorized
} from '@mia-platform-internal/authtool-client-js/react'

const manager = init({ baseUrl: '/bff/', mode: 'tokenExchange' })

function App() {
const { user, fetch } = useAuthtoolContext()

if (user === undefined) {
return <p>Loading…</p>
}

return (
<>
<RedirectToLogin />
{!isUnauthorized(user) && (
<div>
<h1>Welcome, {String(user.userinfo.email)}</h1>
<button onClick={async () => {
const res = await fetch('/api/data')
console.log(await res.json())
}}>
Fetch data
</button>
</div>
)}
</>
)
}

createRoot(document.getElementById('root')!).render(
<AuthtoolProvider value={manager}>
<App />
</AuthtoolProvider>
)