OAuth 2.0 authorization protocol that allows third-party applications to access restricted HTTP resources, like the OAuth 2.0 authorization framework, which is commonly used when people use Github and Google accounts to log on to other systems. The following is the authorization page diagram of the Coding system using Github accounts:
There are many other concepts related to OAuth 2.0, such as roles, types of authorization, and so on. Here is a summary of my OAuth 2.0 authorization brains, which I hope will help you understand the OAuth 2.0 authorization protocol.
This article will expand the OAuth 2.0 protocol with the content of the brain map, and will also build the OAuth2 client with Spring Security OAuth2. This is also the purpose of learning OAuth 2.0, and will be applied directly to the actual project to enhance the understanding of OAuth 2.0 and Spring Security.
OAuth 2.0 Role
There are four types of roles in OAuth 2.0: Resource Owner, Authorization Service, Client, Resource Service. These four roles are responsible for different tasks. To make it easier to understand, give a general flowchart first, and then expand the details separately:
OAuth 2.0 Approximate Authorization Process
Resource Owner
Resource Owner can be understood as a user, as in the example mentioned earlier when using Github to log on to Coding, when a user logs on to Coding using a GitHub account, Coding needs to know the user's image, username, email, etc. in the GitHub system. This account information belongs to the user so it is not difficult to understand Resource Owner.It is also not easy for Coding to request the desired user information from GitHub, which, for security reasons, requires at least the consent of the user (resource Owner).
Resource Server
Once you know the resource Owner, you know what a resource server is. In this case, the user account information is stored on the GitHub server, so the resource server here is the GitHub server.The GitHub server is responsible for saving and protecting users'resources. Any other third-party system that wants to use this information needs to be authorized by the resource Owner and interact with each other according to the OAuth 2.0 authorization process.
Client
Once you know the resource Owner and the resource server, the role of the client in OAuth is relatively easy to understand, simply speaking, the client is the system that you want to get the resources from. For example, when you log in to Coding using GitHub, Coding is the client in OAuth.Clients are mainly responsible for initiating authorization requests, acquiring AccessToken, and acquiring user resources.
Authorization Server
If the resource Owner, the resource server and the client cannot complete the OAuth authorization, the authorization server is also required.Authorization servers in OAuth are responsible for not only interacting with users (resource Owners), but also generating AccessToken, validating AccessToken, which is a very important part of OAuth authorization. In this case, authorization servers are GitHub's servers.
Summary
In OAuth, four roles, Resource Owner, Authorization Service, Client, Resource Service, are represented in the example of using GitHub to login to Coding:
- Resource Owner: GitHub user
- Authorization service: GitHub server
- Client: Coding System
- Resource Services: GitHub Server
Authorized service servers and resource servers can be set up separately (ghosts know how GitHub was built).A single authorization service can be created in the micro-server architecture. Resource service services can be multiple, such as user resources, warehouse resources, etc. They can be freely divided into services according to their needs.
OAuth2 Endpoint
OAuth2 has three important Endpoints: the authorization Endpoint, the Token Endpoint node on the authorization server, and an optional redirection Endpoint on the client.
- Authorize Endpoint: Use Authorize Endpoint to obtain authorization for the resource Owner
- Token Endpoint: Client gets token
- Redirect Endpoint: Authorization server uses redirect Endpoint to return authorization response to client
Authorization Type
With the four OAuth roles, you should have a general understanding of the OAuth protocol, but you may still be confused about how the roles in OAuth interact. It's okay to go on and look at the authorization types to see how the roles in OAuth fulfill their duties and gain a better understanding of OAuth.There are four types of authorization defined in OAuth:
- Authorization Number Authorization
- Roomside Certificate Authorization
- Password Authorization for Resource Owner
- Implicit authorization
Different authorization types can be used in different scenarios.
Authorization Number Authorization
This is our common form of authorization (e.g. login to Coding with GitHub account). There will be three OAuth roles involved in the whole authorization process: resource Owner, authorization server and client. This is called authorization number authorization because authorization server will issue a code to the client side in the interactive process, and then the client side will continue to enter with the code issued by the authorization server.Line authorization such as requesting authorization server to issue AccessToken.
To make it easier to understand and bring the content of the picture above into a real scene, describe the whole process in words:
- A.1. Users visit the Coding landing page (https://coding.net/login) and click the Github landing button;
- A.2. The Coding server redirects the browser to Github's authorization page (https://github.com/login/oauth/authorize?Client_id=a5ce5a6c7e8c39567ca0&scope=user:email&redirect_uri=https://coding.net/api/oauth/github/callback&response_type=code), with client_id and redirect_uri parameters on the URL;
- B.1. User input username and password to log on to Github;
- B.2. The user clicks on the authorization button and agrees to authorize;
- C.1, Github authorization server returns code;
- C.2, Github by redirecting the browser to the redirect_uri address passed in step A.2 ( https://coding.net/api/oauth/github/callback&response_type=code);
- D. Coding gets the code and calls the Github authorization server API to get AccessToken. Since this step is not captured in the browser behind the Coding server, it is basically to use code to access the access_token node of GitHub to get AccessToken.
The above is the general authorization process, most of which is the interaction between the client and the authorization server. Several parameters are explained as follows:
- client_id: Appid registered with Github to mark clients
- redirect_uri: Understand a callback that authorizes the server to redirect the browser to this address with a code parameter after verifying information such as client and user names
- code: A credential returned by the authorization server to obtain AccessToken
- state: passed by the client to the authorization server, which normally returns as is when redirect_uri is called
Authorization Number Authorization Request
In the Authorization with Authorization Number mode, when a client requests authorization, it needs to request authorization in accordance with the specification. The following are the parameters needed to initiate authorization with Authorization Number Authorization:
If you use Github to log in to the Coding example at https://github.com/login/oauth/authorize? Client_id=a5ce5a6c7e8c39567ca0&scope=user:email&redirect_uri=https://coding.net/api/oauth/github/callback&response_type=code Authorization Request URL, there are client_id, redirect_uri parameters. As for what there is no response_type, guess it is because Github has been omitted.
Authorization Number Authorization Response
If the user agrees to authorize, the authorization server also returns the standard OAuth authorization response:
For example, if https://coding.net/api/oauth/github/callback&response_type=code in Coding login, the Github authorization server calls back the callback address of Coding and returns the code, state parameters when the user agrees to authorize.
Client Certificate Authorization
The process of authorizing room-side credentials involves only client interaction with the authorization server, which is simpler than the other three types of authorization.Generally, this authorization mode is used for authorization between services, such as application server (A) and data server (B) in AWS. A server needs authorization from authorization server before it can access B server to obtain data.
It's a simple two-step process to complete the certificate authorization, but when using the certificate authorization, the client is directly accessing the authorization server to obtain the AccessToken interface.
Client Certificate Authorization Request
In the room-side credential authorization, the client directly initiates AccelessTokenEndpoint to obtain the AccessToken request authorization server with the following request parameters:
Note: AccessTokenEndpoint uses HTTP Basic authentication in OAuth and also needs to carry Authorization request headers when requesting, such as when testing requests with postman:
The username and password parameters are generated by the authorization server for client_id and client_secret in the OAuth protocol.
Client Certificate Authorization Response
The authorization server returns token after verifying client_id and client_secret:
{ "access_token":"2YotnFZFEjr1zCsicMWpAA", "token_type":"example", "expires_in":3600, "example_parameter":"example_value" }
User Certificate Authorization
User credential authorization is similar to client credential authorization except that the user name and password are provided when authorizing.
The basic process is as follows:
- A. Clients need to know the user's credentials first
- B. Use user credentials to obtain AccessToken
- C. Authorization server validates client and user credentials and returns AccessToken
User Certificate Authorization Request
User credential authorization request parameters have more username and pwssword parameters than client credential authorization:
Note: HTTP Basic authentication is used when acquiring Token, just like client certificate authorization.
User Certificate Authorization Response
User certificate authorization response is similar to client certificate authorization:
{ "access_token":"2YotnFZFEjr1zCsicMWpAA", "token_type":"example", "expires_in":3600, "refresh_token":"tGzv3JOkF0XG5Qx2TlKWIA", "example_parameter":"example_value" }
Implicit authorization
Implicit authorization is used to acquire AccessToken, but unlike user credential authorization and client authorization, it acquires AccessToken when accessing authorized Endpoint instead of Token Endpoing, and AccessToken is returned as a Section of redirect_uri.
- A.1, A.2, Browser access to authorized Endpoint of server supporting implicit authorization;
- B.1. User input account password;
- B.2. The user clicks on the authorization button and agrees to authorize;
- C. Authorized server uses redirect_uri to return AccessToken;
- D. Authorization server redirects browser to redirect_uri and carries AccessToken such as: http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA&state=xyz&token_type=example&expires_in=3600;
- The address of D, redirect_uri refers to a Web resource client
- E. Web resource client returns a script
- F, Browser Execution Script
- D. Client gets AccessToken
Implicit authorization is not easy to understand, but a careful comparison of client certificate authorization and user certificate authorization reveals that it is safer to do so without knowing user or client credentials.
Implicit Authorization Request
When implicit authorization is used again, the following request parameters are required:
Implicit authorization response
The implicit authorization response parameter is returned through a redirect_uri callback, such as http://example.com/cb#access_token=2YotnFZFEjr1zCsicMWpAA &state=xyz&token_type=example&expires_in=3600. It is important to note that the response parameter is in the form of a Segment, not a normal URL parameter.
OAuth2 Client
As mentioned earlier, there are four roles in the OAuth protocol. This section uses Spring Boot to implement an OAuthClient that logs on to GitHub. To log on to GitHub using the OAuth2 protocol, you first need to apply within the cloud GitHub:
Apply for OAuth App
Fill in the required information
The Authorization callback URL in the figure above is that GitHub redirects the browser to the address after the redirect_uri user agrees to authorize it, so first add an interface to the local OAuth client service to respond to GitHub's redirection request.
Configure OAuthClient
Familiarizing ourselves with the OAuth2 protocol, we are configuring a GitHub authorization client using Spring Security OAuth2, using the Authentication Code authorization process (you can go through the Authentication Code authorization flowchart first), sample project dependency:
<dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-oauth2-client</artifactId> </dependency> <dependency> <groupId>org.springframework.boot</groupId> <artifactId>spring-boot-starter-web</artifactId> </dependency>
Spring Security OAuth2 integrates commonly used authorization servers such as Github and Goolge by default, because the configuration information for these commonly used authorization services is public. Spring Security OAuth2 has already been configured for us. All development needs to do is specify the necessary information such as clientId, clientSecret.
Spring Security OAuth2 uses Registration as the client's configuration entity:
public static class Registration { //Authorization Server Provider Name private String provider; //Client id private String clientId; //Client credentials private String clientSecret; ....
Below is information about a previously registered GitHub OAuth App:
spring.security.oauth2.client.registration.github.clientId=5fefca2daccf85bede32 spring.security.oauth2.client.registration.github.clientSecret=01dde7a7239bd18bd8a83de67f99dde864fb6524``
Configure redirect_uri
Spring Security OAuth2 has a built-in redirect_uri template: {baseUrl}/login/oauth2/code/{registrationId}, where the registrationId is extracted from the configuration:
spring.security.oauth2.client.registration.[registrationId].clientId=xxxxx
As in the GitHub client configuration above, since the specified registrationId is github, the redirection uri address is:
{baseUrl}/login/oauth2/code/github
Start Server
Once the OAuth2 client and redirect Uri are configured, start the server and open the browser to http://localhost:8080/.Open for the first time because no authentication will redirect the browser to GitHub's authorized Endpoint:
Common Authorization Server (CommonOAuth2Provider)
Spring Security OAuth2 has some commonly used authorization server configurations built into it, which are in the CommonOAuth2Provider:
public enum CommonOAuth2Provider { GOOGLE { @Override public Builder getBuilder(String registrationId) { ClientRegistration.Builder builder = getBuilder(registrationId, ClientAuthenticationMethod.BASIC, DEFAULT_REDIRECT_URL); builder.scope("openid", "profile", "email"); builder.authorizationUri("https://accounts.google.com/o/oauth2/v2/auth"); builder.tokenUri("https://www.googleapis.com/oauth2/v4/token"); builder.jwkSetUri("https://www.googleapis.com/oauth2/v3/certs"); builder.userInfoUri("https://www.googleapis.com/oauth2/v3/userinfo"); builder.userNameAttributeName(IdTokenClaimNames.SUB); builder.clientName("Google"); return builder; } }, GITHUB { @Override public Builder getBuilder(String registrationId) { ClientRegistration.Builder builder = getBuilder(registrationId, ClientAuthenticationMethod.BASIC, DEFAULT_REDIRECT_URL); builder.scope("read:user"); builder.authorizationUri("https://github.com/login/oauth/authorize"); builder.tokenUri("https://github.com/login/oauth/access_token"); builder.userInfoUri("https://api.github.com/user"); builder.userNameAttributeName("id"); builder.clientName("GitHub"); return builder; } }, FACEBOOK { @Override public Builder getBuilder(String registrationId) { ClientRegistration.Builder builder = getBuilder(registrationId, ClientAuthenticationMethod.POST, DEFAULT_REDIRECT_URL); builder.scope("public_profile", "email"); builder.authorizationUri("https://www.facebook.com/v2.8/dialog/oauth"); builder.tokenUri("https://graph.facebook.com/v2.8/oauth/access_token"); builder.userInfoUri("https://graph.facebook.com/me?fields=id,name,email"); builder.userNameAttributeName("id"); builder.clientName("Facebook"); return builder; } }, OKTA { @Override public Builder getBuilder(String registrationId) { ClientRegistration.Builder builder = getBuilder(registrationId, ClientAuthenticationMethod.BASIC, DEFAULT_REDIRECT_URL); builder.scope("openid", "profile", "email"); builder.userNameAttributeName(IdTokenClaimNames.SUB); builder.clientName("Okta"); return builder; } }; private static final String DEFAULT_REDIRECT_URL = "{baseUrl}/{action}/oauth2/code/{registrationId}"; }
There are four authorization server configurations in the CommonOAuth2Provider: OKTA, FACEBOOK, GITHUB, GOOGLE.The configuration items redirect_uri, Token Endpoint, Authorization Endpoint, scope in the OAuth2 protocol are configured here:
GITHUB { @Override public Builder getBuilder(String registrationId) { ClientRegistration.Builder builder = getBuilder(registrationId, ClientAuthenticationMethod.BASIC, DEFAULT_REDIRECT_URL); builder.scope("read:user"); builder.authorizationUri("https://github.com/login/oauth/authorize"); builder.tokenUri("https://github.com/login/oauth/access_token"); builder.userInfoUri("https://api.github.com/user"); builder.userNameAttributeName("id"); builder.clientName("GitHub"); return builder; } }
Redirected Uri Interception
Brain melon seed is a bit confused, I feel I have configured clientid and clientSecret to complete an OAuth2 client, some of the reasons are not clear yet.What's most interesting is how redirection Uri is handled.
Spring Security OAuth2 is based on Spring Security. You have read the Spring Security article before and know how it works based on filters. If you don't know, this article is recommended: Spring Security Architecture .Looking in the source code, we found a suspect Security filter:
- OAuth2LoginAuthenticationFilter: Filter for handling OAuth2 authorization
This Security filter has a constant:
public static final String DEFAULT_FILTER_PROCESSES_URI = "/login/oauth2/code/*";
Is a matcher, as previously mentioned, there is a default redirect_uri template in Spring Security OAuth2: {baseUrl}/{action}/oauth2/code/{registrationId}, /login/oauth2/code/* exactly matches the redirect_uri template, so OAuth2LoginAuthenticationFilter will execute with user's consent and authorization, and it is constructed as follows:
public OAuth2LoginAuthenticationFilter(ClientRegistrationRepository clientRegistrationRepository, OAuth2AuthorizedClientService authorizedClientService) { this(clientRegistrationRepository, authorizedClientService, DEFAULT_FILTER_PROCESSES_URI); }
OAuth2LoginAuthenticationFilter takes out the code returned by the authorization server, authenticates it with AuthenticationManager (gets AccessToken), and then removes some of the code from the source code:
@Override public Authentication attemptAuthentication(HttpServletRequest request, HttpServletResponse response) throws AuthenticationException { MultiValueMap<String, String> params = OAuth2AuthorizationResponseUtils.toMultiMap(request.getParameterMap()); //Check no code and state if (!OAuth2AuthorizationResponseUtils.isAuthorizationResponse(params)) { OAuth2Error oauth2Error = new OAuth2Error(OAuth2ErrorCodes.INVALID_REQUEST); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); } //Get OAuth2AuthorizationRequest OAuth2AuthorizationRequest authorizationRequest = this.authorizationRequestRepository.removeAuthorizationRequest(request, response); if (authorizationRequest == null) { OAuth2Error oauth2Error = new OAuth2Error(AUTHORIZATION_REQUEST_NOT_FOUND_ERROR_CODE); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); } //Remove ClientRegistration String registrationId = authorizationRequest.getAttribute(OAuth2ParameterNames.REGISTRATION_ID); ClientRegistration clientRegistration = this.clientRegistrationRepository.findByRegistrationId(registrationId); if (clientRegistration == null) { OAuth2Error oauth2Error = new OAuth2Error(CLIENT_REGISTRATION_NOT_FOUND_ERROR_CODE, "Client Registration not found with Id: " + registrationId, null); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); } String redirectUri = UriComponentsBuilder.fromHttpUrl(UrlUtils.buildFullRequestUrl(request)) .replaceQuery(null) .build() .toUriString(); //Authenticate, Get AccessToken OAuth2AuthorizationResponse authorizationResponse = OAuth2AuthorizationResponseUtils.convert(params, redirectUri); Object authenticationDetails = this.authenticationDetailsSource.buildDetails(request); OAuth2LoginAuthenticationToken authenticationRequest = new OAuth2LoginAuthenticationToken( clientRegistration, new OAuth2AuthorizationExchange(authorizationRequest, authorizationResponse)); authenticationRequest.setDetails(authenticationDetails); OAuth2LoginAuthenticationToken authenticationResult = (OAuth2LoginAuthenticationToken) this.getAuthenticationManager().authenticate(authenticationRequest); ... return oauth2Authentication; }
Get AccessToken
As mentioned earlier, OAuth2LoginAuthentication Filter uses Authentication Manager for OAuth2 authentication. Authentication Manager in Spring Security usually uses Provider Manager for authentication, so there is one OAuth2LoginAuthentication Provider in Spring Security OAuth2 for obtaining AccessToken:
public class OAuth2LoginAuthenticationProvider implements AuthenticationProvider { private final OAuth2AccessTokenResponseClient<OAuth2AuthorizationCodeGrantRequest> accessTokenResponseClient; private final OAuth2UserService<OAuth2UserRequest, OAuth2User> userService; private GrantedAuthoritiesMapper authoritiesMapper = (authorities -> authorities); .... @Override public Authentication authenticate(Authentication authentication) throws AuthenticationException { OAuth2LoginAuthenticationToken authorizationCodeAuthentication = (OAuth2LoginAuthenticationToken) authentication; // Section 3.1.2.1 Authentication Request - https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest // scope // REQUIRED. OpenID Connect requests MUST contain the "openid" scope value. if (authorizationCodeAuthentication.getAuthorizationExchange() .getAuthorizationRequest().getScopes().contains("openid")) { // This is an OpenID Connect Authentication Request so return null // and let OidcAuthorizationCodeAuthenticationProvider handle it instead return null; } OAuth2AccessTokenResponse accessTokenResponse; try { OAuth2AuthorizationExchangeValidator.validate( authorizationCodeAuthentication.getAuthorizationExchange()); //Visit GitHub TokenEndpoint to get Token accessTokenResponse = this.accessTokenResponseClient.getTokenResponse( new OAuth2AuthorizationCodeGrantRequest( authorizationCodeAuthentication.getClientRegistration(), authorizationCodeAuthentication.getAuthorizationExchange())); } catch (OAuth2AuthorizationException ex) { OAuth2Error oauth2Error = ex.getError(); throw new OAuth2AuthenticationException(oauth2Error, oauth2Error.toString()); } ... return authenticationResult; } @Override public boolean supports(Class<?> authentication) { return OAuth2LoginAuthenticationToken.class.isAssignableFrom(authentication); } }
Reference material
Focus on the public number Architecture Digest, which is a great daily article in the field of architecture. It covers top-line Internet companies'application architectures (high availability, high performance, high stability), big data, machine learning, Java architecture and other hot areas.