Full stack Django: Quick start with JWT auth and React/Redux (Part III)

In the previous two parts, we created a simple backend and frontend with user authentication.

In this final part, we would perform our first authenticated call to the API and implement JWT Refresh Token workflow.

Calling an API

Let’s create a simple helper, that would add Authentication header with token from our state.

reducers/index.js

...
export function withAuth(headers={}) {
return (state) => ({
...headers,
'Authorization': `Bearer ${accessToken(state)}`
})
}

Now we can create our first API action, in a new file actions/echo.js

import { RSAA } from 'redux-api-middleware';
import { withAuth } from '../reducers'
export const ECHO_REQUEST = '@@echo/ECHO_REQUEST';
export const ECHO_SUCCESS = '@@echo/ECHO_SUCCESS';
export const ECHO_FAILURE = '@@echo/ECHO_FAILURE';
export const echo = (message) => ({
[RSAA]: {
endpoint: '/api/echo/',
method: 'POST',
body: JSON.stringify({message: message}),
headers: withAuth({ 'Content-Type': 'application/json' }),
types: [
ECHO_REQUEST, ECHO_SUCCESS, ECHO_FAILURE
]
}
})

When action came into redux-api-middleware the headers function would be called, and the Authorization header would be expanded.

Save the API call result in the store with the simple reducer:

reducers/echo.js

import * as echo from '../actions/echo'const initialState = {
message: ""
}
export default (state=initialState, action) => {
switch(action.type) {
case echo.ECHO_SUCCESS:
return {
message: action.payload.message
}
default:
return state
}
}
export const serverMessage = (state) => state.message

Let’s call it. The simplest thing that we can do, is to perform API calls on componentDidMount just after when a user goes into the App page.

App.js

import React, { Component } from 'react';
import { connect } from 'react-redux'
import {echo} from './actions/echo'
import {serverMessage} from './reducers'
class App extends Component {
componentDidMount() {
this.props.fetchMessage('Hi!')
}
render() {
return (
<div>
<h2>Welcome to React</h2>
<p>{this.props.message}</p>
</div>
);
}
}
export default connect(
state => ({ message: serverMessage(state) }),
{ fetchMessage: echo }
)(App);

We connect the App component with the state. As soon as component became mounted, the echo action would go to the redux-api-middleware that would perform the actual API call. On complete, ECHO_SUCCEED message would be dispatch down to the echo reducer. The message would be saved in the state.echo.message that would cause UI reconciliation.

Everything would work fine, till the Access token became expired. Just imagine the situation, when a user opens an application after a while. A user has a valid Refresh token, and the Access token already expired. API requests fire immediately from different components of an application. But we can’t execute them right now, till Access token became updated.

JWT Workflow

So, here is the trick. We going to wrap redux-api-middleware into out custom middleware, that postpone actions till valid access token received

import { isRSAA, apiMiddleware } from 'redux-api-middleware';import { TOKEN_RECEIVED, refreshAccessToken } from './actions/auth'
import { refreshToken, isAccessTokenExpired } from './reducers'
export function createApiMiddleware() {
let postponedRSAAs = []
return ({ dispatch, getState }) => {
const rsaaMiddleware = apiMiddleware({dispatch, getState})
return (next) => (action) => {
сonst nextCheckPostoned = (nextAction) => {
// Run postponed actions after token refresh
if (nextAction.type === TOKEN_RECEIVED) {
next(nextAction);
postponedRSAAs.forEach((postponed) => {
rsaaMiddleware(next)(postponed)
})
postponedRSAAs = []
} else {
next(nextAction)
}
}
if(isRSAA(action)) {
const state = getState(),
token = refreshToken(state)
if(token && isAccessTokenExpired(state)) {
postponedRSAAs.push(action)
if(postponedRSAAs.length === 1) {
const action = refreshAccessToken(token)
return rsaaMiddleware(nextCheckPostoned)(action)
} else {
return
}
}
return rsaaMiddleware(next)(action);
}
return next(action);
}
}
}
export default createApiMiddleware();

If everything is fine, we just delegate an action to the rsaaMiddleware(next)

But if we have a Refresh token and our Access token is expired, the action goes to the postponedRSAAs list, and refresAccessToken action fired instead. Since all middlewares works like a chain, and there is no loop in message processing, we catch TOKEN_RECEIVED in the custom nextCheckPostponed function that passed as next parameter to the redux-api-middleware

As soon as new Access Token received, we could fire postponed actions.

Now you can just replace an import apiMiddleware in the store.js to make everything works

import apiMiddleware from './middleware';

That’s our simple application.

Having JWT workflow implemented in the middleware allows to keep actions code free from authentication flow details.

The sample code available at the https://github.com/viewflow/cookbook/tree/master/_articles/redux_jwt_auth

Reusable workflow library #django #python http://viewflow.io

Get the Medium app

A button that says 'Download on the App Store', and if clicked it will lead you to the iOS App store
A button that says 'Get it on, Google Play', and if clicked it will lead you to the Google Play store