Skip to content

Commit

Permalink
Pull userId from token, fix auth middleware
Browse files Browse the repository at this point in the history
  • Loading branch information
aselbie committed Jan 16, 2016
1 parent 651ee98 commit bd76b40
Show file tree
Hide file tree
Showing 5 changed files with 76 additions and 23 deletions.
4 changes: 1 addition & 3 deletions src/middleware/api.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@ import { normalize, Schema } from 'normalizr'
import Schemas from './schemas'
import axios from 'axios'
import decode from 'jwt-decode'
import checkAuth from './check-auth'

const trim = (token) => token.substring(1, token.length - 1)

Expand Down Expand Up @@ -72,8 +71,7 @@ export default store => next => action => {
const [ requestType, successType, failureType ] = types
next(actionWith({ type: requestType }))

checkAuth()
.then( () => callApi(callAPI) )
return callApi(callAPI)
.then( response => {
const successAction = actionWith({ response, type: successType })
next(successAction)
Expand Down
50 changes: 34 additions & 16 deletions src/middleware/check-auth.js
Original file line number Diff line number Diff line change
@@ -1,50 +1,68 @@
import decode from 'jwt-decode'
import fetch from 'isomorphic-fetch'
import axios from 'axios'
import { CALL_API } from '../middleware/api'

const trim = (token) => token.substring(1, token.length - 1)
const expiresIn = (token) => Math.round(decode(token).exp - Date.now() / 1000)

const API_URL = process.env.API_URL || 'https://api.topcoder.com'
const AUTH0_TOKEN_NAME = process.env.AUTH0_TOKEN_NAME || 'userJWTToken'

// Bind our token refreshes to the same promise chain
let refreshPromise = null

export default function() {
export default store => next => action => {

// Continue if there is no API call associated
if (typeof action[CALL_API] === 'undefined') {
return next(action)
}

const token = trim(localStorage.userJWTToken)
const expires = expiresIn(token)
const fresh = expires > 60

console.log(token)

// Continue if the token is fresh
if (fresh) {
return Promise.resolve(null)
return next(action)
}

console.log(`Token will expire in ${ expires } seconds. Getting a new one.`)

// If we are already refreshing the token for other actions, append this
// request to the chain
if (refreshPromise) {
return refreshPromise
return refreshPromise = refreshPromise.then( () => next(action) )
}

const API_URL = process.env.API_URL || 'https://api.topcoder.com'
const url = API_URL + '/v3/authorizations/1'
// Configure our fresh request
const config = {
url: API_URL + '/v3/authorizations/1',
headers: {
'Authorization': 'Bearer ' + token
}
}

return refreshPromise = axios.get(url)
.then( res => res.json() )
.then( json => {
if (json.result.status === 200) {
console.log(json.result.content.token)
refreshPromise = axios(config)
.then( res => {
if (res.status === 200) {
// Get token from response
const newToken = '"' + res.data.result.content.token + '"'

// Assign it to local storage
localStorage.setItem(AUTH0_TOKEN_NAME, newToken)

localStorage.setItem('userJWTToken', '"' + json.result.content.token + '"')
// Clear our promise chain
refreshPromise = null
console.log(`Token refreshed. Completing blocked API requests`)

// Continue with our action
next(action)
} else {
throw 'Token refresh failed'
}
})
.catch( (err) => {
refreshPromise = null
})

return refreshPromise
}
30 changes: 30 additions & 0 deletions src/middleware/check-user.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
import decode from 'jwt-decode'

const trim = (token) => token.substring(1, token.length - 1)

export const SET_USER = 'SET_USER'

export default store => next => action => {
if (action.type == SET_USER) {
return next(action)
}

const token = trim(localStorage.userJWTToken)
const tokenUserId = decode(token).userId
const storeUserId = store.getState().user.id

if (!tokenUserId && !storeUserId) {
return null
}

if (!storeUserId) {
next({
type: SET_USER,
user: {
id: tokenUserId
}
})
}

return next(action)
}
11 changes: 8 additions & 3 deletions src/reducers/index.js
Original file line number Diff line number Diff line change
@@ -1,14 +1,19 @@
import * as ActionTypes from '../actions'
import merge from 'lodash/object/merge'
import {reducer as formReducer} from 'redux-form'
import { reducer as formReducer } from 'redux-form'
import { combineReducers } from 'redux'
import { SET_USER } from '../middleware/check-user'

const defaultState = {
profiles: {}
}

function user(state, action) {
return state || {}
function user(state = {}, action) {
if (action.type == SET_USER) {
state = action.user
}

return state
}

// Updates an entity cache in response to any action with response.entities.
Expand Down
4 changes: 3 additions & 1 deletion src/store/configureStore.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
import { createStore, applyMiddleware, compose } from 'redux'
import thunk from 'redux-thunk'
import checkUser from '../middleware/check-user'
import checkAuth from '../middleware/check-auth'
import api from '../middleware/api'
import createLogger from 'redux-logger'
import rootReducer from '../reducers'

const finalCreateStore = compose(
applyMiddleware(thunk, api),
applyMiddleware(thunk, checkUser, checkAuth, api),
applyMiddleware(createLogger()),
window.devToolsExtension ? window.devToolsExtension() : f => f
)(createStore)
Expand Down

0 comments on commit bd76b40

Please sign in to comment.