req.user — no extra calls needed.profile.branding on vault entities.passport.includeBranding: true is declared in catalog-info.yaml, Bio-ID fetches vault branding at mint time (fail-open, 3s timeout) and bundles it into the JWT branding claim.req.user.branding — no separate vault call needed for authenticated requests.interface VaultBranding {
// Self-serve (Claimed tier)
displayName: string // shown on all surfaces — REQUIRED on first write
tagline?: string // "Colorado's largest commercial agency"
logoUrl?: string // primary logo — SVG or PNG
logoMarkUrl?: string // icon/mark for small surfaces (32x32)
primaryColor: string // hex — buttons, accents (default: '#0F172A')
secondaryColor?: string // hex
websiteUrl?: string
phone?: string
email?: string
// White-Label Approved tier only
customDomain?: string // auth.acmeagency.com
hideEcoBranding: boolean // remove "Powered by InsurEco" — requires whiteLabelApproved
emailFromName?: string // magic link sender name
customCss?: string // injected into consent page, max 10KB
// Platform-managed (only settable by platform org)
verified: boolean
whiteLabelApproved: boolean
}
| Tier | Who | What they can set |
|---|---|---|
| Claimed | Any entity after Bio-ID claim flow | displayName, tagline, logoUrl, logoMarkUrl, colors, website, phone, email |
| White-Label Approved | Platform approval after verification | customDomain, hideEcoBranding, emailFromName, customCss |
GET /v1/branding/:orgSlug → 0 gas, no auth (public)
GET /v1/entities/:iecHash/branding → 0 gas, no auth (public)
PATCH /v1/entities/:iecHash/branding → 2 gas, auth required (owner/admin)
displayName.verified and whiteLabelApproved can only be set by platform services — PATCH from a regular service returns 403 for those fields.hideEcoBranding is silently ignored unless whiteLabelApproved: true.import { VaultClient } from '@insureco/bio-vault'
const vault = VaultClient.fromEnv()
// Read by orgSlug (most common — you have it from the JWT)
const branding = await vault.getBranding('acme-agency')
// Read by iecHash (for public entity pages)
const branding = await vault.getBrandingByHash('0x1a2b3c...')
// Update (owner/admin only)
await vault.updateBranding('0x1a2b3c...', {
tagline: 'Updated tagline',
primaryColor: '#1D3557',
})
When an OAuth client has passportConfig.includeBranding: true, Bio-ID fetches vault branding at token mint time and includes it in the JWT.
interface IPassportConfig {
includeBranding: boolean // fetch vault branding at mint time
includeIecHash: boolean // include entity iecHash in token
}
Services opt in via catalog-info.yaml:
spec:
auth:
mode: sso
passport:
includeBranding: true # sets passportConfig.includeBranding on the OAuth client
includeIecHash: false # sets passportConfig.includeIecHash (default: false)
On deploy, the builder reads spec.auth.passport and sets passportConfig on the OAuth client. Bio-ID then enriches the JWT at token mint time based on these flags.
The shorthand
branding: trueis still accepted and maps toincludeBranding: true. The explicit form is preferred for new services.
When includeBranding: true, the JWT contains a branding claim with this shape:
{
"branding": {
"displayName": "Acme Insurance",
"logoUrl": "https://...",
"logoMarkUrl": "https://...",
"primaryColor": "#0066CC",
"secondaryColor": "#003366",
"verified": true,
"whiteLabelApproved": false
}
}
// TypeScript interface — subset of VaultBranding, only display-relevant fields
interface PassportBranding {
displayName: string
logoUrl?: string
logoMarkUrl?: string
primaryColor: string
secondaryColor?: string
verified: boolean
whiteLabelApproved: boolean
}
Bio-ID fetches vault branding with a 3-second timeout. If the vault is unreachable at mint time, Bio-ID issues the token without a branding claim — auth still works. Branding is best-effort. Services should handle req.user.branding being undefined.
Branding refreshes on every token mint and refresh. Since access tokens have a 1-hour TTL, branding changes propagate within 1 hour at most.
The full Passport shape (identity + branding + permissions):
interface Passport {
// Identity
bioId: string
email: string
firstName: string
lastName: string
displayName: string
avatar?: string
userType: string // 'user' | 'service' | 'admin'
roles: string[]
// Primary org
orgSlug: string
orgId: string // ORG-xxx format
orgName: string
// All org memberships
orgs: {
orgSlug: string
orgId: string
orgName: string
roles: string[]
modules: string[]
}[]
modules: string[]
// Branding — present when passport.includeBranding: true
branding?: PassportBranding
// Permissions — resolved from Koko at mint time
permissions: { scopes: string[] }
// Vault link
iecHash?: string
// Session metadata
issuedAt: string
connectedServices: string[]
}
// req.user IS the Passport — populated by Janus or your middleware
const { branding, permissions } = req.user
if (branding) {
document.title = branding.displayName
headerLogo.src = branding.logoUrl
root.style.setProperty('--primary', branding.primaryColor)
// Trust badge
if (branding.verified) showVerifiedBadge()
// White-label features
if (branding.whiteLabelApproved) hideEcoBranding()
}
// Permission gating
if (permissions.scopes.includes('raterspot:rate')) showRatingPanel()
Only call vault when there is no Passport available:
// Public entity profile page — no user session
const branding = await vault.getBranding('acme-agency')
// Background job — no request context
const branding = await vault.getBranding(orgSlug)
wss://bio.insureco.io/passport?token=<access_token>
| Event | When | Payload |
|---|---|---|
identity | On connect | { type, passport } |
passport_updated | Role/org/branding change | { type, passport } |
revoked | Logout or token invalidated | { type } |
const ws = new WebSocket(`${BIO_ID_URL.replace(/^http/, 'ws')}/passport?token=${token}`)
ws.onmessage = (e) => {
const { type, passport } = JSON.parse(e.data)
if (type === 'identity' || type === 'passport_updated') applyBranding(passport.branding)
if (type === 'revoked') clearSession()
}
All three produce the same Passport. The mode determines the user experience.
| Mode | catalog-info.yaml | Bio-ID visible? |
|---|---|---|
| Branded | auth: mode: sso | Yes — "Sign in with Bio-ID" |
| White-Label | auth: mode: sso + passport: includeBranding: true | No — org's domain + branding |
| Headless | auth: mode: sso + passport: includeBranding: true, headless: true | No — server-to-server, fully invisible |
White-Label requires whiteLabelApproved: true on the vault entity for custom domain and hideEcoBranding features.
passport: includeBranding: true in catalog-info.yaml, the Passport carries identity only.if (req.user.branding) before accessing fields.req.user.branding instead. Vault calls are for public/unauthenticated contexts only.verified and whiteLabelApproved return 403 when set by non-platform services.whiteLabelApproved: true has no effect.displayName on a new branding record fails validation.Last updated: July 15, 2026