Skip to content

Recipe: Auth Tokens via baseUrl + auth

The baseUrl pattern covers the unauthenticated case: one origin, relative item paths. A real media server almost always sits behind auth too, baseUrl still supplies the origin, auth supplies the credentials every request against it carries, including the ones nomercy-video-player issues itself for HLS manifests and segments.

Wiring bearerToken

bearerToken accepts a plain string, a sync getter, or an async getter, so a reactive store’s current value is always read fresh instead of captured once at setup() time:

TypeScript
import nmplayer from '@nomercy-entertainment/nomercy-video-player';
import type { VideoPlaylistItem } from '@nomercy-entertainment/nomercy-video-player';

const player = nmplayer('player');
// Your own protected catalogue — this recipe is about the `auth` block,
// not about media, so this is your app's real playlist, not a fixture.
const playlist: VideoPlaylistItem[] = [];

player.setup({
baseUrl: 'https://media.your-domain.com/api/files',
auth: {
bearerToken: () => authStore.accessToken, // read fresh on every request
},
playlist,
});

An async getter works identically, useful when the token itself has to be fetched or decrypted before use:

TypeScript
auth: {
bearerToken: async () => (await tokenCache.get()) ?? '',
},

Reaching the HLS backend

Authorization: Bearer <token> is not only for fetch()-based calls (subtitles, chapters, thumbnails). nomercy-video-player’s HTML5 backend wires the same resolved bearer value into hls.js’s own manifest and segment requests automatically, there is nothing extra to configure for adaptive streaming specifically. Set auth once and every request path, kit-level fetch, hls.js, image resolution, uses it.

Handling a 401

refreshOnUnauthenticated runs exactly once per 401 response, never on a 403 (which propagates immediately, on the assumption a 403 means “not allowed”, not “token expired”). After it resolves, the original request retries once with freshly-resolved bearerToken / headers:

TypeScript
auth: {
bearerToken: () => authStore.accessToken,
refreshOnUnauthenticated: async () => {
await authStore.refresh(); // updates authStore.accessToken in place
},
retryAfterRefresh: 1, // default — 0 disables the retry entirely
},

The getter is what makes this work without extra plumbing: refreshOnUnauthenticated mutates the store, and the next bearerToken() call (the retry) reads the already-updated value, no manual “pass the new token back” step needed.

Rotating the token mid-session

For a token that expires on a schedule you already know (a short-lived signed URL scheme, for example), update auth directly on the live player instead of waiting for a 401 to trigger the refresh path:

TypeScript
player.auth({ bearerToken: newToken }); // merges onto the existing AuthConfig

Any object argument shallow-merges onto the current config; auth(null) clears it entirely. Passing just { bearerToken } here only changes that field, headers, credentials, and the rest of the current config stay intact. refreshAuth() runs the current config’s refreshOnUnauthenticated handler on demand, outside the 401 path, then emits auth:refreshed; if that handler throws it emits auth:failed instead of throwing. Reach for it to trigger a refresh you know is due without waiting for a 401 to force one.

When the media server relies on a session cookie rather than a header, skip bearerToken and set credentials instead. The kit applies it to its own fetch() calls (subtitles, chapters, thumbnails, playlist):

TypeScript
auth: {
credentials: 'include',
},

The HLS backend is the exception: its hls.js manifest and segment requests carry cookies only when they are same-origin with the page, because the backend wires the bearer header but never sets withCredentials. So for a cross-origin media server, cookie auth covers the kit fetches but not adaptive streaming; reach for a bearer token or a same-origin proxy there. When a fetch is cross-origin, credentials: 'include' also needs the server’s CORS headers to line up (Access-Control-Allow-Credentials: true plus an explicit origin, never *); the browser enforces that regardless of anything this config does.

Next steps

  • Adapter: Video Backend: how the resolved backend applies auth to every manifest and segment request it issues.