auth: required → Janus verifies tokens for you. No SDK needed.verifyTokenJWKS(token). No secret needed.BioAuth.fromEnv().getClientCredentialsToken(scopes).JWT_SECRET — that was HS256. Bio-ID 0.3.0+ issues RS256 tokens.Bio-ID issues RS256-signed JWTs. The private key lives only in Bio-ID. Consuming services verify using Bio-ID's public JWKS endpoint — no shared secret.
Developer → OAuth flow → Bio-ID → RS256 JWT
JWT → request header → Janus (verifies via JWKS) → your service
If your service registers routes in catalog-info.yaml with auth: required, Janus verifies the token before proxying. Your handler receives a verified request. Most services never call verifyTokenJWKS() directly.
spec:
routes:
- path: /api/my-service/data
methods: [GET]
auth: required # Janus handles verification
Only services with custom auth middleware (Next.js edge middleware, standalone API servers that don't register routes in Koko) need to verify tokens themselves.
import { verifyTokenJWKS } from '@insureco/bio'
// In middleware or route handler:
const token = req.headers.authorization?.slice(7)
if (!token) return res.status(401).json({ error: 'Unauthorized' })
const payload = await verifyTokenJWKS(token)
// payload.bioId, payload.orgSlug, payload.roles, payload.email
BIO_ID_URL is auto-injected by the builder. verifyTokenJWKS() reads it automatically.
import { BioAuth } from '@insureco/bio'
// All env vars auto-injected by builder: BIO_CLIENT_ID, BIO_CLIENT_SECRET, BIO_ID_URL
const bio = BioAuth.fromEnv()
// 1. Build authorization URL
const { url, state, codeVerifier } = bio.getAuthorizationUrl({
redirectUri: `${process.env.APP_URL}/api/auth/callback`,
})
// Store state + codeVerifier in httpOnly cookies, redirect user to url
// 2. Handle callback (must be at /api/auth/callback — builder registers this URI)
const tokens = await bio.exchangeCode(code, codeVerifier, redirectUri)
// Store tokens.access_token and tokens.refresh_token in session
// 3. Refresh when expired
const newTokens = await bio.refreshToken(refreshToken)
Critical: Your callback MUST be at /api/auth/callback. The builder registers this exact path. Any other path fails with "Invalid Redirect URI".
const bio = BioAuth.fromEnv()
const { access_token } = await bio.getClientCredentialsToken(['target-service:scope'])
await fetch(`${process.env.TARGET_URL}/api/endpoint`, {
headers: { Authorization: `Bearer ${access_token}` },
})
| Variable | Source | Purpose |
|---|---|---|
BIO_CLIENT_ID | Builder (auto) | Your service's OAuth client ID |
BIO_CLIENT_SECRET | Builder (auto) | Your service's OAuth client secret |
BIO_ID_URL | Builder (auto when bio-id is internalDependency) | Bio-ID base URL |
// ❌ WRONG: HS256 with shared secret
import { verifyToken } from '@insureco/bio'
const payload = verifyToken(token, process.env.JWT_SECRET) // throws on RS256 tokens
// ❌ WRONG: JWT_SECRET env var — not used with RS256
JWT_SECRET=some-secret-value
// ❌ WRONG: BIO_ID_BASE_URL — renamed to BIO_ID_URL
BIO_ID_BASE_URL=https://bio.tawa.insureco.io
// ✅ CORRECT
import { verifyTokenJWKS } from '@insureco/bio'
const payload = await verifyTokenJWKS(token)
Last updated: February 28, 2026