npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

redux-refresh-token

v0.1.0

Published

A promise callback to refresh access tokens when using RSAA

Downloads

306

Readme

redux-refresh-token

Build Status Coverage Status

Extension to your API middleware to refresh access tokens when a request hits a 401 response (access token expired).

Introduction

If you use transient (short-lived) OAuth tokens for authentication then these will expire all the time. However, instead of logging the user out every time the token expires it is much better to simply request a new token using the refresh token.

If you use Redux you are surely using an API middleware for requesting an API. You might roll your own, use the one from the real-world example or a 3rd party like agraboso's redux-api-middleware. I use the latter - you should give it a try. It is great.

This library provides a function you can plug into your API call. If the API call returns a 401 response then your access token probably expired. This library will try to refresh the token. Whilst refreshing it will queue up all API calls. If the refresh was successful it will retry all the queued calls. If it was not successful it will log out the user.

Prerequisites

Your API middleware must return FSA-style responses. There is a great description about flux-standard-actions here. You want the quick run down? Your middleware must return actions like this.

Successful response (status range 200-299)

{
    type: "ACTION",
    payload: {
        // API response
    }
}

Error response (everything else)

{
    type: "ACTION",
    error: true,
    payload: {
        status: 401 // Http status code
    }
}

This is the most important thing. redux-refresh-token will look for the error property and will look for the status to determine whether or not it should try a refresh. It will always attempt a refresh on 401 status codes. Everything else will just be ignored.

Installation

You will need to install a reducer under the key tokenRefresh. If you want to change the key you have to pass the refreshReducerKey setting to attemptRefresh.

import { reducer } from 'redux-refresh-token'

const store = createStore(
  combineReducers({
    tokenRefresh: refreshReducer,
    // Other reducers
  }),
  {},
  applyMiddleware(api)
)

So you probably have some API middlware in your code that ends a long the lines of this below

return fetch(`${API_ROOT}${endpoint}${querystring}`, {
  method,
  body,
  headers,
  credentials: "same-origin"
})
.then(response => response.json().then(json => {
  if (!response.ok) {
    return Promise.reject(json)
  }

  return json
}))
.then(response => next({
  type: successType,
  payload: response
})), 
error => next({
  type: failureType,
  error: true,
  payload: {
    status: response.status,
    error: error.message
  }
}))

Now replace it with

import attemptRefresh, { createFSAConverter } from "redux-refresh-token";

// Middleware code skipped for breweity

return fetch(`${API_ROOT}${endpoint}${querystring}`, {
  method,
  body,
  headers,
  credentials: "same-origin"
})
// This step will convert fetch's response into a FSA style action
// This is needed in the attempt method
.then(createFSAConverter(successType, failureType))
.then(
  attemptRefresh({
      // An action creator that determines what happens when the refresh failed
      failure: logout,
      // An action creator that creates an API request to refresh the token
      refreshActionCreator: attemptTokenRefresh,
      // This is the current access token. If the user is not authenticated yet 
      // this can be null or undefined
      token,

      // The next 3 parameters are simply the arguments of 
      // the middleware function
      action,
      next,
      store,
  })
)

API

The attemptRefresh has various parameters to adjust to different types of API middleware.

failure (action creator) (required)

Should be an actionCreator. This action will be dispatched whenever the refresh failed. Usually this would be a logout action.

An example could be

export const LOGOUT_REQUEST = 'LOGOUT_REQUEST'
export const LOGOUT_SUCCESS = 'LOGOUT_SUCCESS'
export const LOGOUT_FAILURE = 'LOGOUT_FAILURE'

export const logout = () => ({
  [CALL_API]: {
    endpoint: '/logout',
    method: 'POST',
    types: [
      LOGOUT_REQUEST,
      LOGOUT_SUCCESS,
      LOGOUT_FAILURE
    ]
  }
})

isRefreshCall (function(action, refreshAction))

When refreshing it is important to determine whether or not a failed action is an attempt to refresh the access token. Because the library will attempt to refresh all 401's, if the refresh call returns 401 and we do not check if successfully we are stuck in an infinite loop.

The default behaviour is to check like below

return action["Call API"].endpoint === refreshAction["Call API"].endpoint;

refreshActionCreator (action creator) (required)

An action creator that creates the action that will request the API for a new token.

Example

export const TOKEN_REFRESH_REQUEST = 'TOKEN_REFRESH_REQUEST'
export const TOKEN_REFRESH_SUCCESS = 'TOKEN_REFRESH_SUCCESS'
export const TOKEN_REFRESH_FAILURE = 'TOKEN_REFRESH_FAILURE'

export const attemptTokenRefresh = () => ({
  [CALL_API]: {
    endpoint: '/login/refresh',
    method: 'POST',
    types: [
      TOKEN_REFRESH_REQUEST,
      TOKEN_REFRESH_SUCCESS,
      TOKEN_REFRESH_FAILURE
    ]
  }
})

refreshReducerKey (string)

By default the reducer should be installed under the key tokenRefresh but you can change the default setting using this key.

setAccessTokenActionCreator (action creator)

An action creator used to set a new access token in the redux store state.

The default creator is given below

export const SET_ACCESS_TOKEN = 'SET_ACCESS_TOKEN'

export const setAccessToken = ({ access_token, expires_in }) => ({
  type: SET_ACCESS_TOKEN,
  access_token,
  expires_in
})

token (string) (required)

The current access token from the redux store state.

Tests

npm test

License

The MIT License (MIT). Please see License File for more information.