https://andifalk.github.io/reactive-spring-security-5-workshop
Novatec Consulting GmbH
https://www.novatec-gmbh.de
andreas.falk@novatec-gmbh.de / @andifalk
https://andifalk.github.io/reactive-spring-security-5-workshop https://github.com/andifalk/reactive-spring-security-5-workshop
Prepare for the hands-on parts:
https://github.com/andifalk/reactive-spring-security-5-workshop/tree/master/setup
“[...] we want systems that are Responsive, Resilient, Elastic and Message Driven. We call these Reactive Systems.”
“The system responds in a timely manner if at all possible”
“The system stays responsive in the face of failure”
“The system stays responsive under varying workload”
“Reactive Systems rely on asynchronous message-passing”
“Reactive Streams is an initiative to provide a standard for asynchronous stream processing with non-blocking back pressure..”
package org.reactivestreams;
public interface Publisher<T> {
public void subscribe(Subscriber<? super T> s);
}
package org.reactivestreams;
public interface Subscriber<T> {
public void onSubscribe(Subscription s);
public void onNext(T t);
public void onError(Throwable t);
public void onComplete();
}
package org.reactivestreams;
public interface Subscription {
public void request(long n);
public void cancel();
}
package org.reactivestreams;
public interface Processor<T, R> extends
Subscriber<T>, Publisher<R> {}
java.util.concurrent.Flow.Publisher<T>
java.util.concurrent.Flow.Subscriber<T>
java.util.concurrent.Flow.Subscription
java.util.concurrent.Flow.Processor<T, R>
“Reactor is a fourth-generation1 reactive library for building non-blocking applications on the JVM based on the Reactive Streams Specification”
An Asynchronous 0-1 Result
An Asynchronous Sequence of 0-N Items
String msg = "World";
String upperCaseMsg = msg.toUpperCase();
String greeting = "Hello " + upperCaseMsg + "!";
System.out.println(greeting);
Mono.just("World")
.map(String::toUpperCase)
.map(um -> "Hello " + um + "!")
.subscribe(System.out::println);
Debugging Reactive code can sometimes be challenging
spring.io/blog/2019/03/28/reactor-debugging-experience
https://projectreactor.io/docs/core/release/reference/#debugging
Detect blocking code
intro-labs/reactive-playground
projectreactor.io/docs/core/release/reference
projectreactor.io/docs/core/release/reference/#which-operator
“From my experience all software developers are now security engineers wether they know it, admit to it or do it [...]”
“If it's not secure, it's not guaranteed to be anything else either”
Application Security Verification Standard
...
implementation
'org.springframework.boot:spring-boot-starter-security'
implementation
'org.springframework.boot:spring-boot-starter-web'
...
@Configuration
public class WebSecurityConfiguration
extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http)
throws Exception {
http.authorizeRequests()
.anyRequest()
.authenticated()
.and()
.httpBasic().and().formLogin();
}
}
...
implementation
'org.springframework.boot:spring-boot-starter-security'
implementation
'org.springframework.boot:spring-boot-starter-webflux'
...
@Configuration
@EnableWebFluxSecurity
public class ReactiveWebSecurityConfiguration {
@Bean
SecurityWebFilterChain
securityWebFilterChain(ServerHttpSecurity http) {
return http.authorizeExchange()
.anyExchange()
.authenticated()
.and()
.httpBasic().and().formLogin().and().build();
}
}
https://andifalk.github.io/reactive-spring-security-5-workshop/api-doc.html
Internet Access
Java JDK version 8 or 11
A Java IDE (Eclipse, STS, IntelliJ, VS Code, NetBeans, ...)
Get via git clone or download as zip file:
https://github.com/andifalk/reactive-spring-security-5-workshop
https://andifalk.github.io/reactive-spring-security-5-workshop/workshop-tutorial.html
Make sure your Keycloak instance is set up and running
RFC 6749: The OAuth 2.0 Authorization Framework
RFC 6750: OAuth 2.0 Bearer Token Usage
RFC 6819: OAuth 2.0 Threat Model and Security Considerations
OAuth 2.0 is an authorization delegation framework
Client Type | Flow | Refresh Tokens |
---|---|---|
Confidential | Authorization Code | X |
Public (Native) | Authorization Code (PKCE) | X |
Public (SPA) | Implicit | -- |
Trusted | RO Password Creds | X |
No Resource Owner | Client Credentials | -- |
in action as part of Intro Lab
Client Type | Flow | Refresh Tokens |
---|---|---|
Confidential | Authorization Code (PKCE) | X |
Public (Native) | Authorization Code (PKCE) | X |
Public (SPA) | Authorization Code (PKCE) | -- |
Trusted | RO Password Creds | X |
No Resource Owner | Client Credentials | -- |
(“Pixy”)
Mitigates authorization code attacks
Mitigates token leakage in SPAs
GET https://authserver.example.com/authorize
?response_type=code
&client_id=abcdefg
&redirect_uri=https://client.abc.com/callback
&scope=api.read api.write
&state=xyz
&code_challenge=xyz...
&code_challenge_method=S256
POST https://authserver.example.com/token
Content-Type:
application/x-www-form-urlencoded
grant_type=authorization_code&code=ab23bhW56Xb
&redirect_uri=https://client.abc.com/callback
&client_id=123&client_secret=456
&code_verifier=4gth4jn78k_8
OpenID Connect Core 1.0
OpenID Connect Dynamic Client Registration 1.0
OpenID Connect Discovery 1.0
Base 64 Encoded JSON Formatted Value of...
...Header
...Payload
...Signature
GET / HTTP/1.1
Host: localhost:8080
Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1N...
Header
{
typ: "JWT",
alg: "RS256"
}
Payload
{
iss: "https://identity.example.com",
aud: "my-client-id",
exp: 1495782385,
nonce: "N0.46824857243233511495739124749",
iat: 1495739185,
at_hash: "hC1NDSB8WZ9SnjXTid175A",
sub: "mysubject",
auth_time: 1495739185,
email: "test@gmail.com"
}
Scope | Required | Description |
---|---|---|
iss | X | Issuer Identifier |
sub | X | Subject Identifier |
aud | X | Audience(s) of this ID Token |
exp | X | Expiration time |
iat | X | Time at which the JWT was issued |
auth_time | (X) | Time of End-User authentication |
nonce | -- | Associate a client with an ID Token |
Format and content still not standardized
But...
Required claims: iss, exp, aud, sub, client_id
Consider privacy restrictions for identity claims
Authorization claims according to SCIM Core (RFC7643):
System for Cross-domain Identity Management (SCIM)
JSON Web Token (JWT) Profile for OAuth 2.0 Access Tokens
GET /userinfo HTTP/1.1
Host: identityserver.example.com
Authorization: Bearer SlAV32hkKG
HTTP/1.1 200 OK
Content-Type: application/json
{
"sub": "248289761001",
"name": "Jane Doe",
"given_name": "Jane",
"family_name": "Doe",
"preferred_username": "j.doe",
"email": "janedoe@example.com",
"picture": "http://example.com/janedoe/me.jpg"
}
https://idp.example.com/.well-known/openid-configuration
{
"authorization_endpoint": "https://idp.example.com/auth",
"grant_types_supported": [
"authorization_code",
"implicit",
"refresh_token"
],
"issuer": "https://idp.example.com",
"jwks_uri": "https://idp.example.com/keys",
"token_endpoint": "https://idp.example.com/token",
"userinfo_endpoint": "https://idp.example.com/userinfo",
...
}
Make sure you have setup and started keycloak
THEN
Try the intro lab for Auth Code Demo
and follow instructions for Labs 5 & 6 in the
online tutorial
...on Linux/Mac OS
[keycloak_install_dir]/bin/standalone.sh
...on Windows
[keycloak_install_dir]\bin\standalone.bat
Please follow online tutorial in GitHub Repo
See intro-labs/auth-code-demo for instructions
Spring Security 5.2.0 M2 GitHub Issues
Spring Security 5.2.0 M3 GitHub Issues
Spring Security 5.2.0 RC1 GitHub Issues
Opaque Tokens
class ResSrvConfig extends WebSecurityConfigurerAdapter {
@Override
protected void configure(HttpSecurity http)
throws Exception {
http.oauth2ResourceServer()
.opaqueToken()
.introspectionUri(this.introspectionUri)
.introspectionClientCredentials(
this.clientId, this.clientSecret);
}
}
https://github.com/spring-projects/spring-security/issues/5200
class ResSrvConfig extends WebSecurityConfigurerAdapter {
@Override protected void configure(HttpSecurity http) {
http.oauth2ResourceServer()
.authenticationManagerResolver(
multitenantAuthenticationManager());
}
@Bean AuthenticationManagerResolver<HttpServletRequest>
multiTenantAuthMgr() {...}
AuthenticationManager jwt() {...}
AuthenticationManager opaque() {...}
}
https://github.com/spring-projects/spring-security/issues/5351
class ResSrvConfig extends WebSecurityConfigurerAdapter {
@Value("${spring.security.oauth2.resourceserver.
jwt.key-value}") RSAPublicKey key;
@Override protected void configure(HttpSecurity http) {
http.oauth2ResourceServer().jwt().decoder(jwtDecoder());
}
@Bean JwtDecoder jwtDecoder() throws Exception {
return NimbusJwtDecoder.
withPublicKey(this.key).build();
}
}
https://github.com/spring-projects/spring-security/issues/5465
public class OAuth2ResourceServerTest {
@Test
public void testRequestPostProcessor() {
mockMvc.perform(get("/message")
.with(mockAccessToken().scope("message:read")))
.andExpect(status().isOk())
mockMvc.perform(get("/")
.with(jwt().claim(SUB, "the-subject")))
.andExpect(status().isOk())
}
}
https://github.com/spring-projects/spring-security/issues/6634
Support OAuth 2.0 Authorization Server:
All images used are from Pixabay and are published under Creative Commons CC0 license.
All used logos are trademarks of respective companies
Novatec Consulting GmbH
Dieselstraße 18/1
D-70771 Leinfelden-Echterdingen
andreas.falk@novatec-gmbh.de