Saltar al contenido

¿Cómo usar Redux para actualizar el token JWT?

Te damos la solución a esta incógnita, o por lo menos eso pensamos. Si continuas con dudas coméntalo, que sin dudar

Solución:

Encontré una manera de resolver esto. No estoy seguro de si este es el enfoque de mejores prácticas y probablemente haya algunas mejoras que podrían hacerse.

Mi idea original se mantiene: la actualización de JWT está en el middleware. Ese middleware tiene que venir antes thunk si thunk se usa

...
const createStoreWithMiddleware = applyMiddleware(jwt, thunk)(createStore);

Luego, en el código de middleware, verificamos si el token ha caducado antes de cualquier acción asíncrona. Si está vencido, también verificamos si ya estamos actualizando el token; para poder tener tal verificación, agregamos la promesa de un token nuevo al estado.

import  refreshToken  from '../actions/auth';

export function jwt( dispatch, getState ) 

    return (next) => (action) => 

        // only worry about expiring token for async actions
        if (typeof action === 'function') 

            if (getState().auth && getState().auth.token) 

                // decode jwt so that we know if and when it expires
                var tokenExpiration = jwtDecode(getState().auth.token).;

                if (tokenExpiration && (moment(tokenExpiration) - moment(Date.now()) < 5000)) 

                    // make sure we are not already refreshing the token
                    if (!getState().auth.freshTokenPromise) 
                        return refreshToken(dispatch).then(() => next(action));
                     else 
                        return getState().auth.freshTokenPromise.then(() => next(action));
                    
                
            
        
        return next(action);
    ;

La parte más importante es refreshToken función. Esa función debe enviar una acción cuando se actualice el token para que el estado contenga la promesa del token nuevo. De esa manera, si despachamos varias acciones asíncronas que usan autenticación de token simultáneamente, el token se actualiza solo una vez.

export function refreshToken(dispatch) 

    var freshTokenPromise = fetchJWTToken()
        .then(t => 
            dispatch(
                type: DONE_REFRESHING_TOKEN
            );

            dispatch(saveAppToken(t.token));

            return t.token ? Promise.resolve(t.token) : Promise.reject(
                message: 'could not refresh token'
            );
        )
        .catch(e => 

            console.log('error refreshing token', e);

            dispatch(
                type: DONE_REFRESHING_TOKEN
            );
            return Promise.reject(e);
        );



    dispatch(
        type: REFRESHING_TOKEN,

        // we want to keep track of token promise in the state so that we don't try to refresh
        // the token again while refreshing is in process
        freshTokenPromise
    );

    return freshTokenPromise;

Me doy cuenta de que esto es bastante complicado. También me preocupa un poco despachar acciones en refreshToken que no es una acción en sí misma. Infórmeme sobre cualquier otro enfoque que conozca que maneje la expiración del token JWT con redux.

En lugar de “esperar” a que termine una acción, podría mantener una variable de tienda para saber si todavía está obteniendo tokens:

reductor de muestra

const initialState = 
    fetching: false,
;
export function reducer(state = initialState, action) 
    switch(action.type) 
        case 'LOAD_FETCHING':
            return 
                ...state,
                fetching: action.fetching,
            
    

Ahora el creador de la acción:

export function loadThings() 
    return (dispatch, getState) => 
        const  auth, isLoading  = getState();

        if (!isExpired(auth.token)) 
            dispatch( type: 'LOAD_FETCHING', fetching: false )
            dispatch(loadProfile());
            dispatch(loadAssets());
        else 
            dispatch( type: 'LOAD_FETCHING', fetching: true )
            dispatch(refreshToken());
       
    ;

Esto se llama cuando se monta el componente. Si la autorización key está obsoleto, enviará una acción para establecer fetching para true y también actualizar el token. Tenga en cuenta que aún no vamos a cargar el perfil o los activos.

Nuevo componente:

componentDidMount() 
    dispath(loadThings());
    // ...


componentWillReceiveProps(newProps) 
    const  fetching, token  = newProps; // bound from store

    // assuming you have the current token stored somewhere
    if (token === storedToken) 
        return; // exit early
    

    if (!fetching) 
        loadThings()
     

Tenga en cuenta que ahora intenta cargar sus cosas en la montura, pero también bajo ciertas condiciones cuando recibe accesorios (esto se llamará cuando la tienda cambie para que podamos seguir fetching allí) Cuando la recuperación inicial falla, activará la refreshToken. Cuando termine, configurará el nuevo token en la tienda, actualizará el componente y, por lo tanto, llamará componentWillReceiveProps. Si aún no se está recuperando (no estoy seguro de que esta verificación sea necesaria), cargará las cosas.

Hice un envoltorio simple alrededor redux-api-middleware para posponer acciones y actualizar el token de acceso.

middleware.js

import  isRSAA, apiMiddleware  from 'redux-api-middleware';

import  TOKEN_RECEIVED, refreshAccessToken  from './actions/auth'
import  refreshToken, isAccessTokenExpired  from './reducers'


export function createApiMiddleware() 
  const postponedRSAAs = []

  return ( dispatch, getState ) => 
    const rsaaMiddleware = apiMiddleware(dispatch, getState)

    return (next) => (action) => 
      const nextCheckPostponed = (nextAction) => 
          // Run postponed actions after token refresh
          if (nextAction.type === TOKEN_RECEIVED) 
            next(nextAction);
            postponedRSAAs.forEach((postponed) => 
              rsaaMiddleware(next)(postponed)
            )
           else 
            next(nextAction)
          
      

      if(isRSAA(action)) 
        const state = getState(),
              token = refreshToken(state)

        if(token && isAccessTokenExpired(state)) 
          postponedRSAAs.push(action)
          if(postponedRSAAs.length === 1) 
            return  rsaaMiddleware(nextCheckPostponed)(refreshAccessToken(token))
           else 
            return
          
        
      
        return rsaaMiddleware(next)(action);
      
      return next(action);
    
  


export default createApiMiddleware();

Mantengo los tokens en el estado y uso un asistente simple para inyectar el token Acess en los encabezados de una solicitud

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

Entonces redux-api-middleware las acciones permanecen casi sin cambios

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
      ]
  
)

Escribí el artículo y compartí el ejemplo del proyecto, que muestra el flujo de trabajo del token de actualización de JWT en acción

¡Haz clic para puntuar esta entrada!
(Votos: 0 Promedio: 0)



Utiliza Nuestro Buscador

Deja una respuesta

Tu dirección de correo electrónico no será publicada. Los campos obligatorios están marcados con *