Security and Authorization
This page covers the shared client responsibilities that apply before and after calling protected endpoints.
Base URL
The API exposes partition-specific base URLs for the active environment. Clients should use the API host presented by the current documentation build and avoid hard-coding a specific partition unless the workflow explicitly requires it.
Authentication
The platform uses Cognito-backed authentication for browser and application clients. In practice, clients should expect:
- Bearer JWT authentication for user-facing API calls
- Short-lived tokens for specialized shell flows
- Optional MFA depending on the user’s account configuration
Tokens may be issued before initial account setup is complete so the user can
finish Cognito-side onboarding. Until email is verified and either SMS/phone,
TOTP setup, or federated identity-provider authentication is complete, normal
platform APIs may reject the token with 401_116 Unauthorized_IncompleteAccountSetup.
For browser-driven sign-in, follow the endpoint-specific contracts in the
Security > OAuth section of the sidebar.
Access Token Claims
ĀYŌDÈ user-facing APIs expect a Cognito access token. In addition to standard Cognito claims, the platform pre-token trigger adds the following claims to the access token when source values are available:
| Claim | Meaning |
|---|---|
email |
User email address copied into the access token for API use. |
email_verified |
Cognito email verification flag. |
phone_number |
User phone number, when present. |
phone_number_verified |
Cognito phone-number verification flag. |
preferred_username |
Cognito preferred username, when present. |
ayode:userEID |
Platform user external identifier. This claim is absent until the platform has propagated the user EID to Cognito and the user has refreshed the JWT. |
ayode:email_verified |
Platform-computed email setup status. |
ayode:phone_number_verified |
Platform-computed phone/SMS setup status. SMS MFA setup can satisfy this claim even when Cognito has not separately marked the raw phone number as verified. |
ayode:totp_verified |
Platform-computed TOTP setup status. |
ayode:is_federated_user |
Whether the user authenticated through a federated identity provider. |
ayode:account_setup_complete |
Whether the user has completed the account setup requirements used by protected platform APIs. |
The access token also receives the ayode.institute/api.user scope. Clients
should treat the ayode:* claims as platform claims and refresh the token before
assuming newly established platform state is present. Chat SSE routing, for
example, requires ayode:userEID; if GET /v1/chat/events returns 404 with
no recipient identity, refresh the JWT and reconnect.
Sample Authorization header:
Authorization: Bearer eyJraWQiOiJleGFtcGxlIiwiYWxnIjoiUlMyNTYifQ.eyJ0b2tlbl91c2UiOiJhY2Nlc3MiLCJzY29wZSI6ImF5b2RlLmluc3RpdHV0ZS9hcGkudXNlciIsInN1YiI6ImV4YW1wbGUtY29nbml0by1zdWIiLCJjbGllbnRfaWQiOiJleGFtcGxlLWNsaWVudCIsInVzZXJuYW1lIjoiZXhhbXBsZS11c2VyIiwiZW1haWwiOiJzdHVkZW50QGV4YW1wbGUuY29tIiwiZW1haWxfdmVyaWZpZWQiOiJ0cnVlIiwicGhvbmVfbnVtYmVyIjoiKzE1NTU1NTUwMTAwIiwicGhvbmVfbnVtYmVyX3ZlcmlmaWVkIjoiZmFsc2UiLCJwcmVmZXJyZWRfdXNlcm5hbWUiOiJzdHVkZW50LW9uZSIsImF5b2RlOnVzZXJFSUQiOiIwMDAwMDAwMC0wMDAwLTAwMDAtMDAwMC0wMDAwMDAwMDAwMDAiLCJheW9kZTplbWFpbF92ZXJpZmllZCI6InRydWUiLCJheW9kZTpwaG9uZV9udW1iZXJfdmVyaWZpZWQiOiJ0cnVlIiwiYXlvZGU6dG90cF92ZXJpZmllZCI6ImZhbHNlIiwiYXlvZGU6aXNfZmVkZXJhdGVkX3VzZXIiOiJmYWxzZSIsImF5b2RlOmFjY291bnRfc2V0dXBfY29tcGxldGUiOiJ0cnVlIn0.signature
Decoded payload shape:
{
"token_use": "access",
"scope": "ayode.institute/api.user",
"sub": "example-cognito-sub",
"client_id": "example-client",
"username": "example-user",
"email": "student@example.com",
"email_verified": "true",
"phone_number": "+15555550100",
"phone_number_verified": "false",
"preferred_username": "student-one",
"ayode:userEID": "00000000-0000-0000-0000-000000000000",
"ayode:email_verified": "true",
"ayode:phone_number_verified": "true",
"ayode:totp_verified": "false",
"ayode:is_federated_user": "false",
"ayode:account_setup_complete": "true"
}
OAuth Login Paths
Two distinct OAuth entrypoints exist:
/v1/auth/loginis the platform-managed login initiator. It always uses the backend OAuth client and the platform callback endpoint.- Direct Cognito hosted UI login is the supported path for development tools that need a local loopback callback.
/v1/auth/login does not accept a caller-supplied redirect URI and is not a
generic localhost handoff endpoint.
Development Loopback OAuth
In the development silo, developer tools such as Postman or IDE HTTP clients
may complete OAuth directly against the Cognito hosted UI by using the public
web client with authorization code flow and PKCE.
Approved exact-match loopback callback and logout URIs:
http://localhost:3000/callbackhttp://127.0.0.1:3000/callbackhttp://localhost:5173/callbackhttp://127.0.0.1:5173/callbackhttp://localhost:8555/callbackhttp://127.0.0.1:8555/callback
Production does not permit any loopback callback or logout URI.
Realm Assertion
Many endpoints require the client to assert the acting realm. Supported headers include:
X-Ayode-Asserted-Realm-EIDX-Ayode-Asserted-Realm-URNX-Ayode-Asserted-Realm-Path
Only one assertion header should be sent on a given request.
Authorization Model
Authorization is privilege-based, scope-aware, and predicate-aware.
Important concepts:
- Privilege actions describe what operation is being requested.
- Allowed scopes describe where that privilege applies.
- Privilege predicates describe an additional relationship that must be true for the request being authorized.
- The asserted realm determines the context used for privilege evaluation.
Common scope patterns:
selfrealm- broader hierarchy-aware scopes where explicitly documented
Each privilege is evaluated as an action, scope, and predicate tuple. A
privilege written as /teams/update{realm} is equivalent to
/teams/update{realm | ⊤}: the action and scope must match, and the predicate
⊤ is always satisfied. The predicate ⊥ is also valid, but it is never
satisfied and therefore never authorizes a request.
Predicate expressions model business relationships. Examples include:
p ∈ members(team(eid))p ∈ managers(team(eid))p ∈ invitors(teamMembershipRequest(eid))p ∈ invitees(teamMembershipRequest(eid))p ∈ applicants(teamMembershipRequest(eid))p ∈ approvers(teamMembershipRequest(eid))p ∈ members(team(eid)) ∧ p ∉ managers(team(eid))
The variable p is the authenticated principal. A term such as team(eid)
means that the endpoint being authorized must provide the team identifier for
the request. The predicate catalog exposes those requirements through
requiredContextVariableNames; for example, predicates over team(eid) require
teamEID, and predicates over teamMembershipRequest(eid) require
teamMembershipRequestEID.
Clients do not send a generic authorization-context object. Context values come from the endpoint contract being executed, such as a path parameter, request body field, or server-resolved resource relationship. If an endpoint uses a predicate but cannot provide every required context variable, authorization fails explicitly instead of silently falling back to action-and-scope-only authorization.
At request time, authorization checks proceed in order:
- Match the requested privilege action.
- Verify that the privilege scope applies to the asserted realm and target.
- Match the privilege predicate expression assigned to the grant.
- Evaluate the predicate using the endpoint-specific context values.
Failure responses remain diagnosable. A denied request may reflect a missing privilege, invalid scope, missing predicate assignment, missing required context, or a predicate that evaluated to false.
Use GET /v1/privilege-predicates to list the predicate catalog, including
each predicate URN, display expression, required context variables, and
localized display name. Use the predicate URN as predicateExpressionURN when
creating or updating v2 privileges.
Response Envelope
Most successful JSON responses are wrapped in a shared envelope plus operation data payload.
Expect envelope metadata such as:
- request identifiers
- paging metadata where relevant
- rate-limit metadata where relevant
- link metadata and version metadata when applicable
Permission Recipes
Two recurring permission-check patterns are especially important for client integrators:
- Self-scope operations, such as reading or updating your own profile
- Realm-scope operations, such as browsing another user’s content within an authorized realm
If behavior is unexpected, verify both the asserted realm and the effective privilege scope.
Verification and Troubleshooting
Useful troubleshooting practices:
- Confirm the asserted realm matches the intended target realm.
- Inspect the response envelope for rate-limit and version metadata.
- Use privilege-matrix-style endpoints where available to verify that a user actually has the expected grant.
- Distinguish authentication failures (
401) from authorization failures (403) and business conflicts (409).
For the generated cross-endpoint privilege summary, see Endpoint Privileges Map.
Paging
List endpoints commonly use page-number plus items-per-page semantics. Clients should treat paging fields as part of the shared response contract and avoid assuming that a single page contains the full dataset.
Where To Go Next
- Platform Concepts for global rules such as rate limiting and lifecycle policy.
- Shell Access for the shell-specific security flow.