Skip to content

Instantly share code, notes, and snippets.

@rwinch
Last active February 25, 2021 05:54
Show Gist options
  • Save rwinch/cd1b459d6e04d30d72edb7e6919b3cbb to your computer and use it in GitHub Desktop.
Save rwinch/cd1b459d6e04d30d72edb7e6919b3cbb to your computer and use it in GitHub Desktop.

Spring Security OAuth 2 HTTP to Java Mappings

This document maps the HTTP request/responses to Java code for Spring Security’s OAuth support.

Reactive

OAuth2 Log In Mappings from HTTP to Java

This section maps the HTTP request/responses to Java code for Spring Security’s Reactive OAuth2 Log In support.

Authentication Request

To initiate authentication (trigger an Authentication Request):

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

Authentication

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"
}

Resource Server

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"
  }
]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment