Product documentation
Module map
Every module in the identity platform — the five servers, the federation-member connectors, and the shared core libraries and Spring Boot starters — plus the contributor extension points the starters expose.
Module map
oathy is a modular Maven build (Java 21, Kotlin, Spring Boot). This page inventories the
modules that make up the identity platform and how they layer. It reflects the repository as
it is today — the pure-CIAM Hub product after the verifiable-credentials surface, the demo
relying parties, the web front-ends, and the CLI were extracted to sibling repositories.
The console UI, the self-service BFF, and the
thorynCLI are the customer plane's clients and live in the siblingthoryn-webrepository. They are not part ofoathy. The verifiable-credentials / wallet product lives inthoryn-vc-broker. This map covers the identity platform's own modules.
Servers — the deployable services
| Module | Plane | Role |
|---|---|---|
servers/authorization-hub | Broker | The OAuth 2.0 / OIDC engine (Spring Authorization Server 7). Authorize, PAR, token, revoke, introspect, userinfo, device grant, DCR, logout, federation. Owns no user data. |
servers/federation-members/* | Identity | The identity sources — Thoryn's own Identity Service plus the per-provider connectors (below) |
servers/product-api | Customer | The customer self-service management API (resource server): tenant config, OAuth-client registration, federation, RBAC/FGA, audit, compliance |
servers/api-gateway | Customer | The public-internet ingress for product-api — TLS, WAF, edge rate limiting, bearer validation (Spring Cloud Gateway, fully reactive) |
servers/synthetic-monitor | (operations) | A Tier-3 deep-flow synthetic monitor (Kubernetes CronJob) that exercises real hub/identity OAuth flows in staging |
Federation members
A federation member is a customer-side identity source a tenant attaches. They divide into Thoryn's own IdP, external OIDC connectors, enterprise SAML, and directory/system connectors:
| Category | Modules |
|---|---|
| Thoryn's own IdP | identity-service — Thoryn-managed users, login UI, passkeys/MFA/magic-link, SCIM 2.0, account lifecycle |
| Reference / test IdP | dummy-member — prototype of the identity-service federation pattern |
| External OIDC connectors (standalone member modules) | google-workspace, apple, github-member — each carries behaviour a generic OIDC broker can't cover (Workspace hd + Cloud Identity Groups; Apple's ES256 client_secret JWT + private-relay email; GitHub's non-OIDC id_token synthesis) |
| In-hub OIDC federation (no standalone module) | Microsoft Entra ID, Okta, Google, and any plain-OIDC IdP are brokered in-process by the hub's FederationProvider runtime — EntraFederationProvider / OktaFederationProvider / GoogleFederationProvider / GenericOidcFederationProvider. SSO-746 removed the superseded standalone entra-id, okta, google, jumpcloud, onelogin, and linkedin connector modules |
| Enterprise SAML 2.0 | adfs, pingfederate, saml-member (generic SAML) |
| Directory / system connectors | ldap-connector, erp-connector |
identity-service is built on the shared Spring Authorization Server contributor wiring (the
sas-basic-starter); it is Thoryn's own OIDC provider and, because it acts as a federation
member to the hub, it lives under federation-members/ alongside the external connectors.
A note on runtime tiers
The services do not all use the same I/O model, which matters when reading the code:
api-gatewayis fully reactive (WebFlux / Spring Cloud Gateway) — it must never block the event loop, which is why its per-tenant issuer check is an in-memory snapshot (see Multi-tenancy).authorization-hubandproduct-apirun the servlet-tier Spring stack with a non-blocking R2DBC data layer; the hub additionally configures a JDBCDataSourcepurely so Flyway can run migrations at startup.identity-serviceruns on JDBC at runtime (it is the one service that legitimately does not use R2DBC).
Every reactive→blocking bridge in the codebase must carry an explicit Duration on its
.block(...) call — a CI guard enforces it — so a downstream blip cannot exhaust a worker
pool. That discipline is part of why the platform holds up under concurrent token issuance.
Core libraries — core/lib/*
Shared, service-agnostic building blocks:
| Module | Purpose |
|---|---|
common | Cross-cutting security and web primitives — TrustedTenantIssuers, the per-tenant JWKS decoder factories, OutboundUrlGuard, securedHubCookie, the RFC 9457 ProblemDetailsWriter |
signing | The signing seam: signing/common (the SigningStrategy interface) and signing/hashicorp-vault (the Vault/OpenBao Transit implementation) |
sas | Spring Authorization Server building blocks: sas/basic (federation filter + contributors), sas/federation, sas/step-up |
fapi | FAPI 1.0 / 2.0 building blocks (PAR, client profiles) |
federation | Shared federation primitives |
email / sms | Notification transports |
geoip | IP geolocation for adaptive / risk-based auth |
dsar | Data-subject-access-request (GDPR) primitives |
vault-kv | Vault/OpenBao KV access |
util | Shared utilities |
Spring Boot starters — core/starters/*
Auto-configuration modules that wire the libraries into a service with sensible defaults:
| Starter | Wires |
|---|---|
common-starter | The core/lib/common primitives (problem-details writer, security helpers) |
sas/basic-starter | The Spring Authorization Server filter chain, OIDC, and the federation authentication filter |
signing/signing-starter | The JwtEncoder factory (SasSigningEncoder) |
signing/signing-vault-transit-starter | The Transit-backed SigningStrategy + its re-authentication management |
fapi | The PAR endpoint and FAPI enforcement |
gateway | The reactive gateway security config, including multi-issuer token validation (GatewayMultiIssuerConfig) |
flyway-starter | Standalone Flyway migration wiring (JDBC alongside R2DBC) |
incident-starter | Incident/observability wiring |
actuator-api-docs-starter | Actuator + API-docs exposure |
The contributor extension points
The sas-basic-starter is intentionally plugin-based — a consuming service customizes the
authorization server without forking the starter, through three extension-point interfaces:
AuthorizationServerContributor— customize the OAuth2 authorization-server configuration.AuthenticationProviderContributor— add customAuthenticationProviderbeans to the token endpoint.HttpSecurityContributor— extend the HTTP security filter chain.
This is how the hub layers its many token customizers, filters, and providers on top of a shared core: they are contributed beans, discovered by the starter, not edits to it. Adding a capability to the broker is usually a new contributor, not a change to the framework wiring.
Source: core/starters/sas/basic-starter/, docs/modules/ROOT/pages/design.adoc.
Dependency management — bom/
bom/ is the platform's bill of materials — it pins dependency versions (including the
deliberate Jackson 2.x LTS pin, imported before Spring Boot's own BOM so it wins version
resolution) so every module builds against one consistent, security-reviewed set.
Layering rules
The module structure encodes the platform's layering discipline:
- Controllers are thin — no business logic; a controller injecting a repository directly
is a layering violation. Data access is always
Controller → Service → Repository. - Module boundaries are respected — no cross-module imports outside declared dependencies.
- Security logic is centralized — in the hub's security config and filters, and in the
shared
core/lib/commonprimitives, rather than reimplemented per service.
See also
- The four planes — how these modules group into planes.
- Delivery models — how the modules are packaged and deployed.
- Federation runtime — the runtime behaviour of the federation-member modules.