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/card-present/transactions/sync, POST /api/v1/card-present/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:activateCreate or manage merchant onboarding/provisioning 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
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
Agent onboarding/MID management clientmerchant:activate, credential-profiles:write
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, 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, and transactions; read settlement/reconciliation evidence and request a location-scoped manual close through the Management BFF
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

Settlement and reconciliation GET routes expose historical evidence. The exception is the location-scoped Management BFF POST /api/v1/settlements/manual-close, which is annotated @RequireManagementPermission(REPORTS_READ), requires the portal's second confirmation, and forwards to Processing as the pinned Management service identity. No public settlement mutation scope exists: batch:manage is not defined, and admin:* does not add retry, force-close, or adjudication routes.

SETTLEMENT_REPAIR_WRITE is a separate platform-only permission for the controlled unmatched-settlement repair paths. It does not authorize manual close, retry, force-close, or adjudication.

Fraud transaction-detail score display is a read-only projection. The organization-scoped FRAUD_DECISIONS_READ permission gates the Management BFF; the Processing fraud query route is internal service-to-service only. A score is returned/displayed only for the exact organization, location, and transaction, and only as score, status, and model version. Missing, insufficient, unknown, malformed, or mismatched data is unavailable; raw fraud evidence and shopper-facing exposure are not authorized.

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

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
GET/api/v1/settlements, /api/v1/settlements/batches, /api/v1/settlements/batches/{gatewayBatchId}, /api/v1/settlements/{batchId}, /api/v1/settlements/{batchId}/transactions@RequireScope("reports:export"); tenant/location checks apply, and Management proxy calls use the configured internal caller
POST/api/v1/internal/processor-settlement/manual-closeROLE_INTERNAL_SERVICE; configured Management caller allowlist; verified forwarded portal operator; Idempotency-Key must equal the body key
GET/api/v1/internal/processor-settlement/unmatched, /api/v1/internal/processor-settlement/unmatched/{rowId}ROLE_INTERNAL_SERVICE; configured unmatched caller; explicit feature gate
POST/api/v1/internal/processor-settlement/unmatched/{rowId}/convert, /api/v1/internal/processor-settlement/unmatched/{rowId}/mark-externalROLE_INTERNAL_SERVICE; configured unmatched caller; explicit feature gate; public BFF additionally requires platform-only SETTLEMENT_REPAIR_WRITE
GET/api/v1/internal/organizations/{organizationId}/fraud-decisions*ROLE_INTERNAL_SERVICE; organization/location/transaction scope is rechecked by Processing

Card-Present Service

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

Management Service

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

MethodPathAuth
POST/api/v1/organizations/{organizationId}/locationsagent_user or higher within the owning agent subtree; organization_admin within its organization
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
GET/api/v1/settlementsREPORTS_READ, scoped to the requested merchant/location
POST/api/v1/settlements/manual-closeREPORTS_READ, exact authorized location; portal second confirmation; Management BFF forwards to the pinned Processing caller
GET/api/v1/settlements/batchesREPORTS_READ, exact authorized organization/location
GET/api/v1/settlements/batches/{gatewayBatchId}REPORTS_READ, exact authorized organization/location
GET/api/v1/settlements/{batchId}REPORTS_READ, exact authorized settlement merchant/location
GET/api/v1/settlements/{batchId}/transactionsREPORTS_READ, exact authorized settlement merchant/location
GET/api/v1/settlements/unmatched, /api/v1/settlements/unmatched/{rowId}REPORTS_READ, support-safe and organization/location scoped
POST/api/v1/settlements/unmatched/{rowId}/convert, /api/v1/settlements/unmatched/{rowId}/mark-externalSETTLEMENT_REPAIR_WRITE (platform-only) plus explicit admin/super_admin role; controlled feature gate
GET/api/v1/organizations/{organizationId}/fraud-decisions (with optional locationId and transactionId)FRAUD_DECISIONS_READ, exact organization scope; transaction-detail score projection is exact organization/location/transaction
GET/api/v1/organizations/{organizationId}/fraud-decisions/overviewFRAUD_DECISIONS_READ, exact organization scope
GET/api/v1/organizations/{organizationId}/fraud-decisions/top-rulesFRAUD_DECISIONS_READ, exact organization scope
GET/api/v1/organizations/{organizationId}/fraud-decisions/recentFRAUD_DECISIONS_READ, exact organization scope
GET/api/v1/organizations/{organizationId}/fraud-decisions/{decisionId}FRAUD_DECISIONS_READ, exact organization scope
GET/api/v1/organizations/{organizationId}/fraud-decisions/{decisionId}/related-activityFRAUD_DECISIONS_READ, exact organization scope

Management Service (OAuth exceptions)

MethodPathScope
POST/api/v1/organizationsorganizations:write, admin:*
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/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/jobsmerchant:activate
GET/api/v1/provisioning/jobs/{id}merchant:activate