This document maps the HTTP request/responses to Java code for Spring Security’s OAuth support.
This section maps the HTTP request/responses to Java code for Spring Security’s Reactive OAuth2 Log In support.
To initiate authentication (trigger an Authentication Request):
-
a request to
/oauth2/authorization/{client-registration-id}
is made -
The OAuth2AuthorizationRequestRedirectFilter
-
Extracts out the
{client-registration-id}
-
Looks up the information in the ReactiveClientRegistrationRepository
-
Redirects to the OAuth2 Authorization Server.
-
GET /oauth2/authorization/keycloak HTTP/1.1
Host: localhost:8080
Cookie: SESSION=36a7a89e-4cee-4b71-9d78-b8730a4afc87
HTTP/1.1 302 Found
Location: http://idp:9999/auth/realms/demo/protocol/openid-connect/auth?response_type=code
&client_id=spring-security
&scope=openid+address+admin+email+message%3Aread+offline_access+phone+profile
&state=5Kyu2YwF2OAm-GMS8cQoRoBk7Ej465T4m7pB_uJEA_g%3D
&redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Flogin%2Foauth2%2Fcode%2Fkeycloak
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1 ; mode=block
Referrer-Policy: no-referrer
content-length: 0
The Authorization Server will respond with a URL in the format of /login/oauth2/code/{client-registration-id}
. The
AuthenticationWebFilter will then proess the request.
GET /login/oauth2/code/keycloak?state=5Kyu2YwF2OAm-GMS8cQoRoBk7Ej465T4m7pB_uJEA_g%3D
&session_state=8a3595b2-d169-42ad-bef2-5c9ef640be04
&code=eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..LrjK-qNurHVSiTJhUM9JrQ.UWXuG1T2qtpJBjUrkLbCnAfVrsWPj4wK559aBwmFZx4JdmjuWlnkK_ztfcSInEqLPP8_Lr9HR5R21-MYYDE2kwH5mCjJC0aOh5Eiw47QPO9ibrvKwVZoi7Bl_CAkcW23KUVF-y38xbxmmnWh_uqvMQWDzBeuuLL-9kxmCh01oROEwit_FBPT51AFg2YNAnI-bjRi1rmjoce1AIvdW5Kej-Um__RTIR_uk8I-GOWk_aqEKwiqkDpNF0w2jViDf95I.iaoINinRudOTgPccggGSpQ HTTP/1.1
Host: localhost:8080
Cookie: SESSION=36a7a89e-4cee-4b71-9d78-b8730a4afc87
HTTP/1.1 302 Found
Location: /
Cache-Control: no-cache, no-store, max-age=0, must-revalidate
Pragma: no-cache
Expires: 0
X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1 ; mode=block
Referrer-Policy: no-referrer
set-cookie: SESSION=f898e08f-ad6a-4e8a-9079-b139ca0f427b; Path=/; HTTPOnly
content-length: 0
The processing begins in OidcAuthorizationCodeReactiveAuthenticationManager which does two main things:
First OidcAuthorizationCodeReactiveAuthenticationManager
delegates to
WebClientReactiveAuthorizationCodeTokenResponseClient to exchange the authorization code for an access token.
POST /auth/realms/demo/protocol/openid-connect/token HTTP/1.1
host: idp:9999
Accept: application/json
Authorization: Basic c3ByaW5nLXNlY3VyaXR5OmJmYmQ5ZjYyLTAyY2UtNDYzOC1hMzcwLTgwZDQ1NTE0YmQwYQ==
Content-Type: application/x-www-form-urlencoded;charset=UTF-8
grant_type=authorization_code&
code=eyJhbGciOiJkaXIiLCJlbmMiOiJBMTI4Q0JDLUhTMjU2In0..LrjK-qNurHVSiTJhUM9JrQ.UWXuG1T2qtpJBjUrkLbCnAfVrsWPj4wK559aBwmFZx4JdmjuWlnkK_ztfcSInEqLPP8_Lr9HR5R21-MYYDE2kwH5mCjJC0aOh5Eiw47QPO9ibrvKwVZoi7Bl_CAkcW23KUVF-y38xbxmmnWh_uqvMQWDzBeuuLL-9kxmCh01oROEwit_FBPT51AFg2YNAnI-bjRi1rmjoce1AIvdW5Kej-Um__RTIR_uk8I-GOWk_aqEKwiqkDpNF0w2jViDf95I.iaoINinRudOTgPccggGSpQ&
redirect_uri=http%3A%2F%2Flocalhost%3A8080%2Flogin%2Foauth2%2Fcode%2Fkeycloak
HTTP/1.1 200 OK
Connection: keep-alive
Content-Type: application/json
Content-Length: 3974
Date: Mon, 11 Feb 2019 22:48:02 GMT
{
"access_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJQMW5MRG8wbzBSdXdab1BrNUR1NzVieGNsSjV1Tk9iTGt3UnBFZ0xidmVVIn0.eyJqdGkiOiIxNGMzY2FhYS03NmRjLTQ2NDAtYTAzMi1mOWI5Njg2YWFiMTciLCJleHAiOjE1NDk5Mjg4ODIsIm5iZiI6MCwiaWF0IjoxNTQ5OTI1MjgyLCJpc3MiOiJodHRwOi8vaWRwOjk5OTkvYXV0aC9yZWFsbXMvZGVtbyIsImF1ZCI6InNwcmluZy1zZWN1cml0eSIsInN1YiI6ImE0MGM0YmQ5LWVjOWEtNDczZi1iNzg5LTk0YjFlYmRiOWI2MSIsInR5cCI6IkJlYXJlciIsImF6cCI6InNwcmluZy1zZWN1cml0eSIsImF1dGhfdGltZSI6MTU0OTkyNTExNiwic2Vzc2lvbl9zdGF0ZSI6IjhhMzU5NWIyLWQxNjktNDJhZC1iZWYyLTVjOWVmNjQwYmUwNCIsImFjciI6IjAiLCJhbGxvd2VkLW9yaWdpbnMiOltdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoib3BlbmlkIHBob25lIGFkZHJlc3MgcHJvZmlsZSBtZXNzYWdlOnJlYWQgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYWRkcmVzcyI6e30sInVzZXJfaWQiOiIxIiwibmFtZSI6IlJvYiBXaW5jaCIsInByZWZlcnJlZF91c2VybmFtZSI6InJvYiIsImdpdmVuX25hbWUiOiJSb2IiLCJmYW1pbHlfbmFtZSI6IldpbmNoIiwiZW1haWwiOiJyb2JAZXhhbXBsZS5jb20ifQ.KInCzg6SlxtYuNMJdVy_FlWh7HK1Yu3Yajxi7zqLasxix4AXxteIS2fikhqle3VwIkI6goLzOJ79gOB3mBpq0D51-Yx3HkrZY6yKYd5yi9xL7fpXJX_B44IpnKcELTvZqFfKLv5xI5LJv1S_wwvUNaIwsP2lgePZBb3ZL-j_qCzBVWMSDqjTrMnSshFd4cD621-UXE3RlEzPmMZux99H_sZ92A9Jpyiyq-5BqFsZkxpB3Bb8e0d5862UaabVXGpRpFYLC5EKqcQjB9OHaszh3xoWHFd0aCoektfWu1PoouZ7Cu0XVfJFqTuKHy5mVOJQvhYPmKlsE3s5IuZqXvyE5w",
"expires_in":3600,
"refresh_expires_in":0,
"refresh_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJQMW5MRG8wbzBSdXdab1BrNUR1NzVieGNsSjV1Tk9iTGt3UnBFZ0xidmVVIn0.eyJqdGkiOiIwYjMxZWMwZC1jNTYzLTQ2OWUtOWM2ZS02NjRjMjUwZjFjNGUiLCJleHAiOjAsIm5iZiI6MCwiaWF0IjoxNTQ5OTI1MjgyLCJpc3MiOiJodHRwOi8vaWRwOjk5OTkvYXV0aC9yZWFsbXMvZGVtbyIsImF1ZCI6InNwcmluZy1zZWN1cml0eSIsInN1YiI6ImE0MGM0YmQ5LWVjOWEtNDczZi1iNzg5LTk0YjFlYmRiOWI2MSIsInR5cCI6Ik9mZmxpbmUiLCJhenAiOiJzcHJpbmctc2VjdXJpdHkiLCJhdXRoX3RpbWUiOjAsInNlc3Npb25fc3RhdGUiOiI4YTM1OTViMi1kMTY5LTQyYWQtYmVmMi01YzllZjY0MGJlMDQiLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoib3BlbmlkIHBob25lIGFkZHJlc3MgcHJvZmlsZSBtZXNzYWdlOnJlYWQgZW1haWwgb2ZmbGluZV9hY2Nlc3MifQ.GQ50NtYWC1ziaTRCXzBZknzOJL23lWNwDoEDXTM2-b69Vq6ti0UOhB3lTJ-RfbIDCQhH9eONvIgWKnsJLJJ_mPaXqlYY8i42jX-nLO45ZVCKhkVYwmyTaHA_hM7k9m7JW0M4LuYcI6nhgGaq6QkF9opZ3KdwlXuK44zooPzy0b3o3eRn9tPI_Mt-clQ-WKUsS9cZxQhjOkgTiMRyvs6He8eYZVWLj9W1N8n4v7963OM9wSOPz2ww-iUFaGYTTxHE1CZJ6mP9jRchfbTQZomG-yA5G7L_JzdnX7WSCy3LA_3V5LX3JRDZaCMED2UT48Cqw-NRS9tANi0MRFY32Cd8hQ",
"token_type":"bearer",
"id_token":"eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJQMW5MRG8wbzBSdXdab1BrNUR1NzVieGNsSjV1Tk9iTGt3UnBFZ0xidmVVIn0.eyJqdGkiOiI4MDFlNTg4My04NTAxLTQxMDEtYmNhMS00ZTBmYzJlYzM2ZjAiLCJleHAiOjE1NDk5Mjg4ODIsIm5iZiI6MCwiaWF0IjoxNTQ5OTI1MjgyLCJpc3MiOiJodHRwOi8vaWRwOjk5OTkvYXV0aC9yZWFsbXMvZGVtbyIsImF1ZCI6InNwcmluZy1zZWN1cml0eSIsInN1YiI6ImE0MGM0YmQ5LWVjOWEtNDczZi1iNzg5LTk0YjFlYmRiOWI2MSIsInR5cCI6IklEIiwiYXpwIjoic3ByaW5nLXNlY3VyaXR5IiwiYXV0aF90aW1lIjoxNTQ5OTI1MTE2LCJzZXNzaW9uX3N0YXRlIjoiOGEzNTk1YjItZDE2OS00MmFkLWJlZjItNWM5ZWY2NDBiZTA0IiwiYWNyIjoiMCIsImVtYWlsX3ZlcmlmaWVkIjp0cnVlLCJhZGRyZXNzIjp7fSwidXNlcl9pZCI6IjEiLCJuYW1lIjoiUm9iIFdpbmNoIiwicHJlZmVycmVkX3VzZXJuYW1lIjoicm9iIiwiZ2l2ZW5fbmFtZSI6IlJvYiIsImZhbWlseV9uYW1lIjoiV2luY2giLCJlbWFpbCI6InJvYkBleGFtcGxlLmNvbSJ9.EbQ3WkPnXF4dUGi7MZdZPAsvxu-qIMlCu9OPMeV9-IYmbBx4fcHq5WpExpUeQW8QbwY7FkFhcbBM4PY1Ck5VHLrrlT0pkK49pFg8GM0YIp_i-g2inrr2s5w63tpyOqK1RMWNn8gYhOpm7ZoldmBYuURYe5bNK4SmNpX9LXJgHc7w9zLvzjLoJjVjmrbhrWr8HMFfdIeW-JBTnHK9J85aNVn5QFpO3zRpRpUdHJjJvzTyqENfqvSaUZON2eDMEc9_K0bl3_E2Sfo8cxqMpwK3dKbY0IwcspaNgrj_hNu4KJjykezm4BPjnVaNyCYA3ZNU-O0i63IxNv7ds6-YkYKBoQ",
"not-before-policy":0,
"session_state":"8a3595b2-d169-42ad-bef2-5c9ef640be04",
"scope":"openid phone address profile message:read email offline_access"
}
We can decode the JWT token using jwt.io.
Now that we have an access token, the OidcAuthorizationCodeReactiveAuthenticationManager
delegates to OidcReactiveOAuth2UserService to translate the user info request into an OidcUser
GET /auth/realms/demo/protocol/openid-connect/userinfo HTTP/1.1
host: idp:9999
Accept: application/json
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJQMW5MRG8wbzBSdXdab1BrNUR1NzVieGNsSjV1Tk9iTGt3UnBFZ0xidmVVIn0.eyJqdGkiOiIxNGMzY2FhYS03NmRjLTQ2NDAtYTAzMi1mOWI5Njg2YWFiMTciLCJleHAiOjE1NDk5Mjg4ODIsIm5iZiI6MCwiaWF0IjoxNTQ5OTI1MjgyLCJpc3MiOiJodHRwOi8vaWRwOjk5OTkvYXV0aC9yZWFsbXMvZGVtbyIsImF1ZCI6InNwcmluZy1zZWN1cml0eSIsInN1YiI6ImE0MGM0YmQ5LWVjOWEtNDczZi1iNzg5LTk0YjFlYmRiOWI2MSIsInR5cCI6IkJlYXJlciIsImF6cCI6InNwcmluZy1zZWN1cml0eSIsImF1dGhfdGltZSI6MTU0OTkyNTExNiwic2Vzc2lvbl9zdGF0ZSI6IjhhMzU5NWIyLWQxNjktNDJhZC1iZWYyLTVjOWVmNjQwYmUwNCIsImFjciI6IjAiLCJhbGxvd2VkLW9yaWdpbnMiOltdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoib3BlbmlkIHBob25lIGFkZHJlc3MgcHJvZmlsZSBtZXNzYWdlOnJlYWQgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYWRkcmVzcyI6e30sInVzZXJfaWQiOiIxIiwibmFtZSI6IlJvYiBXaW5jaCIsInByZWZlcnJlZF91c2VybmFtZSI6InJvYiIsImdpdmVuX25hbWUiOiJSb2IiLCJmYW1pbHlfbmFtZSI6IldpbmNoIiwiZW1haWwiOiJyb2JAZXhhbXBsZS5jb20ifQ.KInCzg6SlxtYuNMJdVy_FlWh7HK1Yu3Yajxi7zqLasxix4AXxteIS2fikhqle3VwIkI6goLzOJ79gOB3mBpq0D51-Yx3HkrZY6yKYd5yi9xL7fpXJX_B44IpnKcELTvZqFfKLv5xI5LJv1S_wwvUNaIwsP2lgePZBb3ZL-j_qCzBVWMSDqjTrMnSshFd4cD621-UXE3RlEzPmMZux99H_sZ92A9Jpyiyq-5BqFsZkxpB3Bb8e0d5862UaabVXGpRpFYLC5EKqcQjB9OHaszh3xoWHFd0aCoektfWu1PoouZ7Cu0XVfJFqTuKHy5mVOJQvhYPmKlsE3s5IuZqXvyE5w
HTTP/1.1 200 OK
Content-Type: application/json
Content-Length: 208
Date: Mon, 11 Feb 2019 22:48:02 GMT
{
"sub":"a40c4bd9-ec9a-473f-b789-94b1ebdb9b61",
"email_verified":true,
"address":{},
"user_id":"1",
"name":"Rob Winch",
"preferred_username":"rob",
"given_name":"Rob",
"family_name":"Winch",
"email":"rob@example.com"
}
When a request with a Bearer token comes in, the AuthenticationWebFilter will extract a BearerTokenAuthenticationToken using ServerBearerTokenAuthenticationConverter.
The BearerTokenAuthenticationToken
is then passed to JwtReactiveAuthenticationManager for authenticating the token. Authentication is done by creating a Jwt using ReactiveJwtDecoder
Note
|
You can decode the JWT Bearer token using https://jwt.io |
GET /messages/inbox HTTP/1.1
host: localhost:8082
accept: */*
Authorization: Bearer eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJQMW5MRG8wbzBSdXdab1BrNUR1NzVieGNsSjV1Tk9iTGt3UnBFZ0xidmVVIn0.eyJqdGkiOiIxNGMzY2FhYS03NmRjLTQ2NDAtYTAzMi1mOWI5Njg2YWFiMTciLCJleHAiOjE1NDk5Mjg4ODIsIm5iZiI6MCwiaWF0IjoxNTQ5OTI1MjgyLCJpc3MiOiJodHRwOi8vaWRwOjk5OTkvYXV0aC9yZWFsbXMvZGVtbyIsImF1ZCI6InNwcmluZy1zZWN1cml0eSIsInN1YiI6ImE0MGM0YmQ5LWVjOWEtNDczZi1iNzg5LTk0YjFlYmRiOWI2MSIsInR5cCI6IkJlYXJlciIsImF6cCI6InNwcmluZy1zZWN1cml0eSIsImF1dGhfdGltZSI6MTU0OTkyNTExNiwic2Vzc2lvbl9zdGF0ZSI6IjhhMzU5NWIyLWQxNjktNDJhZC1iZWYyLTVjOWVmNjQwYmUwNCIsImFjciI6IjAiLCJhbGxvd2VkLW9yaWdpbnMiOltdLCJyZWFsbV9hY2Nlc3MiOnsicm9sZXMiOlsib2ZmbGluZV9hY2Nlc3MiLCJ1bWFfYXV0aG9yaXphdGlvbiJdfSwicmVzb3VyY2VfYWNjZXNzIjp7ImFjY291bnQiOnsicm9sZXMiOlsibWFuYWdlLWFjY291bnQiLCJtYW5hZ2UtYWNjb3VudC1saW5rcyIsInZpZXctcHJvZmlsZSJdfX0sInNjb3BlIjoib3BlbmlkIHBob25lIGFkZHJlc3MgcHJvZmlsZSBtZXNzYWdlOnJlYWQgZW1haWwgb2ZmbGluZV9hY2Nlc3MiLCJlbWFpbF92ZXJpZmllZCI6dHJ1ZSwiYWRkcmVzcyI6e30sInVzZXJfaWQiOiIxIiwibmFtZSI6IlJvYiBXaW5jaCIsInByZWZlcnJlZF91c2VybmFtZSI6InJvYiIsImdpdmVuX25hbWUiOiJSb2IiLCJmYW1pbHlfbmFtZSI6IldpbmNoIiwiZW1haWwiOiJyb2JAZXhhbXBsZS5jb20ifQ.KInCzg6SlxtYuNMJdVy_FlWh7HK1Yu3Yajxi7zqLasxix4AXxteIS2fikhqle3VwIkI6goLzOJ79gOB3mBpq0D51-Yx3HkrZY6yKYd5yi9xL7fpXJX_B44IpnKcELTvZqFfKLv5xI5LJv1S_wwvUNaIwsP2lgePZBb3ZL-j_qCzBVWMSDqjTrMnSshFd4cD621-UXE3RlEzPmMZux99H_sZ92A9Jpyiyq-5BqFsZkxpB3Bb8e0d5862UaabVXGpRpFYLC5EKqcQjB9OHaszh3xoWHFd0aCoektfWu1PoouZ7Cu0XVfJFqTuKHy5mVOJQvhYPmKlsE3s5IuZqXvyE5w
HTTP/1.1 200 OK
transfer-encoding: chunked
Content-Type: application/json;charset=UTF-8
[
{
"id":1,
"to":"1",
"from":"2",
"text":"Hello World"
},
{
"id":2,
"to":"1",
"from":"2",
"text":"Greetings Spring Enthusiasts"
}
]