preface

OAuth is an open network standard about authorization, which is widely used all over the world. Various online services such as Facebook, Twitter and Google offer authentication mechanisms based on the OAuth specification.

OAuth is typically used for authentication in apis that are widely exposed to third parties. In other words, assuming that online service A with user registration function (such as Tencent QQ) has made its API public, online service B(such as Baidu web disk) can use the various functions provided by the API of these online service A. In this case, when a user has been registered in QQ baidu network disk online service, the network disk online service will want to access QQ to use the user information. At this time, judge whether to allow the web disk to use the user registered in QQ information mechanism is OAuth.

OAuth

The key to OAuth is that users do not need to re-enter their QQ password to use Baidu’s online service. To implement this mechanism, the authentication process uses a Web page provided by QQ to ask users to confirm whether they are allowed to access the qq account information provided to Baidu’s online service. If not already logged in to QQ, users are required to enter a password, which is only logged in to QQ and not sent to Baidu’s online service.

If the OAuth access succeeds, the web disk can obtain a token named Access Token from QQ. With this token, users can access the information allowed in QQ.

The main advantage of OAuth is that it is a widely recognized authentication mechanism and has been standardized.

OAuth2.0 authentication process

Grant Type role
Authorization Code This is suitable for Web applications that do a lot of processing on the server
Implicit Suitable for smart phone applications and applications that use JavaScript clients for heavy processing
Resource Owner Password Credential This mode is applicable to applications that do not use the server
Client Credentials This method is applicable to applications that do not implement authentication by user

The Resource Owner Password Credential mode is that there is no web site B, and the client gets the Password directly from the user and the access token from server A. This licensing pattern can then be applied to client applications developed in-house.

When using the Resource Owner Password Credential pattern for authentication, the API needs to have parameters in application/ X-www-form-urlencoded form (that is, form), Utf-8 is encoded and sent to the server

Key value content
grant_type The character string password. Indicates that Resource Owner Password Credential is used
username User name for logging in
password Login password
scope Specify the range of permissions to allow access

The scope column at the end is used to specify the scope of permission to access. The name of the permission range can be defined by the online service alone and can use all ASCII text characters except Spaces, double quotes, and backslashes. By using scope, the external service (online service B) can obtain the token while limiting the scope of access. It can also display a prompt to the user, such as “the service will access the following information”. Although scope is not required, it is recommended to define it beforehand.

Example:

POST/v1 / oauth2 token HTTP / 1.1 Host: api.example.com Authorization: Basic XXXXXXXXXXXXXXXXXXXXXXXXX content-type: application/x-www-form-urlencoded grant_type=password&username=zhang&password=zhang&scope=apiCopy the code

The sample request also has an additional Authorization header, called Client Authorization. It is used to describe who the service (online service B) that needs to be accessed is.

When applications log in to online services, these services will issue Client ID and Client Secret to them as user names and passwords, and put them into the Authorization head after Base64 encoding in the form of Basic authentication. Client ID and Client Secret can be used arbitrarily. The server can identify the application identity of the currently accessed service based on these information. For example, the Client ID and Client Secret can be used when the server limits the number of times an application can access the API or wants to shield some unauthorized applications.

When the correct information is sent to the server, the server returns a JSON response in the following format:

HTTP/1.1 200 OK Content-Type: application/json cache-Control: no-store Pargma: no-cache {"access_token": 'zskldjflsdjflksjdflkjsd' "token_type": "bearer", "expires_in": 2629743, "refresh_token": 'ajsldkjflskdfjldfg' }Copy the code

Bearer in token_type is the token type used in OAuth2.0 as defined in RFC6750. Access_token is the access token required for future access. When accessing the API in the future, simply send the token information along with it. There is no need to send ClientID and ClientSecret messages again. Because different clients get specific Access tokens from the server, the server can also use access token information to identify the application even without ClientID.

As defined by RFC6750, there are three ways for clients to send bearer token information to the server side:

  • Add to the header of the request information
  • Add to the request message body
  • Add to the URL as a query parameter

1. When adding the token information to the header of the request message, the client uses the Authorization header and specifies the token content in the following form:

GET /v1/users/ HTTP/1.1 Host: api.example.com Authorization: Bearer of ZSKLDJFLSDJFLKSJDFLKJSDCopy the code

