Micro services unified authentication and authorization of Go language implementation
Dear readers, happy Year of the Rat. I wish you good health and good luck in the coming year.
The recent serious epidemic is a special time, we must pay attention to protection. Many provinces have delayed the start of business, and most Internet companies will start telecommuting next week. You can use a few days at home to learn and recharge your batteries, but you can’t get out anyway (🙂🙂🙂).
Today, THE author is going to write about the component practice related to Go micro-services. The author came into contact with Go language several years ago and began to engage in the development of Go micro-services last year. In the process, he and his partner jointly wrote a book called Go High Concurrency and Micro-service Practice, which will be published and listed soon. This article is the first version of which is intercepted to read, introduce the micro services unified authentication and authorization of Go language implementation.
1 introduction
Unified authentication and authorization are the basic functions of the microservice architecture. Different from individual application architectures, the authentication and authorization are centralized. When services are split, authentication and authorization for each microservice becomes very decentralized. Therefore, unified authentication and authorization functions will be integrated in the microservice architecture as a crosscutting concern.
2 Common authentication and authorization schemes
Common authentication and authorization schemes include OAuth, distributed Session, OpenID and JWT. The following four schemes will be introduced respectively.
2.1 request
OAuth2 related theory introduction mainly comes from the OAuth2 official documents, related address to https://tools.ietf.org/html/rfc6749.
The purpose of OAuth protocol is to provide a secure, open and simple standard for user resource authorization. The introduction on the official website is as follows:
An open protocol to allow secure API authorization in a simple and standard method from web, mobile and desktop applications.
Because OAuth1 is not compatible with OAuth2, and the signature logic is too complex and the authorization process is too single, I will not talk about it here. The following focuses on the OAuth2 authentication process, which is the mainstream authorization process in the current Web application.
OAuth2 is the current industry standard for authorization and focuses on providing a simple client-side development approach for the authorization process for Web applications, desktop applications, mobile devices, and indoor devices. It provides third-party applications with limited access to HTTP services. Resource owners can authorize third-party applications to obtain HTTP services, or third-party applications can obtain access rights in their own names.
role
There are four main types of roles in OAuth2
- Resource Owner An entity that grants access rights to protected resources. The entity can be a user and is called end-user.
- Resource Server A resource server that holds protected resources and allows access requests with access tokens to access protected resources.
- Client A client authorized by the resource owner to access protected resources on behalf of the resource owner.
- Authorization Server Authenticates the authorization of resource owners and sends access tokens to clients.
In many cases, the resource server and the authorization server are one and the same, the authorization server for authorization interaction and the resource server for requesting resource interaction. But the authorization server is a separate entity that can issue access tokens that are accepted by multiple resource servers.
The agreement process
Take a look at this flow chart from the authorities:
+--------+ +---------------+ | |--(1)- Authorization Request ->| Resource | | | | Owner | | |<-(2)-- Authorization Grant ---| | | | +---------------+ | | | | +---------------+ | |--(3)-- Authorization Grant -->| Authorization | | Client | | Server | | |<-(4)----- Access Token -------| | | | +---------------+ | | | | +---------------+ | |--(5)----- Access Token ------>| Resource | | | | Server | | |<-(6)--- Protected Resource ---| | +--------+ +---------------+Copy the code
This is an abstract interaction flowchart for the OAuth2 role, consisting of the following six steps:
- The client requests authorization from the resource owner;
- The resource owner agrees to authorize and returns an Authorization Grant, which represents the resource owner’s Authorization credentials;
- Client with authorization request authorization server for authentication, request access token;
- The authorization server authenticates the client, authenticates the authorization and, if valid, returns the access token;
- The client requests access to protected resources from the resource server with access permission.
- The resource server validates the access token and, if valid, accepts the access request and returns the protected resource. Client authorization type
In order to obtain access tokens, the client must obtain authorization from the resource owner. OAuth2 provides four authorization types by default, and of course provides an extended mechanism for defining additional authorization types. The default four authorization types are as follows:
- Authorization Code Indicates the authorization code type
- Implicit simplified types (also called implicit types)
- Resource Owner Password Credentials Password type
- Client Credential Indicates the client type
The following describes the common authorization code and password types.
Authorization Code Type
Authorization code allows the resource owner to directly interact with the authorization server for authorization through redirection, avoiding the leakage of the resource owner information to the client. It is the most complete authorization type with the most rigorous process. However, the client must be able to interact with the resource owner’s proxy (usually a Web browser) and accept requests from the authorization server (redirecting the authorization code). The authorization process is as follows:
+----------+ | Resource | | Owner | | | +----------+ ^ | (2) +----|-----+ Client Identifier +---------------+ | -+----(1)-- & Redirection URI ---->| | | User- | | Authorization | | Agent -+----(2)-- User authenticates --->| Server | | | | | | -+----(3)-- Authorization Code ---<| | +-|----|---+ +---------------+ | | ^ v (1) (3) | | | | | | ^ v | | +---------+ | | | |>---(4)-- Authorization Code ---------' | | Client | & Redirection URI | | | | | |<---(5)----- Access Token -------------------' +---------+ (w/ Optional Refresh Token)Copy the code
- The client directs the resource owner’s user agent to the endpoint of the authorization server, usually through redirection. The information submitted by the client should include the client identifier, requested scope, local state and the redirection URI used to return the authorization code.
- The authorization server authenticates the resource owner (through the user agent) and confirms whether the resource owner approves or denies the client access request.
- If the resource owner grants access to the client, the authorization server calls back the redirect address provided by the client by redirecting the user agent and adds the authorization code and any local state previously provided by the client to the redirect address;
- The client requests the access token from the authorization server with the authorization code obtained in the previous step. In this step, both the authorization code and the client are authenticated by the authorization server. The client needs to submit the redirection address for obtaining the authorization code.
- The authorization server authenticates the client and authenticates the authorization code, ensuring that the received redirection address matches the redirection address used in step 3 to obtain the authorization code. If valid, the access Token is returned, along with a Refresh Token that may be returned.
Type a password
Resource owner password Credentials Require the resource owner to hand over the password credentials to the clients, and the clients directly obtain authorization from the authorization server using their own information. In this case, the resource owner is required to be highly trusted with the client, and the client is not allowed to store password credentials. This type of authorization applies to clients that can obtain the credentials (such as user names and passwords) of the resource owners. The authorization process is as follows:
+----------+
| Resource |
| Owner |
| |
+----------+
v
| Resource Owner
(1) Password Credentials
|
v
+---------+ +---------------+
| |>--(2)---- Resource Owner ------->| |
| | Password Credentials | Authorization |
| Client | | Server |
| |<--(3)---- Access Token ---------<| |
| | (w/ Optional Refresh Token) | |
+---------+ +---------------+
Copy the code
- The resource owner provides credentials such as user names and passwords to the client.
- The client carries the credentials of the resource owner (username and password) and requests the access token from the authorization server.
- The authorization server authenticates the client and validates the resource owner’s credentials and, if valid, returns an access Token and possibly a Refresh Token.
The token refresh
The access token obtained by the client from the authorization server is generally invalid. When the access token expires, the client with valid user credentials can request the access token from the authorization server again. However, a client without user credentials can obtain a new access token from the authorization server through the Refresh token returned with the last access token.
2.2 Distributed Session
2.2.1 What is Session and What is Cookie?
HTTP is a stateless protocol. Once the data exchange is complete, the connection between the client and server is closed and a new connection needs to be established to exchange data again. This means that the server cannot track the session from the connection.
A session is a sequence of actions a user takes after logging into a web site, such as browsing for items to add to a cart and making a purchase. Session tracing is a common technique used in Web applications to track a user’s entire Session. Common Session tracking techniques are cookies and sessions.
A Cookie is actually a small piece of text information. The client requests the server, and if the server needs to record the user state, it issues a Cookie to the client browser using response. The client saves the Cookie.
When the browser requests the site again, the browser submits the requested URL along with the Cookie to the server. The server checks the Cookie to identify the user’s state. The server can also modify the contents of the Cookie as needed.
Session is another mechanism for recording the client’s state, except that cookies are stored in the client browser, while sessions are stored on the server. When the client browser accesses the server, the server records the client information in some form
It’s on the server. So that’s Session. The client browser only needs to look up the client’s status from the Session when revisiting.
Each user accesses the server and establishes a session. How does the server identify the unique identity of the user? In fact, when a user establishes a connection with the server, the server automatically assigns a SessionId to the user.
In simple terms, cookies identify users by logging information on the client side, while sessions identify users by logging information on the server side.
2.3 the OpenID
Some sites see that OpenID login is allowed, such as using a Facebook account or a Google account.
OpenID is very similar to OAuth. But they are fundamentally two very different things:
- OpenID: For Authentication only, it allows you to log in to multiple sites with the same account. It simply endorses your legal identity, and when you log in to a site with your Facebook account, that site has no access to your Facebook data.
- OAuth: Used for Authorisation, allowing the authorized party to access the user data of the authorized party.
2.4 JWT
JWT, as an open standard, is either compact (fast, small) or self-contained (self-contained, payload will contain all the information the user needs, avoiding multiple queries to the database). Defines secure JSON objects for sending between parties.
The reason for introducing JWT is that JWT serves well as a vehicle for the Access and Refresh tokens described in the previous section, which is a good way to securely transfer information between two sides of the Web. When only the authorized server holds the secret that issues and verifies the JWT, then only the authorized server can verify the validity of the JWT and send the JWT with the signature, which only guarantees the validity and security of the TOKEN based on the JWT.
The composition of JWT
The JWT format is generally as follows:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiY2FuZyB3dSIsImV4cCI6MTUxODA1MTE1NywidXNlcklkIjoiMTIzNDU2In0.IV4XZ0y0nMp mMX9orv0gqsEMOxXXNQOE680CKkkPQcs
It consists of three parts, each of which passes. Separately, they are:
- The Header in the head
- Payload
- Signature Signature
Then we will introduce each part in detail.
Header
The head usually consists of two parts:
- Typ, usually JWT.
- Alg encryption algorithm, usually HMAC SHA256 or RSA.
A simple header example is as follows:
{
"alg": "HS256"
"typ": "JWT"
}
Copy the code
This part of the JSON is then Base64Url encoded to form the first part of the JWT:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Playload
The payload, the second part of JWT, is the vehicle used to carry valid information, mainly declarations about user entities and additional metadata. It consists of three parts:
- Registered Claims, which is a set of intended but not mandatory claims that provide a useful set of claims that can be used together. There are iss(JWT issuer), EXP (JWT expiration time), sub(JWT user), AUD (JWT recipient), etc.
- Public claims can include any information, such as user information or business expansion information.
- A claim defined by both a JWT provider and a consumer that is neither a registration statement nor a public statement.
You are not advised to add sensitive information to the payload because Base64 is decrypted symmetrically, which means that the information in the payload is visible.
A simple payload example:
{
"name": "cang wu"."exp": 1518051157."userId": "123456"
}
Copy the code
This part of the JSON will be Base64Url encoded to form the second part of the JWT:
eyJuYW1lIjoiY2FuZyB3dSIsImV4cCI6MTUxODA1MTE1NywidXNlcklkIjoiMTIzNDU2In0
Signature
To create a signature, the encoded header, the encoded payload and a secret are required. Finally, the encryption algorithm ALG defined in the header is used to encrypt and generate the signature. The pseudo-code of the generated signature is as follows:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
Copy the code
The encryption algorithm used is HMACSHA256
Secret is stored on the server to authenticate and issue the JWT, so it must be held only by the server and should not be disclosed.
Here’s a simple signature:
IV4XZ0y0nMpmMX9orv0gqsEMOxXXNQOE680CKkkPQcs
This will become part 3 of the JWT.
Finally, these three sections pass. Split to form the final JWT as follows:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJuYW1lIjoiY2FuZyB3dSIsImV4cCI6MTUxODA1MTE1NywidXNlcklkIjoiMTIzNDU2In0.IV4XZ0y0nMp mMX9orv0gqsEMOxXXNQOE680CKkkPQcs
3 Authorization Server
3.1 Overall Architecture
After the above brief introduction, we have understood the common unified authentication and authorization scheme at present. Next, we will implement a simple authentication and authorization system based on OAuth2 protocol and JWT. The system is mainly composed of two services, authorization server and resource server. Figure 11-4 shows the interaction between them:
If a client wants to access the resource information held by the user in the resource server, it first needs to request the access token from the authorization server with the user credentials. After the authorization server verifies the validity of the client and user credentials, it returns the generated access token to the client. Then the client requests the corresponding user resources with the access token to the resource server. After the resource server verifies the validity of the access token through the authorization server, the corresponding user resources will be returned.
Most of the time, the authorization server and the resource server are combined into one, that is, they can issue access tokens, but also limited access to user resources. It is also possible to divide their responsibilities in more detail, with the authorization server primarily responsible for token issuance and token validation, and the resource server responsible for protecting user resources, allowing only requests with valid access tokens to access restricted resources.
The main responsibility of the authorization server is to issue access tokens and validate access tokens, for which we need to provide two interfaces externally:
- /oauth/token is used by clients to request access tokens with user credentials
- /oauth/check_token Is used to verify the validity of the access token and return the client and user information corresponding to the access token.
Generally speaking, each client can request an access token for a user, so a valid access token is bound to the client and the user, which means that a user grants a client access to a resource.
The authorization server we implemented next mainly includes the following modules, as shown in Figure 11-5:
- ClientDetailsService to obtain client information.
- UserDetailsService, used to obtain user information.
- TokenGrant, for different authentication processes based on authorization type, and for generating access tokens using TokenService;
- TokenService, which generates and manages tokens and uses TokenStore to store tokens;
- TokenStore, which is responsible for storing tokens.
Due to space constraints, our authorization server only provides password-type access tokens, but provides a simple and extensible mechanism that readers can extend to their own needs.
3.2 User Service and Client Service
The function types of user service and client service are to load user and client information based on corresponding unique identifiers for subsequent verification of user and client information. The user information and client information structures we define are as follows:
type UserDetails struct {
// User id
UserId int
// Unique user name
Username string
// User password
Password string
// Permissions that the user has
Authorities []string
}
// Verify that the user name and password match
func (userDetails *UserDetails)IsMatch(username string, password string) bool {
return userDetails.Password == password && userDetails.Username == username
}
type ClientDetails struct {
// Identify the client
ClientId string
// The client key
ClientSecret string
// Access token validity time in seconds
AccessTokenValiditySeconds int
// Refresh the token validity time in seconds
RefreshTokenValiditySeconds int
// Redirection address, used in authorization code type
RegisteredRedirectUri string
// The type of authorization that can be used
AuthorizedGrantTypes []string
}
// Verify that clientId and ClientSecret match
func (clientDetails *ClientDetails) IsMatch(clientId string, clientSecret string) bool {
return clientId == clientDetails.ClientId && clientSecret == clientDetails.ClientSecret
}
Copy the code
In addition to the basic information they have, the #IsMatch method is provided to verify that the account information and password match. Since our information is stored in plaintext, we can directly compare whether the information is equal or use some encryption algorithms according to the requirements of the project to avoid the plaintext storage of sensitive information.
Both UserDetailsService and ClientDetailService provide only one method for loading information according to the corresponding identifier. The interface definitions are as follows:
typeUserDetailsService interface {// load user information according to the username GetUserDetailByUsername(username string)(*UserDetails, error)}typeClientDetailService interface {/ / load client information according to the clientId GetClientDetailByClientId clientId (string) (* ClientDetails, error) }Copy the code
User information and client information can come from many sources and can be loaded from a database, from a cache, or even from other user microservices via RPC.
summary
This paper mainly introduces the concepts of unified authentication and authorization in microservices architecture, as well as the architecture and service interface involved in authorization server implementation. The TokenGrant token generator and TokenService TokenService, as well as other implementations, are covered in the next article.
Recommended reading
A collection of microservices