U-net Docs
quickstart

Sign in with U-net

Add QR-based login where your service receives only a stable service-scoped ID and a short-lived signed assertion.

@union-networks/web-login@union-networks/server@union-networks/react

Mental model

Your website creates a one-time login session. The browser renders a QR payload. The user scans it in the U-net app, confirms with the device security boundary, and U-net returns a signed assertion to the waiting browser.

The assertion contains the service ID, scoped user ID, session ID, issue time, and expiry. It does not contain a public U-net ID, holder ID, email, phone number, FCM token, private key, or attestation data.

  • Store `scopedUserId` as the account identifier for your service.
  • Verify `assertionJws` on your server before creating a session.
  • Treat denied and expired QR sessions as normal user outcomes.

Create and poll a login QR

Use `@union-networks/web-login` in browser code when the page is opened outside U-net.

tsBrowser login
import {
  createLoginSession,
  isApprovedLoginResult,
  pollLoginSession,
  renderLoginQrPayload,
} from '@union-networks/web-login';

const session = await createLoginSession(
  {
    serviceId: 'example-shop',
    origin: window.location.origin,
    expiresInSeconds: 120,
  },
  { issuerBaseUrl: 'https://issuer.egress.live' },
);

renderQr(renderLoginQrPayload(session));

const result = await pollLoginSession(session.sessionId, {
  issuerBaseUrl: 'https://issuer.egress.live',
  intervalMs: 1500,
  timeoutMs: 120000,
});

if (isApprovedLoginResult(result)) {
  await fetch('/api/unet/session', {
    method: 'POST',
    headers: { 'content-type': 'application/json' },
    body: JSON.stringify({ assertionJws: result.assertionJws }),
  });
}
Representation

QR states: creating -> pending -> approved | denied | expired

Verify the assertion on your server

Never trust a scoped ID copied directly from browser JavaScript. Send the assertion to your server and verify it there.

tsNext.js route handler
import { verifyLoginAssertion } from '@union-networks/server';

export async function POST(request: Request) {
  const { assertionJws } = await request.json();

  const claims = verifyLoginAssertion(assertionJws, {
    secret: process.env.UNET_WEB_LOGIN_ASSERTION_SECRET!,
    serviceId: 'example-shop',
  });

  // Store claims.scopedUserId as the account id for your service.
  await createSession({ scopedUserId: claims.scopedUserId });
  return Response.json({ ok: true });
}
Representation

Server creates app session only after HMAC assertion verification.