Browser apps (SPA)

Use a Browser app (SPA) client when JavaScript in the page exchanges the authorization code itself. It has no client secret, because anything shipped to the browser is public. PKCE protects the code instead.

Register JavaScript origins

Your page calls Motaware's token, userinfo and revocation endpoints across origins. Add every origin your app is served from, such as https://app.example.com or http://localhost:5173, under JavaScript origins on the client. Origins are scheme, host and port only, with no path. These calls are made without cookies.

oidc-client-ts

// npm install oidc-client-ts
import { UserManager, WebStorageStateStore, InMemoryWebStorage } from "oidc-client-ts";

export const motaware = new UserManager({
  authority: "https://auth.motaware.com",
  client_id: "mw_yourclientid",
  redirect_uri: "https://app.example.com/callback",
  post_logout_redirect_uri: "https://app.example.com/",
  response_type: "code",
  scope: "openid email profile offline_access",
  // Tokens in memory only (the default is sessionStorage). After a page reload, call
  // signinRedirect() again: it is quick while the person is still signed in to Motaware.
  userStore: new WebStorageStateStore({ store: new InMemoryWebStorage() }),
  automaticSilentRenew: true,   // uses the refresh token (offline_access), not an iframe
});

// Sign-in button
await motaware.signinRedirect();

// On /callback
const user = await motaware.signinRedirectCallback();
console.log(user.profile.sub, user.profile.email);

Keeping people signed in

  • Ask for offline_access and renew with the refresh token. oidc-client-ts does this when it has a refresh token.
  • Iframe-based silent renew is not supported for outside apps. Motaware's sign-in and consent pages refuse to be framed.
  • prompt=none works only after the person has already given consent; otherwise you get consent_required.
  • Refresh tokens may be rotated: always store the newest one you receive.
  • Keep tokens in memory where you can. Anything in localStorage can be read by any script that runs on your page.