OAuth
In this demo, we have an Identity Provider such as Keycloak capable of authenticating a user. Once authenticated, the Identity Provider gives a token to the user. Then, the user gives their token to the service they wish to access, known as the resource server, which validates its authenticity with the Identity Provider. This is typical of a backend microservice in a company.
We use Docker to start a Keycloak instance on our workstation.
The configuration of the book realm will be shown during the demo.
1docker run -p 9080:8080 \
2 -e KEYCLOAK_ADMIN=admin \
3 -e KEYCLOAK_ADMIN_PASSWORD=admin \
4 keycloak/keycloak:26.3.5 start-dev
1implementation("org.springframework.boot:spring-boot-starter-oauth2-resource-server")
2implementation("org.springframework.boot:spring-boot-starter-security") 1import org.springframework.context.annotation.Bean;
2import org.springframework.context.annotation.Configuration;
3import org.springframework.security.config.Customizer;
4import org.springframework.security.config.annotation.web.builders.HttpSecurity;
5import org.springframework.security.web.SecurityFilterChain;
6
7import static org.springframework.security.config.Customizer.*;
8
9@Configuration
10public class SecurityConfig {
11
12 @Bean
13 public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
14 http
15 .authorizeHttpRequests(authorize -> authorize
16 .requestMatchers("/api/**").authenticated()
17 .anyRequest().permitAll())
18 .oauth2ResourceServer(oauth2 -> oauth2.jwt(Customizer.withDefaults()));
19 return http.build();
20 }
21
22}1spring:
2 application:
3 name: spring-security-oauth-demo
4 security:
5 oauth2:
6 resourceserver:
7 jwt:
8 # issuer-uri: http://localhost:9080/realms/book
9 jwk-set-uri: http://localhost:9080/realms/book/protocol/openid-connect/certs1$ ACCESS_TOKEN=$(curl --silent --location 'http://localhost:9080/realms/book/protocol/openid-connect/token' \
2 --header 'Content-Type: application/x-www-form-urlencoded' \
3 --data-urlencode 'grant_type=client_credentials' \
4 --data-urlencode 'client_id=book-api' \
5 --data-urlencode 'client_secret=XjGMMRAzBCLKFSZLmaOs364fj9OCi83e' \
6 | jq -r .access_token)
|
Note
|
jq is a command-line JSON processor.
It is a very useful tool for extracting values or transforming a JSON document into another document with a different structure.
Here, we are extracting the value associated with the access_token key.
|
1$ curl --silent -X GET 'http://localhost:8080/api/books' \
2 --header "Authorization: Bearer $ACCESS_TOKEN" \
3 | jq .
4[
5 {
6 "isbn": "978-21-0081-182-3",
7 "title": "L'approche Lean pour la transformation digitale",
8 "author": [
9 "Yves Caseau"
10 ]
11 },
12 {
13 "isbn": "978-01-3475-768-1",
14 "title": "Refactoring: Improving the Design of Existing Code",
15 "author": [
16 "Martin Fowler"
17 ]
18 },
19 {
20 "isbn": "03-2121-335-1",
21 "title": "Refactoring to Patterns",
22 "author": [
23 "Joshua Kerievsky"
24 ]
25 },
26 {
27 "isbn": "978-03-2112-521-7",
28 "title": "Domain-Driven Design: Tackling Complexity in the Heart of Software",
29 "author": [
30 "Eric Evans"
31 ]
32 },
33 {
34 "isbn": "978-14-9207-753-4",
35 "title": "The Software Architect Elevator",
36 "author": [
37 "Gregor Hohpe"
38 ]
39 },
40 {
41 "isbn": "978-14-9190-306-3",
42 "title": "Designing Data-Intensive Applications",
43 "author": [
44 "Martin Kleppmann"
45 ]
46 },
47 {
48 "isbn": "978-14-4935-971-3",
49 "title": "RESTful Web APIs",
50 "author": [
51 "Leonard Richardson",
52 "Mike Amundsen",
53 "Sam Ruby"
54 ]
55 }
56]
The access token is sent to the server through the Authorization header.
The server verifies the authenticity of the token with the OAuth 2 server, then authorizes access to the resource exposed on the /api/books endpoint.
|
Note
|
With bash, you must use double quotes for string interpolation to work. With single quotes, the string $ACCESS_TOKEN is sent to the server, which correctly observes that the format is incorrect. Access is therefore denied. |