Lab: OAuth 2.0
Lab: OAuth 2.0
OAuth 2.0 is a protocol that allows a user to grant a third-party web site or application access to the user’s protected resources, without necessarily revealing their long-term credentials or even their identity. For example, a user could allow a social media site to access their photos without sharing their full access to their account.
Overview
In this lab, you will learn how to use OAuth 2.0 to authenticate users, authorize access to resources, obtain an access token, and refresh an access token. We will use Express and Vue.js as platforms to demonstrate how we could connect to a third-party OAuth 2.0 provider.
Objectives
In this lab, you will learn how to:
- Use OAuth 2.0 to authenticate users
- Use OAuth 2.0 to authorize access to resources
- Obtain an access token
- Refresh an access token
- Use OAuth 2.0 with Express
- Use OAuth 2.0 with Vue.js
Prerequisites
- Basic knowledge of JavaScript
- Basic knowledge of Express
- Basic knowledge of Vue.js
- Basic knowledge of RESTful APIs
- Basic knowledge of HTTP
- Basic knowledge of JSON
- Basic knowledge of Web development
OAuth 2.0 Authentication flow
In order to implement OAuth 2.0, you need to understand the basic authentication flow.
Overview
Let’s take a look at the basic OAuth 2.0 authentication flow. The following diagram shows the sequence of steps involved in the OAuth 2.0 protocol:
sequenceDiagram
participant User
participant Client
participant External Authorization Server
participant API Resource Server
User->>Client: Access
Client->>External Authorization Server: Authorization Request
External Authorization Server->>User: Authorization Prompt
User->>External Authorization Server: Authorization Grant
External Authorization Server->>Client: Authorization Code
Client->>External Authorization Server: Token Request
External Authorization Server->>Client: Access Token
Client->>API Resource Server: Access Token
API Resource Server->>Client: Protected API Resource
Client is the application that wants to access the user’s protected resources. External Authorization Server is the server that authenticates the user and issues an access token to the client. API Resource Server is the server that hosts the user’s protected resources.
Step 1: Authorization Request
When User
wants to access the Client
, it will redirect the user to the External Authorization Server
to authenticate the user.
sequenceDiagram
participant User
participant Client
participant External Authorization Server
User->>Client: Access
Client->>External Authorization Server: Authorization Request
External Authorization Server->>User: Authorization Prompt
User->>External Authorization Server: Authorization Grant
External Authorization Server->>Client: Authorization Code
After the user is authenticated, the External Authorization Server
will send an Authorization Code
to the Client
. The Client
will then exchange the Authorization Code
for an Access Token
from the External Authorization Server
.
Step 2: Token Request
After the Client
receives the Authorization Code
, it will send a Token Request
to the External Authorization Server
to obtain an Access Token
.
sequenceDiagram
participant User
participant Client
participant External Authorization Server
User->>External Authorization Server: Authorization Grant
External Authorization Server->>Client: Authorization Code
Client->>External Authorization Server: Token Request
External Authorization Server->>Client: Access Token
Step 3: Access Token
After the Client
receives the Access Token
, it will use the Access Token
to access the API Resource Server
.
sequenceDiagram
participant Client
participant API Resource Server
Client->>API Resource Server: Access Token
API Resource Server->>Client: Protected API Resource
Implementing OAuth 2.0 in Express and Vue.js
In this lab, we will use Auth0 to demonstrate how to implement OAuth 2.0 in Express and Vue.js. Auth0 is a flexible, drop-in solution to add authentication and authorization services to applications and APIs. Auth0 supports the latest OAuth 2.0 and OpenID Connect (OIDC) specifications. OpenID Connect is a simple identity layer on top of the OAuth 2.0 protocol, which allows clients to verify the identity of the end-user based on the authentication performed by an authorization server, as well as to obtain basic profile information about the end-user in an interoperable and REST-like manner. In this lab, we will use Auth0 to demonstrate how to implement OAuth 2.0 in Express and Vue.js.
Application setup
In order to simply the setup process, we have already registered an application with Auth0. The application has the following details:
- Domain:
hkbu.jp.auth0.com
- Client ID:
dxiOZbTWeGFhW4ScsQrV6rpR1IWO8VJw
- Client Secret:
AGu90xbpSYfM22Jd3RUHvXL1esG2e_m8y3yfvdm0LHHuP1WMxj7ZX9DrMGwU1879
- Callback URL:
http://localhost:3000/callback
- Logout URL:
http://localhost:3000
Login
The first step is to redirect the user to the authorize
endpoint to login the user with OAuth 2.0.
sequenceDiagram
participant User
participant Client
participant External Authorization Server
User->>Client: Access
Client->>External Authorization Server: Authorization Request
To login the user with OAuth 2.0, simply redirect the user to the authorize
endpoint with the required parameters. The required parameters are:
response_type
: The type of response that the client expects. In this case, it iscode
.client_id
: The client ID of the application.redirect_uri
: The URL to redirect the user after the authentication is successful.
There are also optional parameters:
scope
: The scope of the access request. In this case, it isopenid profile email
.state
: A random value generated by the client to maintain state between the request and the callback.
The following is an HTML example of how to login the user with OAuth 2.0. You may add it to the App.vue
of your Vue.js application to login the user with OAuth 2.0.
<a href="https://hkbu.jp.auth0.com/authorize?response_type=code&client_id=dxiOZbTWeGFhW4ScsQrV6rpR1IWO8VJw&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fcallback">Login</a>
Or, you may use JavaScript to redirect the user to the authorize
endpoint.
import { v4 as uuidv4 } from 'uuid';
location.assign('https://hkbu.jp.auth0.com/authorize?response_type=code&client_id=dxiOZbTWeGFhW4ScsQrV6rpR1IWO8VJw&redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fcallback')
If you are doing in the backend, you may use the following code to redirect the user to the authorize
endpoint. In this case, we could easily to inject a random state
value for identifying the user.
const { v4: uuidv4 } = require('uuid');
const state = uuidv4()
res.redirect(`https://hkbu.jp.auth0.com/authorize?response_type=code&client_id=dxiOZbTWeGFhW4ScsQrV6rpR1IWO8VJw&state=${state}redirect_uri=http%3A%2F%2Flocalhost%3A3000%2Fcallback`)
The External Authorization Server
will then authenticate the user.
sequenceDiagram
participant User
participant Client
participant External Authorization Server
User->>Client: Access
Client->>External Authorization Server: Authorization Request
External Authorization Server->>User: Authorization Prompt
User->>External Authorization Server: Authorization Grant
Callback
After the user is authenticated, the External Authorization Server
will redirect the user back to the redirect_uri
with an Authorization Code
. The redirect_uri
should be the URL on the backend server of Client
.
sequenceDiagram
participant User
participant Client
participant External Authorization Server
User->>Client: Access
Client->>External Authorization Server: Authorization Request
External Authorization Server->>User: Authorization Prompt
User->>External Authorization Server: Authorization Grant
External Authorization Server->>Client: Authorization Code
Client->>External Authorization Server: Token Request
External Authorization Server->>Client: Access Token
The Authorization Code
is a short-lived token that the Client
can exchange for an Access Token
. The Client
will then exchange the Authorization Code
for an Access Token
from the External Authorization Server
.
In order to receive the Authorization Code
in the backend, you need to create a route to handle the redirect_uri
. It receives the Authorization Code
from the External Authorization Server
with query parameters, and then exchanges the Authorization Code
for an Access Token
with POST
request using fetch
API. Here is an example of how to handle the redirect_uri
in Express.
router.get('/callback', async (req, res) => {
const code = req.query.code
if (!code) {
return res.status(400).send('Bad Request')
}
const response = await fetch('https://hkbu.jp.auth0.com/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
grant_type: 'authorization_code',
client_id: 'dxiOZbTWeGFhW4ScsQrV6rpR1IWO8VJw',
client_secret: 'AGu90xbpSYfM22Jd3RUHvXL1esG2e_m8y3yfvdm0LHHuP1WMxj7ZX9DrMGwU1879',
code: code,
redirect_uri: 'http://localhost:3000/callback',
scope: 'openid profile email'
})
})
if (!response.ok) {
return res.status(400).send('Bad Request')
}
const data = await response.json()
})
The above data
should be like:
{
"access_token":"eyJz93a...k4laUWw",
"refresh_token":"GEbRxBN...edjnXbL",
"id_token":"eyJ0XAi...4faeEoQ",
"token_type":"Bearer",
"expires_in":86400
}
After that, we may use the Access Token
to access the APIs provided by External Authorization Server
.
The integration with Express and Vue.js
Let’s take a look at how to integrate OAuth 2.0 with Express and Vue.js. The Client
in original diagram now is separated into two parts: Vue.js
and Express
. Vue.js
is the frontend application that redirects the user to the authorize
endpoint to login the user with OAuth 2.0. Express
is the backend server that receives the Authorization Code
from the External Authorization Server
with query parameters, and then exchanges the Authorization Code
for an Access Token
with POST
request using fetch
API.
sequenceDiagram
participant User
participant Vue.js
participant Express
participant External Authorization Server
User->>Vue.js: Access
Vue.js->>External Authorization Server: Authorization Request
External Authorization Server->>User: Authorization Prompt
User->>External Authorization Server: Authorization Grant
External Authorization Server->>Express: Authorization Code
Express->>External Authorization Server: Token Request
External Authorization Server->>Express: Access Token
Express->>Vue.js: JWT Token
Vue.js->>Express: use JWT Token to access protected resources
The following is an example of how to handle the redirect_uri
in Express. Please note that fetch
API is only supported by nodejs v18+.
router.get('/callback', async (req, res) => {
const code = req.query.code
if (!code) {
return res.status(400).send('Bad Request')
}
// Exchange the code for an access token
try {
const response = await fetch('https://hkbu.jp.auth0.com/oauth/token', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({
grant_type: 'authorization_code',
client_id: 'dxiOZbTWeGFhW4ScsQrV6rpR1IWO8VJw',
client_secret: 'AGu90xbpSYfM22Jd3RUHvXL1esG2e_m8y3yfvdm0LHHuP1WMxj7ZX9DrMGwU1879',
code: code,
redirect_uri: 'http://localhost:3000/callback',
scope: 'openid profile email'
})
})
if (!response.ok) {
return res.status(400).send('Bad Request')
}
const data = await response.json()
// the response should be like
// {
// "access_token":"eyJz93a...k4laUWw",
// "refresh_token":"GEbRxBN...edjnXbL",
// "id_token":"eyJ0XAi...4faeEoQ",
// "token_type":"Bearer",
// "expires_in":86400
// }
// we may fetch the user profile with the access token
// ...
// after that, generate a jwt token and send it to frontend for authentication
const token = jwt.sign(data, 'secret')
return res.redirect(`http://localhost:5173/login-success?token=${token}`)
} catch (error) {
console.error(error)
return res.status(500).send('Internal Server Error')
}
})
After receiving the Access Token
, we may fetch the user profile with the Access Token
from the External Authorization Server
. After that, we may generate a JWT token and send it to the frontend for authentication.
If we would like to include user profile information in the JWT token, we may fetch the user profile with the Access Token
from the External Authorization Server
. Auth0 provides a /userinfo
endpoint to fetch the user profile with the Access Token
. You may check out the documentation of Auth0 for more details.
Here is an example of how to fetch the user profile with the Access Token
in Express.
const response = await fetch('https://hkbu.jp.auth0.com/userinfo', {
headers: {
'Authorization': `Bearer ${data.access_token}`
}
})
if (!response.ok) {
return res.status(400).send('Bad Request')
}
const profile = await response.json()