A valet key?  OAuth 2.0 Delegated Authorization in Node.js 
Tags: node, oauth, webdev
Published: 2020-10-13 20:33:11

---
title:
published: true
tags: 
canonical_url: https://wanjohi.vercel.app/2020/10/13/A-valet-key-OAuth-2-0-Delegated-Authorization-in-Node-js/
---
How to implement OAuth 2.0 in Node.js with Spotify using the Authorization code flow.

# What is Delegated Authorization?
First things first, Delegated Authorization is all about granting access to perform some task on your behalf.
Consider a valet key, you hand a valet parking attendant a valet key to park and retrieve your car on your behalf. Valet keys typically unlock the driver's door and start the car but can't unlock the trunk or the glove box.
For an application to perform such tasks, for instance make API requests in order to perform actions(read and write) on user's resources(in another application), they need to be authorized by the user to perform only a set of actions on the user's behalf.
OAuth is a delegation framework that enables all the parties involved to orchestrate such a process.

## OAuth 2.0 in Node.js
Setup your node.js environment and have your server running using the framework of your choice.
We'll be using Spotify as an example to show the OAuth 2.0 authorization code flow in node.js.
You can check out the final application [here](https://nameless-badlands-53247.herokuapp.com/)
### Step 1: Client registration and configuration
From the specification, a client is an application making protected resource requests on behalf of the resource owner (end-user) with its authorization. To register an application you need to provide the information required by the authorization server. This will vary depending on the service provider. With Spotify some of the information includes application name, description, website, Redirect URL.
After registering an application you are presented with a client ID and client secret. Save the client ID and client Secret in a **.env** file.

```javascript
Client_id="your client_id" 
Client_secret="your client secret"
```

A client ID is a unique  identifier and a client secret is used to authenticate the client with the authorization server. 
In the **app.js** create an object to encapsulate the client details (client_id and client_secret). Use the dotenv module to load the environment variables. To install the module, run **npm install dotenv**.

```javascript
require('dotenv').config()

const client = {
    client_id: process.env.Client_id,
    client_secret: process.env.Client_secret,
    redirect_uri: "http://localhost:3000/callback"
}
```

The **redirect_uri** is the endpoint URL that the authorization server redirects the user-agent back to the client with (previously used in client registration) after the resource owner grants or denies permission to your client.
### Step 2: Send an authorization request
You need to include a link that initiates the process and also inform the user what service they are about to access. For instance, **View your Spotify activity**. This is done by sending the client to the authorization endpoint of the authorization server. The authorization endpoint can be found on the [authorization guide](https://developer.spotify.com/documentation/general/guides/authorization-guide/).
![](https://www.datocms-assets.com/30168/1602607954-rsz1presentation7-1.png)
Next, configure the authorization server details; just like the client details, enclose the details in an object.

```javascript
const authProvider = {
    authEndpoint: 'https://accounts.spotify.com/authorize',
    tokenEndpoint:  'https://accounts.spotify.com/api/token'
}
```
From the specification, the **authorization endpoint** is used to interact with the resource owner and obtain an authorization grant. The **token endpoint** is used by the client to obtain an **access token** by providing its authorization grant or refresh tokens.
To send the user to the authorization endpoint, you need the authorization endpoint URL and a few concatenated URL query parameters:
**Client ID**
The client id obtained after client registration.
**response_type**
*code* indicates the type of response returned.
**redirect_url**
The URI to redirect to after the user grants or denies permission to your client. We included it in the client's config object.
**scope**
This refers to the information the client wants to access the resource server.
**state**
A random non-guessable string used to protect against cross-site request forgery(CSRF) attacks.
To build the url with all the queries you can use the build-url library;a library that builds URLs. To install run **npm install build-url --save**.

```javascript
const buildUrl = require('build-url');
const authorizeUrl = buildUrl(authProvider.authEndpoint, {
    queryParams: {
        client_id: client.client_id,
        redirect_uri: client.redirect_uri,
        state,
        response_type: "code",
        scope: "user-top-read"
    }
});
```

First, we pass the authorization endpoint as the first argument of the buildUrl function followed by the query parameters. The queryParams object is self-explanatory, except the state and scope maybe. The scope **"user-top-read"**  from the Spotify guide gives access to a user's top artists and tracks. 
#### So what is the state query parameter?
From the [specification](https://tools.ietf.org/html/rfc6749#section-10.10);
>The client MUST implement CSRF protection for its redirection URI. .........The binding value used for CSRF protection MUST contain a non-guessable value

There is no specification on how to generate the non-guessable value. We can use the node.js **crypto** module to genetrate the random string to be used as state.

```javascript
const crypto = require('crypto');
let state 
crypto.randomBytes(20, (err, buf) => {
  if (err) throw err;
  state = buf.toString('hex')
});
```

The state parameter is returned in the response callback and thus has to be compared with the one in the client, we therefore need to store it somewhere. We could store in  session strorage. To use session storage we need express-session middleware. To install run the command **npm install express-session**.

```javascript
const session = require('express-session');
app.use(session({
    name: 'SESSION_ID',
    secret: process.env.secret, //signs the cookie
    resave: false,
    saveUninitialized: false,
  }))
```

Send the request to the authorization endpoint.

```javascript
app.get('/authorize', (req, res) => {
    req.session.state = state
    res.redirect(authorizeUrl);
})
```

The user initiates the process by clicking the authorize link, they are redirected to the service provider's authorization endpoint where they authenticate themselves first(if not already authenticated), they are then presented with the scopes; information that the client will have access to and whether they agree to grant the client access or not. Whether they agree or not the user is redirected back to the client with the redirect URL.
![](https://www.datocms-assets.com/30168/1602607960-rszpresentation7-3.png)

### Step 3: Exchanging the authorization code for an access token 
If the user grants the client access, the redirect URL contains the **code** and the **state** query parameters. If they deny the client access, the redirect URL contains the **error** parameter with a description why the authorization failed i.e access_denied.
![](https://www.datocms-assets.com/30168/1602607964-rszpresentation7-4.png)

At this point the client extracts the state parameter and compares it with the session state and if they don't match the client doesn't continue with the processing.

```javascript
if(req.query.state !== req.session.state) {
        res.render('error', {error: 'State doesn\'t match'})
    }
``` 

If they match the client extracts the code and exchanges it for the access token via a post request to the authorization server token endpoint.

```javascript
const code = req.query.code;
 const response = await axios({
                method: 'post',
                url: authProvider.tokenEndpoint,
                headers: {
                    'Content-Type': 'application/x-www-form-urlencoded',
                    'Authorization': 'Basic ' + stringToBase64(client.client_id, client.client_secret)
                },
                data: querystring.stringify({
                    code,
                    grant_type: "authorization_code",
                    redirect_uri: client.redirect_uri
                })
            })
            console.log(response.data)
            access_token = response.data.access_token
```

We've used http basic to authenticate the client; the client_id as the username and client_secret as the password encoded in Base64.
![](https://www.datocms-assets.com/30168/1602607969-rszpresentation7-5.png)

### Step 4: Accessing user's resources
At this point you have the access_token. You can then use the access token to access the user's resources using the resource server API endpoints.

```javascript
const fetch_lists = async () => {
        const axiosInstance = axios.create ({
            baseURL : 'https://api.spotify.com/v1/me/top/',
            headers: {
                'Authorization': 'Bearer ' + access_token
                }
          })
        try{
            const [response1, response2] = await Promise.all([
                axiosInstance.get('tracks'),
                axiosInstance.get('artists'),
            ])
            console.log(response1)
        } catch (error) {
						console.log(error.response.data.message)
        }   
    }
    fetch_lists()
```
![](https://www.datocms-assets.com/30168/1602607973-rszpresentation7-6.png)

### Conclusion
That's the gist of OAuth 2.0, You can check out the source code on [Github](https://github.com/Kenwanjohi/spotylister).
You can check out the original post on my [blog](https://wanjohi.vercel.app/posts/a-valet-key-oauth-2-0-delegated-authorization-in-node-js).
#### Related resources
- [OAuth 2.0 RFC 6749](https://tools.ietf.org/html/rfc6749)
- [User Authentication with OAuth 2.0](https://oauth.net/articles/authentication/)