Skip to content

Instantly share code, notes, and snippets.

@fathan
Forked from Godofbrowser/axios.refresh_token.1.js
Created March 20, 2021 14:51
Show Gist options
  • Save fathan/d0a135a3f68b5ebff8f4b79d6b82f863 to your computer and use it in GitHub Desktop.
Save fathan/d0a135a3f68b5ebff8f4b79d6b82f863 to your computer and use it in GitHub Desktop.

Revisions

  1. @Godofbrowser Godofbrowser revised this gist Jul 7, 2020. 1 changed file with 23 additions and 2 deletions.
    25 changes: 23 additions & 2 deletions axios.refresh_token.2.js
    Original file line number Diff line number Diff line change
    @@ -1,4 +1,25 @@
    // for multiple requests (same implementation but with some abstractions)
    // Intercept and refresh expired tokens for multiple requests (same implementation but with some abstractions)
    //
    // HOW TO USE:
    // import applyAppTokenRefreshInterceptor from 'axios.refresh_token.2.js';
    // import axios from 'axios';
    // ...
    // applyAppTokenRefreshInterceptor(axios); // register the interceptor with all axios instance
    // ...
    // - Alternatively:
    // const apiClient = axios.create({baseUrl: 'example.com/api'});
    // applyAppTokenRefreshInterceptor(apiClient); // register the interceptor with one specific axios instance
    // ...
    // - With custom options:
    // applyAppTokenRefreshInterceptor(apiClient, {
    // shouldIntercept: (error) => {
    // return error.response.data.errorCode === 'EXPIRED_ACCESS_TOKEN';
    // }
    // ); // register the interceptor with one specific axios instance
    //
    // PS: You may need to figure out some minor things yourself as this is just a proof of concept and not a tutorial.
    // Forgive me in advance

    const shouldIntercept = (error) => {
    try {
    return error.response.status === 401
    @@ -24,7 +45,7 @@ const handleTokenRefresh = () => {
    const tokenData = {
    idToken: data.auth_token,
    refreshToken: data.refresh_token,
    expiresAt: data.expires_in,
    expiresAt: data.expires_at,
    };
    resolve(tokenData);
    })
  2. @Godofbrowser Godofbrowser revised this gist Jul 7, 2020. 1 changed file with 1 addition and 0 deletions.
    1 change: 1 addition & 0 deletions axios.refresh_token.2.js
    Original file line number Diff line number Diff line change
    @@ -1,3 +1,4 @@
    // for multiple requests (same implementation but with some abstractions)
    const shouldIntercept = (error) => {
    try {
    return error.response.status === 401
  3. @Godofbrowser Godofbrowser renamed this gist Jul 7, 2020. 1 changed file with 0 additions and 0 deletions.
    File renamed without changes.
  4. @Godofbrowser Godofbrowser revised this gist Jul 7, 2020. 2 changed files with 115 additions and 2 deletions.
    113 changes: 113 additions & 0 deletions axios.refresh_token.2.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,113 @@
    const shouldIntercept = (error) => {
    try {
    return error.response.status === 401
    } catch (e) {
    return false;
    }
    };

    const setTokenData = (tokenData = {}, axiosClient) => {
    // If necessary: save to storage
    // tokenData's content includes data from handleTokenRefresh(): {
    // idToken: data.auth_token,
    // refreshToken: data.refresh_token,
    // expiresAt: data.expires_in,
    // };
    };

    const handleTokenRefresh = () => {
    const refreshToken = window.localStorage.getItem('refreshToken');
    return new Promise((resolve, reject) => {
    axios.post('http://localhost:8000/auth/refresh', { refreshToken })
    .then(({data}) => {
    const tokenData = {
    idToken: data.auth_token,
    refreshToken: data.refresh_token,
    expiresAt: data.expires_in,
    };
    resolve(tokenData);
    })
    .catch((err) => {
    reject(err);
    })
    });
    };

    const attachTokenToRequest = (request, token) => {
    request.headers['Authorization'] = 'Bearer ' + token;

    // If there is an edge case where access token is also set in request query,
    // this is also a nice place to add it
    // Example: /orders?token=xyz-old-token
    if (/\/orders/.test(request.url)) {
    request.params.token = token;
    }
    };

    export default (axiosClient, customOptions = {}) => {
    let isRefreshing = false;
    let failedQueue = [];

    const options = {
    attachTokenToRequest,
    handleTokenRefresh,
    setTokenData,
    shouldIntercept,
    ...customOptions,
    };
    const processQueue = (error, token = null) => {
    failedQueue.forEach(prom => {
    if (error) {
    prom.reject(error);
    } else {
    prom.resolve(token);
    }
    });

    failedQueue = [];
    };

    const interceptor = (error) => {
    if (!options.shouldIntercept(error)) {
    return Promise.reject(error);
    }

    if (error.config._retry || error.config._queued) {
    return Promise.reject(error);
    }

    const originalRequest = error.config;
    if (isRefreshing) {
    return new Promise(function (resolve, reject) {
    failedQueue.push({resolve, reject})
    }).then(token => {
    originalRequest._queued = true;
    options.attachTokenToRequest(originalRequest, token);
    return axiosClient.request(originalRequest);
    }).catch(err => {
    return Promise.reject(error); // Ignore refresh token request's "err" and return actual "error" for the original request
    })
    }

    originalRequest._retry = true;
    isRefreshing = true;
    return new Promise((resolve, reject) => {
    options.handleTokenRefresh.call(options.handleTokenRefresh)
    .then((tokenData) => {
    options.setTokenData(tokenData, axiosClient);
    options.attachTokenToRequest(originalRequest, tokenData.idToken);
    processQueue(null, tokenData.idToken);
    resolve(axiosClient.request(originalRequest));
    })
    .catch((err) => {
    processQueue(err, null);
    reject(err);
    })
    .finally(() => {
    isRefreshing = false;
    })
    });
    };

    axiosClient.interceptors.response.use(undefined, interceptor);
    };
    4 changes: 2 additions & 2 deletions axios.refresh_token.js
    Original file line number Diff line number Diff line change
    @@ -51,9 +51,9 @@ axios.interceptors.response.use(function (response) {
    processQueue(err, null);
    reject(err);
    })
    .then(() => { isRefreshing = false })
    .finally(() => { isRefreshing = false })
    })
    }

    return Promise.reject(error);
    });
    });
  5. @Godofbrowser Godofbrowser revised this gist Oct 4, 2019. No changes.
  6. @Godofbrowser Godofbrowser revised this gist Oct 4, 2019. No changes.
  7. @Godofbrowser Godofbrowser revised this gist Aug 17, 2019. 1 changed file with 1 addition and 1 deletion.
    2 changes: 1 addition & 1 deletion axios.refresh_token.js
    Original file line number Diff line number Diff line change
    @@ -29,7 +29,7 @@ axios.interceptors.response.use(function (response) {
    originalRequest.headers['Authorization'] = 'Bearer ' + token;
    return axios(originalRequest);
    }).catch(err => {
    return err
    return Promise.reject(err);
    })
    }

  8. @Godofbrowser Godofbrowser revised this gist Jul 26, 2018. No changes.
  9. @Godofbrowser Godofbrowser revised this gist Jul 26, 2018. 1 changed file with 2 additions and 2 deletions.
    4 changes: 2 additions & 2 deletions axios.refresh_token.js
    Original file line number Diff line number Diff line change
    @@ -44,11 +44,11 @@ axios.interceptors.response.use(function (response) {
    window.localStorage.setItem('refreshToken', data.refreshToken);
    axios.defaults.headers.common['Authorization'] = 'Bearer ' + data.token;
    originalRequest.headers['Authorization'] = 'Bearer ' + data.token;
    processQueue(data.token);
    processQueue(null, data.token);
    resolve(axios(originalRequest));
    })
    .catch((err) => {
    processQueue(null);
    processQueue(err, null);
    reject(err);
    })
    .then(() => { isRefreshing = false })
  10. @Godofbrowser Godofbrowser revised this gist Jul 26, 2018. No changes.
  11. @Godofbrowser Godofbrowser revised this gist Jul 26, 2018. 1 changed file with 13 additions and 13 deletions.
    26 changes: 13 additions & 13 deletions axios.refresh_token.js
    Original file line number Diff line number Diff line change
    @@ -20,7 +20,18 @@ axios.interceptors.response.use(function (response) {

    const originalRequest = error.config;

    if (error.response.status === 401 && !originalRequest._retry && !isRefreshing) {
    if (error.response.status === 401 && !originalRequest._retry) {

    if (isRefreshing) {
    return new Promise(function(resolve, reject) {
    failedQueue.push({resolve, reject})
    }).then(token => {
    originalRequest.headers['Authorization'] = 'Bearer ' + token;
    return axios(originalRequest);
    }).catch(err => {
    return err
    })
    }

    originalRequest._retry = true;
    isRefreshing = true;
    @@ -37,23 +48,12 @@ axios.interceptors.response.use(function (response) {
    resolve(axios(originalRequest));
    })
    .catch((err) => {
    reject(err)
    processQueue(null);
    reject(err);
    })
    .then(() => { isRefreshing = false })
    })
    }

    if (error.response.status === 401 && isRefreshing) {
    return new Promise(function(resolve, reject) {
    failedQueue.push({resolve, reject})
    }).then(token => {
    originalRequest.headers['Authorization'] = 'Bearer ' + token;
    return axios(originalRequest);
    }).catch(err => {
    return err
    })
    }

    return Promise.reject(error);
    });
  12. @Godofbrowser Godofbrowser revised this gist Jul 26, 2018. 1 changed file with 45 additions and 9 deletions.
    54 changes: 45 additions & 9 deletions axios.refresh_token.js
    Original file line number Diff line number Diff line change
    @@ -1,22 +1,58 @@
    // for multiple requests
    let isRefreshing = false;
    let failedQueue = [];

    const processQueue = (error, token = null) => {
    failedQueue.forEach(prom => {
    if (error) {
    prom.reject(error);
    } else {
    prom.resolve(token);
    }
    })

    failedQueue = [];
    }

    axios.interceptors.response.use(function (response) {
    return response;
    }, function (error) {

    const originalRequest = error.config;

    if (error.response.status === 401 && !originalRequest._retry) {
    if (error.response.status === 401 && !originalRequest._retry && !isRefreshing) {

    originalRequest._retry = true;
    isRefreshing = true;

    const refreshToken = window.localStorage.getItem('refreshToken');
    return axios.post('http://localhost:8000/auth/refresh', { refreshToken })
    .then(({data}) => {
    window.localStorage.setItem('token', data.token);
    window.localStorage.setItem('refreshToken', data.refreshToken);
    axios.defaults.headers.common['Authorization'] = 'Bearer ' + data.token;
    originalRequest.headers['Authorization'] = 'Bearer ' + data.token;
    return axios(originalRequest);
    });
    return new Promise(function (resolve, reject) {
    axios.post('http://localhost:8000/auth/refresh', { refreshToken })
    .then(({data}) => {
    window.localStorage.setItem('token', data.token);
    window.localStorage.setItem('refreshToken', data.refreshToken);
    axios.defaults.headers.common['Authorization'] = 'Bearer ' + data.token;
    originalRequest.headers['Authorization'] = 'Bearer ' + data.token;
    processQueue(data.token);
    resolve(axios(originalRequest));
    })
    .catch((err) => {
    reject(err)
    processQueue(null);
    })
    .then(() => { isRefreshing = false })
    })
    }

    if (error.response.status === 401 && isRefreshing) {
    return new Promise(function(resolve, reject) {
    failedQueue.push({resolve, reject})
    }).then(token => {
    originalRequest.headers['Authorization'] = 'Bearer ' + token;
    return axios(originalRequest);
    }).catch(err => {
    return err
    })
    }

    return Promise.reject(error);
  13. @culttm culttm created this gist Oct 5, 2017.
    23 changes: 23 additions & 0 deletions axios.refresh_token.js
    Original file line number Diff line number Diff line change
    @@ -0,0 +1,23 @@
    axios.interceptors.response.use(function (response) {
    return response;
    }, function (error) {

    const originalRequest = error.config;

    if (error.response.status === 401 && !originalRequest._retry) {

    originalRequest._retry = true;

    const refreshToken = window.localStorage.getItem('refreshToken');
    return axios.post('http://localhost:8000/auth/refresh', { refreshToken })
    .then(({data}) => {
    window.localStorage.setItem('token', data.token);
    window.localStorage.setItem('refreshToken', data.refreshToken);
    axios.defaults.headers.common['Authorization'] = 'Bearer ' + data.token;
    originalRequest.headers['Authorization'] = 'Bearer ' + data.token;
    return axios(originalRequest);
    });
    }

    return Promise.reject(error);
    });