oidc-auth-resource-server is a Java / Spring Boot library that makes it trivial to protect HTTP endpoints with OAuth 2.0 / OpenID Connect (OIDC) access tokens. It handles JWT validation, JWK-set caching, opaque-token introspection, and multi-tenant setups for both Spring MVC and WebFlux applications.
| Gradle sub-project | Purpose |
|---|---|
oidc-rs-spring-boot-web |
Auto-configuration for Spring MVC (Servlet stack) |
oidc-rs-spring-boot-web-config |
Support application.yaml configuration for Spring MVC (Servlet stack) |
oidc-rs-spring-boot-webflux |
Auto-configuration for Spring WebFlux (reactive stack) |
oidc-rs-spring-boot-webflux-config |
Support application.yaml configuration for WebFlux (reactive stack) |
oidc-rs-junit-tenant |
JUnit 5 helpers for spinning up an in-memory IdP and generating tokens |
oidc-rs-spring-boot-web-test |
Bundeled oidc-rs-junit-tenant and oidc-rs-spring-boot-web-config |
oidc-rs-spring-boot-webflux-test |
Bundeled oidc-rs-junit-tenant and oidc-rs-spring-boot-webflux-config |
oidc-rs-spring-boot-common |
Core validation & utility classes |
oidc-rs-test |
Test utilities shared across modules |
- Plug-and-play JWT verification
- Automatic JWK-set download and refresh with configurable cache TTL
- Audience, issuer, scope/authority and claim validation
- Multi-tenant support – secure the same app with several issuers in parallel
- Ready-made
SecurityFilterChainbeans for both Spring MVC & WebFlux - JUnit extension for fast, isolated tests (no real IdP required)
Add the dependency that matches your runtime stack:
// Spring MVC / Servlet
implementation("org.entur.auth.resource-server:oidc-rs-spring-boot-web-config:${oidcAuthServerVersion}")
testImplementation("org.entur.auth.resource-server:oidc-rs-spring-boot-web-test:${oidcAuthServerVersion}")// OR: Spring WebFlux
implementation("org.entur.auth.resource-server:oidc-rs-spring-boot-webflux-config:${oidcAuthServerVersion}")
testImplementation("org.entur.auth.resource-server:oidc-rs-spring-boot-webflux-test:${oidcAuthServerVersion}")If you only need manual low-level configuration without use of application.yaml,
depend on oidc-rs-spring-boot-web or oidc-rs-spring-boot-webflux directly.
Using predefined environment and tenants from application.yaml:
entur:
auth:
tenants:
environment: dev # Predefined values for Entur: dev, tst, prd, stage, prod, mock
include: partner # Predefined values for Entur: partner, internal, traveller, personEntur's predefined environment definitions can be replaced by defining own Spring bean:
@Bean
public AuthProviders authProviders() {
return new MyAuthProviders();
}Note
AuthProviders will also provide a granted authority based on tenant to support example: @PreAuthorize("hasAnyAuthority('internal')")
Tip
Default Spring be used by turning off authentication manager configuration with: entur.auth.enabled: false
Additional to predefined tenants can issuers be defined in application.yaml:
entur:
auth:
issuers:
-
issuerUrl: https://internal.mock.entur.io
certificateUrl: http://localhost:${MOCKAUTHSERVER_PORT}/internal/.well-known/jwks.json
-
issuerUrl: https://partner.mock.entur.io
certificateUrl: http://localhost:${MOCKAUTHSERVER_PORT}/partner/.well-known/jwks.jsonAdditional to predefined tenants can issuers be defined in external file:
entur:
auth:
external:
tenants: partner, internal
resource: ${providers_file:file:./src/test/kubernetes/dev/providers.properties}By default, will the authorization filter enforces the use of fully authenticated.
Endpoints can be opened to the world from application.yaml. Example:
entur:
auth:
authorization:
permit-all:
matcher:
patterns: # All methods will be allowed
- /unprotected
- /unprotected/**
method: # Defined methods will be allowed
get:
patterns:
- /unprotected
- /unprotected/**Tip
Default Spring be used by turning off authorize configuration with:entur.auth.authorization.enabled: false
Spring default CORS configuration can be overrides from application.yaml:
entur:
auth:
cors:
mode: api | webapp | default
hosts: # Optional manually configuration
- https://myapp.entur.org
- https://myotherpetstore.swagger.io
- https://*.example1.com # domains ending with example1.com
- https://*.example2.com:[8080,8081] # domains ending with example2.com on port 8080 or port 8081
- https://*.example3.com:[*] # domains ending with example3.com on any port, including the default port - api: CORS requests from the API development portal & petstore plus the hosts specified in the hosts property list are accepted.
- webapp: CORS requests from the hosts specified in the hosts property list are accepted.
- default: CORS is set to '*' and hosts property list is ignored.
Tip
Default Spring be used by turning off CORS configuration with:entur.auth.cors.enabled: false
When running application in Kubernetes you may enable Health Indicator and include jwks cache status from application.yaml:
management:
endpoints.web.exposure.include: health # Add others if needed
endpoint:
health:
probes.enabled: true
group.readiness.include: readinessState, jwksStateTip
Confgured health indicator can be turned off with:management.health.jwks.enabled: false
For copying interesting JWT fields through to the MDC logging context. Default configure mappings is shown below:
entur:
auth:
mdc:
mappings: # List of custom mdc mapping
-
from: azp # azp from claim
to: clientId # to mdc key
-
from: https://entur.io/organisationID
to: organisationIdTip
Default Spring be used by turning off MDC configuration with:entur.auth.mdc.enabled: false
entur:
auth:
lazy-load: true | false # When using RSA certificates can JWKS load be delayed. Default = false.
retry-on-failure: true | false # When true will failure on loading JWKS be retried. Default = false.
connect-timeout: <seconds> # Numbus Resource Retriever - connectTimeout. Default = 5 seconds.
read-timeout: <seconds> # Numbus Resource Retriever - readTimeout. Default = 5 seconds.
jwks-throttle-wait: <seconds> # Numbus JWKSourceBuilder - rateLimited. Default = 30 seconds.
refresh-ahead-time: <seconds> # Numbus RefreshAheadCache - refreshAheadTime. Default = 30 seconds.
cache-refresh-timeout: <seconds> # Numbus Cache - cacheRefreshTimeout. Default = 15 seconds.
cache-lifespan: <seconds> # Numbus Cache - cacheLifespan. Default = 300 seconds.
outage-tolerant: <seconds> # Numbus Cache - cacheLifespan. Default = 300 seconds.On issuer confguration can this values be overwritten:
entur:
auth:
issuers:
- issuerUrl: https://my.site.io
certificateUrl: https://my.site.io/.well-known/jwks.json
retry-on-failure: true | false # When true will failure on loading JWKS be retried.
jwks-throttle-wait: <seconds> # Numbus JWKSourceBuilder - rateLimited.
refresh-ahead-time: <seconds> # Numbus RefreshAheadCache - refreshAheadTime.
cache-refresh-timeout: <seconds> # Numbus Cache - cacheRefreshTimeout.
cache-lifespan: <seconds> # Numbus Cache - cacheLifespan.
outage-tolerant: <seconds> # Numbus Cache - outageTolerant. Default = 300 seconds.Local testing with jwt generation is supported with JUnit integration.
First configure Spring Security to use a local web server for JWKS:
entur:
auth:
tenants:
environment: mock
include: partner, internalOr by:
entur:
auth:
test.load-issuers: true # Needed since oidc-rs-spring-boot-web-test deletes issuers by default
# load-environments: true # Setting to tell oidc-rs-spring-boot-web-test not clear tenant environment
issuers:
- issuerUrl: https://internal.mock.entur.io
certificateUrl: http://localhost:${MOCKAUTHSERVER_PORT}/internal/.well-known/jwks.json
- issuerUrl: https://partner.mock.entur.io
certificateUrl: http://localhost:${MOCKAUTHSERVER_PORT}/partner/.well-known/jwks.jsonImportant
Environment variable ${MOCKAUTHSERVER_PORT} will be automatically defined when TenantJsonWebToken.class is used as extension.
Tip
When tests fails with "Couldn't retrieve JWK set from URL: Read timed out", can it help to explicit configure entur.auth.lazy-load: true for your tests.
@TestPropertySource(
properties = {
"entur.auth.tenants.environment=mock",
"entur.auth.tenants.include=internal,partner",
})
@ExtendWith({SpringExtension.class, TenantJsonWebToken.class})
@SpringBootTest(webEnvironment = WebEnvironment.RANDOM_PORT)
@AutoConfigureMockMvc
class AuthorizeTest {
@Autowired
private MockMvc mockMvc;
@Test
void testProtectedWithPartner(@PartnerTenant(clientId = "clientId", subject = "subject") String authorization) throws Exception {
var requestHeaders = new HttpHeaders();
requestHeaders.add("Accept", MediaType.APPLICATION_JSON_VALUE);
requestHeaders.add("Authorization", authorization);
mockMvc.perform(get("/protected").headers(requestHeaders)).andExpect(status().isOk());
}
}@TestPropertySource(
properties = {
"entur.auth.lazy-load=true",
"entur.auth.tenants.environment=mock",
"entur.auth.tenants.include=internal,partner",
})
@ExtendWith({TenantJsonWebToken.class}) // Needed to define MOCKAUTHSERVER_PORT and support parameter to be resolved
@WebMvcTest({GreetingController.class})
@ImportAutoConfiguration({
ResourceServerAutoConfiguration.class, // Configure default SecurityFilterChain
ResourceServerDefaultAutoConfiguration.class, // Configure default Spring Security behavior
ConfigAuthProvidersAutoConfiguration.class, // Configure predefined tenants environment
ConfigAuthManagerResolverAutoConfiguration.class, // Configure use tenants.environment
ConfigResourceServerAutoConfiguration.class, // Configure authorization, cors, mdc, issuers
ConfigJwksHealthIndicatorAutoConfiguration.class, // Optional: If you use management endpoint jwksState
ConfigExternalPropertyAutoConfiguration.class // Optional: If you read issuers from property file
})
class AuthorizeTest {
@Autowired
private MockMvc mockMvc;
@Test
void testProtectedWithPartner(@PartnerTenant(clientId = "clientId", subject = "subject") String authorization) throws Exception {
var requestHeaders = new HttpHeaders();
requestHeaders.add("Accept", MediaType.APPLICATION_JSON_VALUE);
requestHeaders.add("Authorization", authorization);
mockMvc.perform(get("/protected").headers(requestHeaders)).andExpect(status().isOk());
}
}@TestPropertySource(
properties = {
"entur.auth.lazy-load=true",
"entur.auth.tenants.environment=mock",
"entur.auth.tenants.include=internal,partner",
})
@ExtendWith({TenantJsonWebToken.class}) // Needed to define MOCKAUTHSERVER_PORT and support parameter to be resolved
@WebFluxTest({GreetingController.class})
@ImportAutoConfiguration({
ReactiveResourceServerAutoConfiguration.class, // Configure default SecurityFilterChain
ReactiveResourceServerDefaultAutoConfiguration.class, // Configure default Spring Security behavior
ConfigReactiveAuthProvidersAutoConfiguration.class, // Configure predefined tenants environment
ConfigReactiveAuthManagerResolverAutoConfiguration.class, // Configure use tenants.environment
ConfigReactiveResourceServerAutoConfiguration.class, // Configure authorization, cors, mdc, issuers
ConfigReactiveJwksHealthIndicatorAutoConfiguration.class, // Optional: If you use management endpoint jwksState
ConfigReactiveExternalPropertyAutoConfiguration.class // Optional: If you read issuers from property file
})
class AuthorizeTest {
@Autowired
private WebTestClient webTestClient;
@Test
void testProtectedWithPartner(@PartnerTenant(clientId = "clientId", subject = "subject") String authorization) throws Exception {
var requestHeaders = new HttpHeaders();
requestHeaders.add("Accept", MediaType.APPLICATION_JSON_VALUE);
requestHeaders.add("Authorization", authorization);
mockMvc.perform(get("/protected").headers(requestHeaders)).andExpect(status().isOk());
webTestClient
.get()
.uri("/protected")
.headers(
httpHeaders -> {
httpHeaders.add("Accept", MediaType.APPLICATION_JSON_VALUE);
httpHeaders.add("Authorization", authorization);
})
.exchange()
.expectStatus()
.isOk();
}
}If additional customising of SecurityFilterChain is needed, it can be provided by implementing own Spring Bean's.
Example:
@Configuration
public class ResourceServerDefaultAutoConfiguration {
@Bean
public UserDetailsService userDetailsService() {
return new MyDetailsService();
}
@Bean
public ConfigureAuthorizeRequests configureAuthorizeRequests() {
return new MyConfigureAuthorizeRequests();
}
@Bean
public ConfigureSessionManagement configureSessionManagement() {
return new MyConfigureSessionManagement();
}
@Bean
public ConfigureCsrf configureCsrf() {
return new MyConfigureCsrf();
}
@Bean
public ConfigureCors configureCors() {
return new MyConfigureCors();
}
@Bean
public ConfigureMdcRequestFilter configureMdcRequestFilter() {
return new MyMdcRequestFilter();
}
@Bean
public ConfigureAuth2ResourceServer configureAuth2ResourceServer() {
return new MyConfigureAuth2ResourceServer();
}
}Clone the repository:
git clone https://github.com/entur/oidc-auth-resource-server.git
cd oidc-auth-clientBuild and run JUnit tests:
./gradlew buildPull requests are welcome! See CONTRIBUTING for details.
Licensed under the EUPL-1.2 – see LICENSE for the full text.