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.

Info

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.

Info

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.
  • Authorization cookie — 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:

userPrincipalstringrequired

A stable user identifier — typically an email. Used as the partition key for sessions, messages, prompts, OAuth tokens, and quotas.

accountDiscriminatorstringrequired

A tenant-level identifier. Scopes everything below the user level.

expnumberrequired

Standard expiry claim. Tokens are refreshed transparently via the stored refresh token; you don't need to handle expiry on the client.

Note

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.

Warning

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.

Note

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 configurationtab

Global 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.

Tenantstab

Add, 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.

Productstab

The product catalog — the keys you pass as customizations.product when creating a session. Tracks current and latest versions, and per-product admin assignments.

Quotatab

Per-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.