Skip to main content

Authorization

Peak Gateway uses two authorization models: OAuth2 scopes for API clients (machine-to-machine) and Firebase roles for portal users (human users). Gateway tenant targeting is organization/location based.

OAuth2 Scopes (API Clients)

API clients authenticate via OAuth2 client_credentials flow with the auth service, then use Bearer tokens with scoped access.

ScopeDescriptionEndpoints
txn:processProcess scoped payment transactions on API-client surfacesCard-not-present transaction APIs on online-txn; processing endpoints that explicitly use OAuth scope checks
txn:syncSync terminal-approved card-present transactions and refundsPOST /api/v1/transactions/sync, POST /api/v1/transactions/sync/refund on card-present
session:createCreate online checkout sessions and hosted payment formsPOST /api/v1/checkout/sessions, POST /api/v1/hosted/* on online-txn
merchant:activateActivate payment processors for locationsPOST /api/v1/locations/{id}/activate-transit on management
provision:requestRequest and manage merchant onboarding jobs/api/v1/provisioning/jobs/* on merchant-onboarding
organizations:readRead organization and org-scoped location recordsGET /api/v1/organizations/{organizationId}/locations/* on management
organizations:writeCreate organizations and create, provision, or update organization-bound locationsPOST /api/v1/organizations, POST/PUT /api/v1/organizations/{organizationId}/locations/* on management
batch:manageManage settlement batches (retry stuck, force-close)POST /api/v1/settlements/{id}/retry, POST /api/v1/settlements/{id}/force-close on processing
products:read / products:writeRead / manage storefront products, prices, and promotion codes/api/v1/products/*, /api/v1/prices/*, /api/v1/promotion-codes/* on online-txn
coupons:read / coupons:writeRead / manage storefront coupons/api/v1/coupons/* on online-txn
customer:read / customer:writeRead / manage stored storefront customers/api/v1/customers/* on online-txn
shipping:read / shipping:writeRead / manage storefront shipping configuration/api/v1/shipping/config on online-txn
admin:*Wildcard admin access, grants all scopesAll endpoints across all services

admin:* controls scopes, not portal roles. Organization targeting is independent of scopes:

  • organization_ids binds an OAuth client to specific organizations
  • an empty organization_ids set is treated as a platform-admin/global client and is not available through customer-managed OAuth client APIs

Token claims use snake_case (organization_ids), while management API payloads and portal DTOs use camelCase (organizationIds) for the same fields.

An OAuth client with admin:* and organization_ids=["org_123"] can access organization-scoped management endpoints for org_123 only. Customer-managed OAuth clients cannot request admin:*, organizations:write, platform-admin ownership, global access, or unbound all-organization access.

Management authorizes OAuth tenant access from signed token claims only. It does not call auth during request authorization to recover stored client organization bindings. After changing a client's organization bindings, mint a new access token before calling management; already-issued tokens without the required organization_ids / organizationAccess claims are rejected until they expire or are replaced.

Typical Client Configurations

Client TypeScopes
POS Terminal sync clienttxn:sync
E-Commerce Integrationsession:create, txn:process
Device Management Systemprovision:request
Management/support onboarding clientorganizations:read, organizations:write
Admin Dashboardadmin:*

Customer-Managed OAuth Clients

Merchant portal users can manage their own machine clients through the customer OAuth client surface:

MethodPathPurpose
GET/api/v1/customer-oauth-clientsList customer-owned clients for the caller's organizations
POST/api/v1/customer-oauth-clientsCreate an organization-bound customer client
GET/api/v1/customer-oauth-clients/{clientId}Get an owned client
PUT/api/v1/customer-oauth-clients/{clientId}/scopesUpdate allowed scopes
POST/api/v1/customer-oauth-clients/{clientId}/secret:rotateRotate the client secret
DELETE/api/v1/customer-oauth-clients/{clientId}Revoke the client

Customer-managed clients are persisted in the canonical oauth_clients registry with owner_type=CUSTOMER. Their organization_ids are auto-bound to the caller's allowed organization set or to a requested subset. Reads, updates, rotations, and revocations require both owner_type=CUSTOMER and organization ownership.

Customer-created clients can request only customer-safe scopes: txn:process, txn:sync, session:create, batch:manage, gift_cards:read, gift_cards:write, and organizations:read.

This surface is separate from:

  • platform-admin OAuth client management at /api/v1/oauth-clients, which can manage all registry rows
  • auth-service admin management at /api/v1/clients
  • customer_identities, which links end-customer identity-provider subjects and does not store Gateway OAuth client credentials

Registering a Platform OAuth Client

  1. Admin Portal → OAuth Clients → Register
  2. Assign scopes matching the client's use case
  3. Bind the client to organization IDs when it should be restricted to specific customer organizations
  4. Note the client_id and client_secret
  5. Client calls POST /auth/oauth2/token with grant_type=client_credentials on the public gateway domain

Portal Roles (Firebase Auth)

Portal users authenticate via Firebase Auth (Google SSO or SAML). Roles are hierarchical, higher ranks include all lower rank permissions.

RoleRankDescription
super_admin0Full access including SAML SSO configuration and user deletion
admin1Full operational access across organizations, locations, users, transactions, and settlements
location_admin2Location-scoped operator who manages assigned organization/location settings, users, transactions, and subscriptions
location_user3Location-scoped portal user with read-heavy access to assigned organization/location data
readonly4Read-only access to assigned organization/location data

Token model

Firebase custom claims carry direct grants only (anchors) — never an expanded, pre-computed access list:

  • role: platform tier (super_admin, admin), if any
  • organizationAccess: explicit per-organization role entries
  • locationAccess: explicit per-location role entries
  • agentAccess: explicit per-agent (ISO) role entries — deliberately compact; an agent's descendant subtree is never embedded in the token

Every wider scope — an organization's full location set, an agent's descendant subtree — is expanded server-side from these anchors on each request, by consulting the database. A token never carries a derived or promoted grant.

A user can hold at most 25 direct locationAccess entries. This cap keeps the worst-case claims payload comfortably under Firebase's 1000-byte custom-claim limit; a user needing broader reach takes an explicit organization-level grant instead, which is a single claim entry regardless of how many locations the organization has. Requests that would exceed the cap are rejected at write time.

Revocation is bounded at the check interval (60 seconds by default): whenever a grant is revoked, downgraded, or a user is deactivated, the service rewrites custom claims and calls Firebase's refresh-token revocation for that user. Token signatures are verified locally on every request; the remote revocation lookup (tokensValidAfterTime + disabled flag) runs at most once per user per interval and is cached in between — so a revoked or disabled principal keeps working for at most one interval, the same bound as the server-side grant and subtree caches, without a Firebase round-trip on every request.

Authorization Annotations

Services enforce authorization via two primary authorization patterns:

  • @RequireScope(...): Used on controllers that serve API clients, including online-txn payment/session APIs, merchant-onboarding provisioning APIs, processing OAuth exception paths, and card-present terminal sync.
  • @RequireManagementPermission(...): Used on management controllers for portal users and backed by ManagementAuthorizer
  • @RequireRole(...): Used for portal/location-user service surfaces, including card-present sale/auth and role-protected processing operations.

OAuth clients are not promoted into Firebase-style portal roles. Merchant targeting comes from OAuth token merchant claims, while management permissions remain role-based unless an endpoint explicitly opts into OAuth scope checks.

The scope checks live in libs/security, while management permission enforcement lives under services/management/.../security.

Endpoint Authorization Matrix

Auth Service

MethodPathAuth
GET/healthPublic
POST/api/v1/clientsAdmin (super_admin, admin)
GET/api/v1/clientsAdmin (super_admin, admin)
POST/api/v1/clients/{clientId}/secret:rotateAdmin (super_admin, admin)
DELETE/api/v1/clients/{clientId}Admin (super_admin, admin)
GET/api/v1/meAuthenticated

Processing Service

Most processing controllers are role-protected internal/portal surfaces. OAuth-exposed processing paths are listed only when the controller declares @RequireScope(...).

MethodPathAuth
POST/api/v1/gift-cards/*gift_cards:write / gift_cards:read by operation
GET/api/v1/gift-cards/*gift_cards:read

Card-Present Service

MethodPathAuth
POST/api/v1/transactions/salelocation_user or higher
POST/api/v1/transactions/authlocation_user or higher
POST/api/v1/transactions/synctxn:sync
POST/api/v1/transactions/sync/refundtxn:sync

Management Service

Most management endpoints remain portal-role-based. The OAuth-exposed paths are the explicit exceptions below.

MethodPathMin Role
POST/api/v1/locationsadmin
GET/api/v1/locationsreadonly
PUT/api/v1/locations/{id}location_admin
POST/api/v1/saml-providerssuper_admin
GET/api/v1/audit-logadmin
GET/api/v1/customer-oauth-clientsorganization_admin/location_admin/readonly scoped to caller organizations
POST/api/v1/customer-oauth-clientsorganization_admin/location_admin scoped to caller organizations

Management Service (OAuth exceptions)

MethodPathScope
POST/api/v1/organizationsorganizations:write, admin:*
POST/api/v1/locations/{locationId}/activate-transitmerchant:activate
POST/api/v1/organizations/{organizationId}/locationsorganizations:write or admin:*
GET/api/v1/organizations/{organizationId}/locations/{locationId}organizations:read or admin:*
PUT/api/v1/organizations/{organizationId}/locations/{locationId}organizations:write or admin:*
GET/api/v1/organizations/{organizationId}/locations/by-external-id?externalId=...organizations:read or admin:*
POST/api/v1/organizations/{organizationId}/locations/online/provisionorganizations:write or admin:*
GET/api/v1/event-subscriptionsadmin:*
GET/api/v1/event-subscriptions/{subscriptionId}admin:*
POST/api/v1/event-subscriptionsadmin:*
PUT/api/v1/event-subscriptions/{subscriptionId}admin:*
DELETE/api/v1/event-subscriptions/{subscriptionId}admin:*

Online-Txn Service

MethodPathAuth
POST/api/v1/checkout/sessionssession:create
POST/api/v1/checkout/sessions/{id}/payPublic
POST/api/v1/tokenssession:create
POST/api/v1/api-keysMerchant auth
GET/api/v1/webhooks/eventsPublic
GET/POST/PATCH/api/v1/products/*, /api/v1/prices/*, /api/v1/promotion-codes/*products:read / products:write
GET/POST/PATCH/api/v1/coupons/*coupons:read / coupons:write
GET/PUT/api/v1/customers/*customer:read / customer:write
GET/PUT/DELETE/api/v1/shipping/configshipping:read / shipping:write

Merchant-Onboarding Service

MethodPathScope
POST/api/v1/provisioning/jobsprovision:request
GET/api/v1/provisioning/jobs/{id}provision:request