---
schema: "agents-md/1.0"
ai_agents_docs_site_root: "https://agents.1health.io/public/demo/api/"
rest_api_root: "/authentication"
path_to_agent_file: "https://agents.1health.io/public/demo/api/authentication/agents.md"
kind: "auth"
parent: "https://agents.1health.io/public/demo/api/agents.md"
html: "https://agents.1health.io/public/demo/api/authentication/index.html"
source_version: "43d07c0335624b7661c2872315a5f184e8c3d832"
generated_at: "2026-08-27T03:35:58.960659+00:00"
last_reviewed: "2026-08-27"
---

# Authentication Quickstart

This page is the single, stable entry point an AI agent (or a human developer) needs to go
from a cold start — nothing but demo credentials — to a valid bearer token usable against the
1Health BoCore API. It is hand-authored and reviewed on a recurring cadence (see the
`last_reviewed` date in this file's own front matter); unlike every other file in this tree, it
is not regenerated from source on every pipeline run.

## Registration & Credential Acquisition

There are two distinct, unrelated registration concepts in this system. Do not conflate them:

- **OAuth2 *client* registration** (creating a new `client_id`/secret pair) is a real HTTP
  endpoint (`RegisteredClientManagementController.createRegisteredClient`), but it is
  administrator-only — gated by a class-level `@PreAuthorize("isUserMultiTenantAdministrator()")`.
  A developer or agent cannot self-register a new OAuth2 client, and in normal usage never needs
  to: the system exposes one shared, pre-registered **public client** (see "Token Endpoint &
  Grant Types" below) that every developer/agent authenticates through.
- **End-user *account* registration** is public and self-service: `POST /user/public/register-user`
  (`UserController.registerWithRoles`) carries its own method-level `@PreAuthorize("permitAll")`
  (overriding the controller's class-level admin-only default), and `/user/public/**` is
  additionally allow-listed at the security-filter-chain level. This is the endpoint a
  developer/agent actually calls to obtain initial credentials: register a user account here,
  then run the authorization_code(+PKCE) flow (below) as that user against the shared public
  client.

`UserController` also exposes `POST /user/public/reset-password`,
`POST /user/public/verify-email/{userId}/{identifier}`, and
`POST /user/public/verify-password-update/{userId}/{identifier}`, all similarly `permitAll`.

**[NEEDS SECURITY REVIEW]** The demo sandbox's specific credential-provisioning process is not
confirmed: is there a pre-seeded demo user/tenant an agent can use directly, or does an agent
call the public registration endpoint above against the demo environment to create its own demo
account? Do not assume either answer — confirm with the team before treating this as settled.

**[NEEDS SECURITY REVIEW]** The shared public OAuth2 client's `client_id` value for the demo
environment is environment configuration (`AuthServerClientConfig.publicClientId`, bound via
`@ConfigurationProperties(prefix = "auth.client-config")`), not a literal defined anywhere in
source. It is not stated here because it varies per deployment — obtain the demo environment's
real value from the team rather than guessing or reusing another environment's value.

## Token Endpoint & Grant Types

The authorization server is a genuine mixed public/confidential-client Spring Authorization
Server deployment, confirmed directly against `AuthorizationServerConfig.java`:

- **`authorization_code` with PKCE**, for the shared public client. PKCE is enforced via
  `.clientSettings(ClientSettings.builder().requireAuthorizationConsent(true).requireProofKey(true).build())`
  on the `publicClient` `RegisteredClient` bean — `requireProofKey(true)` is Spring Authorization
  Server's actual PKCE-enforcement flag. This is the grant type a developer/agent uses after
  registering a user account (above): drive the authorization_code+PKCE flow as that user against
  the shared public client to obtain the first access/refresh token pair.
- **`client_credentials`**, registered only to the confidential/private client — not available to
  the public client. Not the grant a typical developer/agent flow uses; relevant only for
  service-to-service integrations holding a confidential client secret.
- **`refresh_token`**, available to both client types (a custom
  `CustomPublicClientRefreshTokenAuthenticationProvider` exists specifically so refresh tokens can
  be issued to public clients too). See "Token Refresh" below.

Client authentication at the token endpoint supports both client-secret (`ClientSecretAuthenticationProvider`,
confidential clients) and JWT client assertion (`JwtClientAssertionAuthenticationProvider`).

**[NEEDS SECURITY REVIEW] Endpoint paths (inferred, not directly confirmed):**
`AuthorizationServerConfig.authorizationServerSettings()`
builds `AuthorizationServerSettings.builder().issuer(issuerUrl).build()` with no custom
endpoint-path overrides found anywhere in the class — meaning the deployment almost certainly
uses Spring Authorization Server's own framework defaults: `/oauth2/authorize`, `/oauth2/token`,
`/oauth2/jwks`, `/.well-known/oauth-authorization-server`. This is corroborated at the
filter-chain level (`/oauth2/token`, `/login`, `/.well-known/**` are all permitted unauthenticated
in the `oauth2` module's own `SecurityConfig`), but has not been confirmed against a live server
response. Do not treat these paths as certain until confirmed against a live deployment.

**[NEEDS SECURITY REVIEW]** The `publicClient` bean also allows `AuthorizationGrantType.JWT_BEARER`,
but no token-endpoint provider for that grant type was found registered alongside the three
above. Do not rely on JWT-bearer support until this is confirmed either way.

## Token Refresh

Use the standard OAuth2 `refresh_token` grant against the token endpoint (`/oauth2/token` per the
framework-default path above), supplying the refresh token issued alongside your original access
token. As noted above, refresh tokens are issued to — and can be redeemed by — the shared public
client as well as confidential clients; this is not client-credentials-only behavior.

## Required Headers

Every authenticated request must carry:

```
Authorization: Bearer $TOKEN
```

**[NEEDS SECURITY REVIEW]** As of this page's drafting, every currently-generated endpoint file's
Authorization section states only the blanket line "Bearer JWT required for all endpoints."
regardless of an endpoint's actual auth requirement, and does not yet link back to this page —
confirmed directly against the then-current generator's default text and output tree (predates
`scripts/pipeline.py`; re-verify against its actual reconciled output before relying on this note).
The per-endpoint "Bearer JWT required." / "None (public)." distinction and
the back-link to this page are this project's intended target state (tracked separately, not yet
implemented as of this writing) — not yet the present, verified behavior of the generated output.
Do not assume an endpoint's documented Authorization line reflects its real auth requirement until
that work lands.

## Tenancy & Organization Semantics

The user model is genuinely multi-tenant: `User.tenantIds: Long[]` and `User.getTenantContext()`
confirm a real multi-tenant data model. No dedicated HTTP header (e.g. `X-Tenant-Id`) exists for
selecting a tenant — tenancy is handled through dedicated endpoints instead:

- During the federated-login redirect flow, `LoginController` references a `TENANT_ID_PARAM`
  query parameter.
- Post-token, an authenticated user can switch their own active tenant via
  `POST /tenant/switch-tenant` (`TenantController.switchTenant`, taking `tenantId`, `userId`, an
  optional `revokeToken` flag defaulting `true`, and a JWT-bearing request body). This endpoint's
  own method-level `@PreAuthorize("isAuthenticated()")` deliberately overrides the controller's
  class-level admin-only default — any authenticated user can call it for their own account, not
  just a multi-tenant administrator.

**[NEEDS SECURITY REVIEW]** Which tenant a demo account defaults to, and whether the demo sandbox
flow requires an explicit call to `/tenant/switch-tenant` at all, is not confirmed — depends on
how the demo account is provisioned (see "Registration & Credential Acquisition" above).

## Demo Sandbox Access

**[NEEDS SECURITY REVIEW]** No source document consulted for this page (the `1h-ng-bo-core-be`
source, the PRD, the addendum, or the architecture spine) states the demo sandbox's own
credential-provisioning process. Whether a demo user account already exists, or must be created
via `POST /user/public/register-user` against the demo environment specifically, is a product/
operational decision, not something this page invents. Do not assume a specific demo login
exists until this is confirmed.

**[NEEDS SECURITY REVIEW]** Whether MFA applies to the demo sandbox's own registration/login
flow is not confirmed. Two related but distinct MFA surfaces exist in the backend:
`GET /mfa/code` (the `oauth2` module's own `MfaController`) and the public, unauthenticated
`/v2/mfa/config` / `/v2/mfa/send-code` endpoints (`bo-core`'s `MfaResource`, backed by
`SecurityConfig.PUBLIC_RESOURCE_PATTERNS`) — these are two distinct endpoints sharing related
model types via a common Feign interface, not the same endpoint. Their unauthenticated status
means MFA config/code-send calls can happen before a token exists, consistent with MFA being a
pre-token login step, but whether the demo sandbox's flow actually exercises MFA is unconfirmed.

This draft satisfies the structural coverage check (all six required topics present above) but
is **not** launch-ready — every `[NEEDS SECURITY REVIEW]` marker on this page must be closed by
the recurring security-review sign-off process before this page (and the credentials it
describes) are relied on for a real, unattended agent run.

## Navigation
Parent: .. · Site guide: ../agents.md
