asus
asus

Reputation: 1759

Axios - extracting http cookies and setting them as authorization headers

I am building authentication for my application and I am using access and refresh tokens.

Upon user login, the API issues 3 things

These tokens are all jwt tokens.

This article discusses why access tokens should be split.

using express, I send the tokens back to the browser in my controller like so:

res.cookie(
      ACCESS_TOKEN_COOKIE_HEADER_PAYLOAD,
      headerAndPayload,
      COOKIE_OPTIONS,
    )
    res.cookie(
      ACCESS_TOKEN_COOKIE_SIGNATURE,
      signature,
      COOKIE_OPTIONS_HTTP_ONLY,
    )
    res.cookie(REFRESH_TOKEN_COOKIE, refreshToken, COOKIE_OPTIONS)

    return res.json({ username, uid })

auth.constants.ts

export const COOKIE_OPTIONS: CookieOptions = {
  secure: true,
  sameSite: 'lax',
}

export const COOKIE_OPTIONS_HTTP_ONLY: CookieOptions = {
  httpOnly: true,
  secure: true,
  sameSite: 'lax',
}

export const ACCESS_TOKEN_COOKIE_HEADER_PAYLOAD = 'access_token_header_payload'
export const ACCESS_TOKEN_COOKIE_SIGNATURE = 'access_token_signature'
export const REFRESH_TOKEN_COOKIE = 'refresh_token'

In the ui (react) I go into Chrome devtools -> application -> storage -> cookeis and I can see that they are updated everytime I login. This is the behavior I want so that's good so far.

devtools cookies

Now when I want to send a request to my API to create something (let's say I am creating a new blog post), I want to grab those cookies and pass them as an Authorization Header.

I am following this person's suggestion except I noticed he is using store which I am guessing is some form of state. Since I am not doing that and multiple sources (source 1, source 2) point to the fact that the standard for sending tokens to the API for authentication is using Authorization header, I would like to follow that.

Currently, when I make an API request using axios, I console log the express request object and can see my tokens in cookies like so:

headers: {
    host: 'localhost:3001',
    connection: 'keep-alive',
    'content-length': '0',
    accept: 'application/json, text/plain, */*',
    'user-agent': 'Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/84.0.4147.105 Safari/537.36',
    origin: 'http://localhost:3000',
    'sec-fetch-site': 'same-site',
    'sec-fetch-mode': 'cors',
    'sec-fetch-dest': 'empty',
    referer: 'http://localhost:3000/',
    'accept-encoding': 'gzip, deflate, br',
    'accept-language': 'en-US,en;q=0.9',
    cookie: 'access_token_header_payload=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InJvc3R5cG9vIiwiaWF0IjoxNTk2ODM0MDIwLCJleHAiOjE1OTY4MzQwODB9; access_token_signature=3pUbxjWgly9xmYSJObOvTgps9qwjOIrHWWE4LPYidmQ; refresh_token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InJvc3R5cG9vIiwiaWF0IjoxNTk2ODM0MDIwLCJleHAiOjE1OTc0Mzg4MjB9.IKdRsaTTgAeUfwicLcBpRvw89WgYXy_rCRN5o2BJFqY'
  },

but I want to send these cookies as Authorization: Bearer <tokens> instead. How would I do that in axios? Or is what I am doing secure?

this is my axios interceptor

import axios from 'axios'

const service = axios.create({
  withCredentials: true,
  baseURL: process.env.REACT_APP_API_BASE_URL,
  timeout: 5000,
})

// Request interceptors
service.interceptors.request.use(
  config => {
    return config
  },
  error => {
    return Promise.reject(error)
  },
)

// Response interceptors
service.interceptors.response.use(
  response => {
    console.log('response', response)

    return response.data
  },
  error => {
    return Promise.reject({ ...error })
  },
)

export default service

Upvotes: 2

Views: 5878

Answers (2)

moswil
moswil

Reputation: 41

As stated in this response by chinesedfan. The way to authorize your requests to your backend API is through query parsing since your cookie is HttpOnly and can't be accessed by any client.

With express, this can be done by creating a global middleware that sets your authorization header. The following snippet shows how to do this, assuming you are using Bearer <accessToken>.

// global middleware for setting authorization header
app.use((req, res, next) => {
  const authHeader = req.cookies.accessToken;
  if (authHeader) {
    req.headers.authorization = `Bearer ${authHeader}`;
  }
  next();
});

// initialize passportjs
app.use(passport.initialize())

Add this middleware in your server.js, assuming you named your initializing file this way. This is where you declare your express app variable.

In express, middleware order matters, so add this middleware before you initialize your passport middleware.

In your frontend, you don't have to add anything to axios, just make the request to the backend, and if that request needs authorization, it will be added to you automatically.

Upvotes: 1

chinesedfan
chinesedfan

Reputation: 449

HttpOnly means the client script can't access the cookie, as well as you can't read it from document.cookie and pass to axios.

In fact, HttpOnly cookie is more secure than http request headers I think. What you need is parsing the auth cookie in the server side, instead of parsing the request header.

Upvotes: 3

Related Questions