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”
1) David Karnok’s Generations of Reactive classification
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
— https://leanpub.com/reactive-spring
“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