Skip to content

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 thoryn CLI are the customer plane's clients and live in the sibling thoryn-web repository. They are not part of oathy. The verifiable-credentials / wallet product lives in thoryn-vc-broker. This map covers the identity platform's own modules.

Servers — the deployable services

ModulePlaneRole
servers/authorization-hubBrokerThe 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/*IdentityThe identity sources — Thoryn's own Identity Service plus the per-provider connectors (below)
servers/product-apiCustomerThe customer self-service management API (resource server): tenant config, OAuth-client registration, federation, RBAC/FGA, audit, compliance
servers/api-gatewayCustomerThe 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:

CategoryModules
Thoryn's own IdPidentity-service — Thoryn-managed users, login UI, passkeys/MFA/magic-link, SCIM 2.0, account lifecycle
Reference / test IdPdummy-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.0adfs, pingfederate, saml-member (generic SAML)
Directory / system connectorsldap-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-gateway is 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-hub and product-api run the servlet-tier Spring stack with a non-blocking R2DBC data layer; the hub additionally configures a JDBC DataSource purely so Flyway can run migrations at startup.
  • identity-service runs 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:

ModulePurpose
commonCross-cutting security and web primitives — TrustedTenantIssuers, the per-tenant JWKS decoder factories, OutboundUrlGuard, securedHubCookie, the RFC 9457 ProblemDetailsWriter
signingThe signing seam: signing/common (the SigningStrategy interface) and signing/hashicorp-vault (the Vault/OpenBao Transit implementation)
sasSpring Authorization Server building blocks: sas/basic (federation filter + contributors), sas/federation, sas/step-up
fapiFAPI 1.0 / 2.0 building blocks (PAR, client profiles)
federationShared federation primitives
email / smsNotification transports
geoipIP geolocation for adaptive / risk-based auth
dsarData-subject-access-request (GDPR) primitives
vault-kvVault/OpenBao KV access
utilShared utilities

Spring Boot starters — core/starters/*

Auto-configuration modules that wire the libraries into a service with sensible defaults:

StarterWires
common-starterThe core/lib/common primitives (problem-details writer, security helpers)
sas/basic-starterThe Spring Authorization Server filter chain, OIDC, and the federation authentication filter
signing/signing-starterThe JwtEncoder factory (SasSigningEncoder)
signing/signing-vault-transit-starterThe Transit-backed SigningStrategy + its re-authentication management
fapiThe PAR endpoint and FAPI enforcement
gatewayThe reactive gateway security config, including multi-issuer token validation (GatewayMultiIssuerConfig)
flyway-starterStandalone Flyway migration wiring (JDBC alongside R2DBC)
incident-starterIncident/observability wiring
actuator-api-docs-starterActuator + 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 custom AuthenticationProvider beans 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/common primitives, rather than reimplemented per service.

See also