Authentication strategies
Every request your server forwards to Nebulas needs credentials. Which ones depends on who is using your site.
Which one should I use?
Section titled “Which one should I use?”| Your situation | Strategy | Browser needs a token? |
|---|---|---|
| Public website, marketing page, anonymous visitors | Service account (recommended) or service auth (ROP / client credentials) | No |
| Users already sign in with Auth0 on your site | Session | No (cookies) |
| Users sign in with Entra ID and calls should carry their identity | Session with Entra On-Behalf-Of | No (cookies) |
| Quick internal tool where people type a login | Browser ROP (example) | Yes |
For a chatbot or a contact form on a public website, pick a service strategy. Your server calls Nebulas as itself, and visitors don’t have to sign in.
No identity provider is involved. Your server signs its own short-lived JWT (RS256, 5-minute TTL) with a private key. Nebulas verifies it with the public key registered on a service account.
import { configureNebulasServer } from "@gnomondigital/nebulas-kit-core";
configureNebulasServer({ a2aUrl: process.env.A2A_URL, nebulasApiUrl: process.env.NEBULAS_API_URL, nebulasApiKey: process.env.NEBULAS_API_KEY, nebulasProjectId: process.env.NEBULAS_PROJECT_ID, nebulasAuthStrategy: "service_account", serviceAccount: { serviceAccountId: process.env.SERVICE_ACCOUNT_ID ?? "", keyId: process.env.SERVICE_ACCOUNT_KEY_ID ?? "", // One of the two: privateKeyPem: process.env.SERVICE_ACCOUNT_PRIVATE_KEY, privateKeyPath: process.env.SERVICE_ACCOUNT_PRIVATE_KEY_PATH, },});Setup: create the service account in Admin, give it a role in your organization, and register its public key. Nebulas takes the org from that role. See Service accounts for the steps.
privateKeyPem accepts \n escapes, so the key can sit on a single line
in your .env.
The server signs in as a dedicated Auth0 service user with the Resource Owner Password grant. It caches the token and adds it to each request.
configureNebulasServer({ a2aUrl: process.env.A2A_URL, nebulasApiUrl: process.env.NEBULAS_API_URL, nebulasApiKey: process.env.NEBULAS_API_KEY, nebulasProjectId: process.env.NEBULAS_PROJECT_ID, authProvider: "auth0", auth0: { domain: process.env.AUTH0_DOMAIN ?? "", clientId: process.env.AUTH0_CLIENT_ID ?? "", clientSecret: process.env.AUTH0_CLIENT_SECRET ?? "", audience: process.env.AUTH0_A2A_AUDIENCE, }, auth0ServiceUser: { username: process.env.AUTH0_ROP_USERNAME ?? "", password: process.env.AUTH0_ROP_PASSWORD ?? "", },});Auth0 setup:
- Create a confidential application with a client secret.
- Enable the Password grant on it.
- Create a database user to act as the service user.
- Check that the API audience matches
AUTH0_A2A_AUDIENCE.
The server gets an app token from Microsoft Entra ID, using either a client secret or a certificate.
configureNebulasServer({ a2aUrl: process.env.A2A_URL, nebulasApiUrl: process.env.NEBULAS_API_URL, nebulasApiKey: process.env.NEBULAS_API_KEY, nebulasProjectId: process.env.NEBULAS_PROJECT_ID, authProvider: "entra", entraService: { tenantId: process.env.ENTRA_TENANT_ID ?? "", clientId: process.env.ENTRA_CLIENT_ID ?? "", scope: process.env.ENTRA_A2A_SCOPE ?? "", // often api://<app-id>/.default clientSecret: process.env.ENTRA_CLIENT_SECRET, // Or certificate auth (preferred over the secret when set): certificatePath: process.env.ENTRA_CLIENT_CERTIFICATE_PATH, privateKeyPath: process.env.ENTRA_CLIENT_PRIVATE_KEY_PATH, },});Your users already sign in with @auth0/nextjs-auth0. The handler gets
their access token from the session, so each request carries the user’s
identity.
import "@/lib/nebulas";import { createA2AProxyHandler } from "@gnomondigital/nebulas-kit-core/nextjs";import { auth0 } from "@/lib/auth";
export const POST = createA2AProxyHandler({ auth0, audience: process.env.AUTH0_A2A_AUDIENCE, apiKey: process.env.NEBULAS_API_KEY,});The browser sends the session cookie automatically, so the React components
need no extra props. You can pass user={{ name, picture }} to show the
user’s avatar.
Service handlers at a glance
Section titled “Service handlers at a glance”With a service strategy, use the WithServiceAuth variants on Next.js. They
use whatever nebulasAuthStrategy you configured, and default to Auth0 ROP
(authProvider: "auth0") or Entra client credentials (authProvider: "entra").
| Route | Next.js handler | Express |
|---|---|---|
POST /api/a2a |
createA2AProxyHandlerWithServiceAuth() |
createA2AExpressRouter(express, { strategy }) |
GET /api/a2a/config |
handleA2AConfigNextJsGET(req, { getHeaders: createGetHeadersWithServiceAuth() }) |
(included in the router) |
/api/nebulas-surfaces/* |
createSurfaceHandlerWithServiceAuth() |
createNebulasSurfaceRouter(express, { apiKey }) |
If service auth fails, the handler returns 401 with a hint that names the
config block to check.
