K

Read the SSO user info

Your app never needs to build login itself. How to read who is connected right now, via the SDK or the SSO headers.

An app behind AxHub never needs to build login itself.
Read who just arrived, and show each person their own screens and data.

The principle fits in one line. The gate standing in front of your app checks login on your behalf, and only on requests that pass does it attach six tags saying "this is who the person is" before handing the request to your app. These tags are called the SSO headers.

사용자요청게이트로그인 검사통과 시 헤더 6개 추가내 앱통과한 요청에 붙는 헤더 6개X-AxHub-User-IDX-AxHub-User-EmailX-AxHub-User-NameX-AxHub-App-RoleX-AxHub-Is-AdminX-AxHub-Tenant-Slug
게이트를 지난 요청에 사용자 정보가 헤더로 담기는 흐름

If you built from a template

The Next.js and Astro templates already read it through the SDK.

import { makeAxhub } from '@/lib/axhub-server';

const sdk = await makeAxhub();
const me = await sdk.identity.me();
// me.email, me.name, me.tenants (companies and roles)

Key your data on something stable per person, like me.email. That's how the template's to-do list separates data per user.

const userKey = me?.email ?? 'local-dev';
await db()`INSERT INTO todos (user_key, title) VALUES (${userKey}, ${title})`;

Filter reads by WHERE user_key = ${userKey} as well, so nobody's rows leak into someone else's screen.

Reading from the headers

You can skip the SDK and read the headers directly. Here are the six that arrive, and what each carries.

HeaderValue
X-AxHub-User-IDThe user's UUID
X-AxHub-User-Emailbase64-encoded email
X-AxHub-User-Namebase64-encoded name
X-AxHub-App-Roleowner · platform_admin · tenant_admin · app_member · tenant_member · guest
X-AxHub-Is-Admintrue · false
X-AxHub-Tenant-SlugThe tenant slug

On the server, you read them like this.

export function currentAxHubUser(req: Request) {
  const h = req.headers;
  return {
    id: h.get('x-axhub-user-id'),
    email: Buffer.from(h.get('x-axhub-user-email') ?? '', 'base64').toString('utf8'),
    name: Buffer.from(h.get('x-axhub-user-name') ?? '', 'base64').toString('utf8'),
    role: h.get('x-axhub-app-role'),
    isAdmin: h.get('x-axhub-is-admin') === 'true',
    tenant: h.get('x-axhub-tenant-slug'),
  };
}

Always read them on the server. Any X-AxHub-* value sent from outside your app is overwritten by the gate — whether it came from a browser, a terminal command, or a script.

When the headers are empty

If X-AxHub-User-ID is empty, treat the visitor as not signed in. That happens in two cases.

  • The request was denied — all six headers arrive empty
  • An anonymous visitor reached an externally public app — they pass through, but all six are still empty

Branching on the role

guest means someone who is signed in but isn't a member of this app. That's what a person from another company gets on an externally public app.

If a value arrives that isn't in the table, treat it as the lowest privilege. Roles can be added later, and treating an unknown value as an admin is how holes appear.

Reading via the SDK

The SDK can tell you who's here too. On top of email and name, you get their companies and roles in one call.

const me = await sdk.identity.me();
// me.email, me.name, me.tenants (companies and roles)

You've succeeded when

You sign in with your company account, visit the deployed app, and the email and name your app read show your own account.

Next, send your app's news to people at your company in Send notifications.