Authentication
The agent backend authenticates every non-public request with an OAuth-issued
JWT. There is no API-key fallback — the only accepted credentials are a
Bearer token or an Authorization cookie carrying a JWT signed by a
registered OAuth provider.
Earlier versions of the platform supported X-API-KEY / X-API-USER
headers. Those have been removed from the middleware and will no longer
authenticate. Use the OAuth flow below.
The OAuth flow
Authentication uses a standard OAuth 2 authorization-code flow. The identity provider is pluggable — whichever provider is registered for your deployment runs the actual login screen.
Begin the flow
Direct the user to /v1/oauth/authenticate with their account
discriminator and user principal as query parameters. The server records
who is starting the flow and redirects the browser to the configured
OAuth provider.
The provider signs the user in
The user authenticates with the provider. On success, the provider
redirects back to the agent's /v1/oauth/callback with an authorization
code.
Token storage
The callback exchanges the code for tokens, stores them keyed by the user principal, and returns the Bearer access token to the caller.
The server stores both the access token and the refresh token. When the access token expires, the next API call triggers a transparent refresh on the server side — clients don't manage expiry.
Using your token
Once you have a JWT from the OAuth flow, every authenticated endpoint accepts the token in one of two places:
Authorization: Bearer <jwt>header — preferred for programmatic use.Authorizationcookie — used when the caller is a browser surface whose host platform sets the cookie via SSO. Useful for in-app extensions.
Both target the same JWT-verifying middleware on the Inference-Server.
curl "$BASE_URL/v1/sessions" \
-H "Authorization: Bearer $TOKEN"Required JWT claims
The middleware verifies the token's signature and inspects the claims. The following are load-bearing:
userPrincipalstringrequiredA stable user identifier — typically an email. Used as the partition key for sessions, messages, prompts, OAuth tokens, and quotas.
accountDiscriminatorstringrequiredA tenant-level identifier. Scopes everything below the user level.
expnumberrequiredStandard expiry claim. Tokens are refreshed transparently via the stored refresh token; you don't need to handle expiry on the client.
Both userPrincipal and accountDiscriminator are deliberately platform-
neutral names. Their values depend on whichever provider is registered
— they might be email + tenant ID, employee ID + org code, or whatever
fits your provider's schema.
Pluggable OAuth providers
The OAuth integration goes through a provider abstraction
(@rfsmart/oauth-consumer). Each provider knows how to:
- Redirect users to the right login screen.
- Validate JWTs signed by that provider.
- Exchange and refresh tokens.
Adding a new provider means registering it with that library and pointing the agent's env config at it — no changes to the agent's route code.
Service-to-service calls
Internal services that call the agent backend (for example, an inference
worker recording a tool result) use a service identity JWT instead of
a user-scoped one. Service tokens use userPrincipal values prefixed with
_svc:, e.g. _svc:inference-server. They're minted via
mintOnBehalfOf from @rfsmart/oauth-consumer with the same signing key
and a short (≈30 s) expiry.
Service tokens have elevated privileges and bypass per-user quota enforcement. They're meant for trusted infrastructure callers only — never issue them to user-facing clients.
OAuth Management
Platform-wide configuration — OAuth providers, tenant accounts, products,
and quota settings — is not edited by hand in AWS. It lives behind a
dedicated admin tool: OAuth Management
(Tools/rfs-ai-oauth-management). It's an internal Next.js app used by
the RFS AI platform team to manage the configuration that the
Authentication-Server reads at runtime.
This tool is internal-only. Application developers don't need to run it; it's listed here so you know where platform-wide settings come from. Talk to the platform team if you need a new tenant added or an OAuth provider registered.
What it manages
A single JSON secret in AWS Secrets Manager
({env}/rfs-ns-ai-agent) holds all of the following — the tool edits it
atomically with a visual form:
OAuth configurationtabGlobal signing keys (StateEncryptionKey, JWTSecret) plus per-account
OAuth credentials (client_id, client_secret, redirect_uri,
state_nonce). These are what @rfsmart/oauth-consumer reads at
request time to validate inbound JWTs and complete the authorization-code
exchange.
TenantstabAdd, remove, or import NetSuite tenant accounts. Each tenant has an admin email list, the products it's entitled to, and flags for internal/dev access.
ProductstabThe product catalog — the keys you pass as customizations.product
when creating a session. Tracks current and latest versions, and
per-product admin assignments.
QuotatabPer-tenant, per-environment quota limits in cents. The Inference-Server reads these to enforce the cost ceiling described earlier.
Environment switching
The tool reads ?env=dev|test|prod from the URL and points at the
matching secret. Each environment has its own copy — changes in dev
don't affect prod.
Auth into the tool itself
The tool authenticates to AWS using the operator's local AWS credentials
(typically an SSO profile). It does not have its own login screen — if
you can aws secretsmanager get-secret-value against the target
environment, you can use the tool.