1. Preface

In the previous article we found the filter OAuth2AuthorizationRequestRedirectFilter that intercepts the OAuth2 authorization request entry /oauth2/authorization and found the method that actually initiates the OAuth2 authorization request sendRedirectForAuthorization. But this method is not described in detail, so I’ll continue today.

2. sendRedirectForAuthorization

This sendRedirectForAuthorization method is not much code, its main purpose is to redirect access to third-party platforms for authorization. All its logic is related to OAuth2AuthorizationRequest, so we can’t gloss over OAuth2AuthorizationRequest, we have to understand how OAuth2AuthorizationRequest came to be and what it’s for.

OAuth2AuthorizationRequestResolver

This requires analyzing the parser class OAuth2AuthorizationRequestResolver, whose core methods have two overloads, one of which is sufficient here.

1
2
3
4
5
6
7
8
9
@Override
public OAuth2AuthorizationRequest resolve(HttpServletRequest request) {
    // registrationId是通过uri路径参数/oauth2/authorization/{registrationId}获得的
   String registrationId = this.resolveRegistrationId(request);
    // 然后去请求对象request中提取key为action的参数,默认值是login
   String redirectUriAction = getAction(request, "login");
    // 然后进入根本的解析方法
   return resolve(request, registrationId, redirectUriAction);
}

The resolve(request, registrationId, redirectUriAction) method inside the above method is the fundamental method to finally extract the OAuth2AuthorizationRequest from /oauth2/authorization. There’s a lot of code but I’ll try to diagram it in a way that’s easy to understand. The resolve method assembles different OAuth2AuthorizationRequests depending on the authorization type (AuthorizationGrantType).

3. OAuth2AuthorizationRequest

The next is the core of the OAuth2.0 protocol is the most important, you may later customize the reference from here, this time circle up to test the knowledge points. I will OAuth2AuthorizationRequestResolver in a variety of ways to authorize the resolution of the OAuth2AuthorizationRequest object for a complete summary of induction. It is roughly divided into the following two parts.

3.1 Determined by AuthorizationGrantType

The combing of OAuth2AuthorizationRequest under different AuthorizationGrantType. The member variables involved are.

  • authorizationGrantType, from the configuration spring.security.client.registration.{registrationId}.authorizationGrantType.
  • responseType, determined by the value of authorizationGrantType, refer to the JSON below.
  • additionalParameters, some additional parameters are required when the value of authorizationGrantType is authorization_code, refer to the JSON below.
  • attributes, different attributes exist for different authorizationGrantType.

where a form like {registrationId} means that {registrationId} is a variable, e.g. registrationId=gitee.

There are five cases in the OAuth2 client configuration spring.security.client.registration.{registrationId} prefix.

When scope does not contain openid and client-authentication-method is not none the above four parameters.

1
2
3
4
5
6
7
8
{
  "authorizationGrantType": "authorization_code",
  "responseType": "code",
  "additionalParameters": {},
  "attributes": {
    "registration_id": "{registrationId}"
  }
}

The above four parameters when scope contains openid and client-authentication-method is not none.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
{
  "authorizationGrantType": "authorization_code",
  "responseType": "code",
  "additionalParameters": {
    "nonce": "{nonce}的Hash值"
  },
  "attributes": {
    "registration_id": "{registrationId}",
    "nonce": "{nonce}"
  }
}

The above four parameters when scope does not contain openid and client-authentication-method is none.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
{
  "authorizationGrantType": "authorization_code",
  "responseType": "code",
  "additionalParameters": {
    "code_challenge": "{codeVerifier}的Hash值",
    // code_challenge_method 当不是SHA256可能没有该key
    "code_challenge_method": "S256(如果是SHA256算法的话)"
  },
  "attributes": {
    "registration_id": "{registrationId}",
    "code_verifier": "Base64生成的安全{codeVerifier}"
  }
}

The above four parameters when scope contains openid and client-authentication-method is none.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
{
  "authorizationGrantType": "authorization_code",
  "responseType": "code",
  "additionalParameters": {
    "code_challenge": "{codeVerifier}的Hash值",
    // code_challenge_method 当不是SHA256可能没有该key
    "code_challenge_method": "S256(如果是SHA256算法的话)",
    "nonce": "{nonce}的Hash值"
  },
  "attributes": {
    "registration_id": "{registrationId}",
    "code_verifier": "Base64生成的安全{codeVerifier}",
    "nonce": "{nonce}"
  }
}

It is much simpler under implicit.

1
2
3
4
5
{
  "authorizationGrantType": "implicit",
  "responseType": "token",
  "attributes": {}
}

3.2 Fixed rules section

The above is the personalized value strategy for the member variables of OAuth2AuthorizationRequest under various AuthorizationGrantType, and there are several parameters whose rules are fixed

  • clientId comes from the configuration and is a unique identifier given to us by the third-party platform.
  • authorizationUri comes from the configuration and is used to construct the request URL to the third party.
  • scopes from the configuration, is the scope of the authorization given to us by the third-party platform, which can be understood as roles.
  • state is generated automatically, to prevent csrf attacks.
  • authorizationRequestUri is the authorization request to the third-party platform, which can be set directly by the OAuth2AuthorizationRequest builder class or generated by the authorizationUri parameters above, and the construction mechanism will be analyzed later.
  • redirectUri When OAuth2AuthorizationRequest is received by the third-party platform, the third-party platform will call back this URI to respond to the authorization request, and the mechanism will be analyzed later.

The construction mechanism of authorizationRequestUri

If authorizationRequestUri is not provided explicitly, it will be constructed using the

  • responseType
  • clientId
  • scopes
  • state
  • redirectUri
  • additionalParameters are stitched into the authorizationUri parameter string according to the following rules, with the key and value of the parameter string being URI-encoded.
1
authorizationUri?response_type={responseType.getValue()}&client_id={clientId}&scope={scopes元素一个字符间隔}&state={state}&redirect_uri={redirectUri}&{additionalParameter展开进行同样规则的KV参数串}

Then the OAuth2AuthorizationRequestRedirectFilter is responsible for redirecting to the authorizationRequestUri to request authorization from the third party.

redirectUri

The third party will call redirectUri when it receives the response, and the callback has certain rules, following the path parameter rule {baseUrl}/{action}/oauth2/code/{registrationId}.

  • baseUrl is the base request path extracted from our /oauth2/authorization request.
  • action, which has two default values login, authorize, and will be populated based on the value of action when the /oauth2/authorization request contains the action parameter.
  • registrationId This goes without saying.

4. Summary

By analyzing the rules of the OAuth2AuthorizationRequest request object in detail, we should be able to roughly know the flow of the filter OAuth2AuthorizationRequestRedirectFilter.

  1. build ClientRegistration through the client configuration, which can subsequently be persisted.
  2. intercept /oauth2/authorization request and construct OAuth2AuthorizationRequest, then redirect to authorizationRequestUri to request authorization.
  3. the third party is redirected to redirect_uri for the corresponding request.

So how does Spring Security OAuth2 handle third-party callbacks accordingly? This part will be covered in the next article.

Reference https://felord.cn/oauth2-authorization-request.html