2. When token information is added to the request message body, the Content-Type of the request message needs to be set as Application/X-www-form-urlencoded, parameters in the message body are named by access_Token, and toKAN information is appended

POST /v1/ Users HTTP/1.1 Host: api.example.com context-type: application/x-www-form-urlencoded access_token=zskldjflsdjflksjdflkjsdCopy the code

3. When you add a token parameter as a query parameter, you can specify the token information after the query parameter access_token.

GET /v1/users? Access_token = ZSKLDJFLSDJFLKSJDFLKJSD HTTP / 1.1 Host: api.example.comCopy the code

Validity period and update of the Access token

When the client obtains the access token, it also gets a data named expiRES_in in the response message, which indicates the number of seconds after the currently obtained Access token expires. When this specified number of seconds is exceeded, the Access Token expires. When the access token expires, if the client still uses it to access the service, the server will return an invalid_token error or 401 error code.

HTTP/1.1 401 Unauthorized Content-Type: application/json Cache-Control: no-store Pragma: no-cache {"error": "invaild_token" }Copy the code

When an inVALID_token error occurs, the client needs to apply for the Access token from the server again using the refresh token. The refresh token here is another token information that the client needs to apply for the Access token again, which can be obtained along with the Access token.

In a request to refresh the Access Token, the client can specify refresh_token in the grent_type parameter and send it to the server with refresh_token.

POST/V1 / OAUTH2 / Token HTTP/1.1 Host: api.example.com Authorization: Bearer of LDJFLSDJFLKSJDFLKJSD Content-Type: application/x-www-form-urlencoded grent_type=refresh_token&refresh_tokne=ajsldkjflskdfjldfgCopy the code

Encapsulate Axios to implement insensitive refresh token

  • utils/oauth.js
const TokenKey = 'access_token'
const ExpiresKey = 'expires_in'
const TokenTypeKey = 'bearer'
const RefreshTokenKey = 'refresh_token'

export function getToken () {
  return localStorage.getItem(TokenTypeKey) + ' ' + localStorage.getItem(TokenKey)
}

export function getRefreshToken () {
	return localStorage.getItem(RefreshTokenKey)
}

export function setToken (data) {
  const ExpiresTime = new Date().getTime() + data.expires_in * 1000

  localStorage.setItem(TokenKey, data.access_token)
  localStorage.setItem(ExpiresKey, ExpiresTime)
  localStorage.setItem(TokenTypeKey, data.token_type)
  localStorage.setItem(RefreshTokenKey, data.refresh_token)
}

export function removeToken () {
  localStorage.removeItem(TokenKey)
  localStorage.removeItem(ExpiresKey)
  localStorage.removeItem(TokenTypeKey)
  localStorage.removeItem(RefreshTokenKey)
}
Copy the code
  • request.js
import axios from 'axios' import Vue from 'vue' import { removeToken } from '@/utils/oauth' const server = axios.create({ baseURL: baseUrl, withCredentials: TokenLock = false function refreshToken () {if (! window.tokenLock) { server.put('/oauth/refresh').then(({data}) => { const ExpiresTime = new Date().getTime() + data.expires_in * 1000 localStorage.setItem('access_token', data.access_token) localStorage.setItem('expires_in', ExpiresTime) localStorage.setItem('token_type', data.token_type) localStorage.setItem('refresh_token', data.refresh_token) }) window.tokenLock = true } } server.interceptors.request.use(req => { req.headers['Authorization']  = `${localStorage.getItem('token_type')} ${localStorage.getItem('access_token')}` return req }, error => { return Promise.reject(error) }) server.interceptors.response.use(rep => { // Token const expiresTimeStamp = new if refresh_token is used 10 minutes before the expiration time Date(Number(localStorage.getItem('expires_in'))).getTime() - new Date().getTime() if (expiresTimeStamp < 10 * 60 * 1000 && expiresTimeStamp > 0) { if (rep.config.url.indexOf('current') < 0) { refreshToken() } } return rep }, Error => {if (error.response.status === 401) {// 401 error: Token invalid or login failed If (location.href. IndexOf ('login') > 0) {vue.prototype.$notify.error({title: 'error ', message: error.response.data.message }) return } removeToken() location.reload() } return Promise.reject(error) }) export default  serverCopy the code

At the end

For more articles, please go to Github, if you like, please click star, which is also a kind of encouragement to the author.