Refresh auth0 token in SPA

You may have heard of auth0 before, if not, it's a service of user authentication, which provides functions for user sign up, login, social login etc, so you don’t have to implement them yourself.

I recently used auth0 in one of my SPA project, after the integration, the user can signup/login to my application, auth0 will return a id_token, which the application can store in the browser, use it to verify the user.

So far so good. However, for security concerns, the id_token have a relatively short expire time, so we need to handle the situation where the token expires while the user is using the application.

The expiration may occur on every api request, so I decide to handle it in the api request layer.

Here is my api module with axios:

import axios from 'axios'
import { getToken } from './auth'

const api = axios.create()

api.interceptors.request.use(config => {
  const token = getToken()
  if (token) {
    config.headers['Authorization'] = `Bearer ${token}`
  }
  return config
})

It should be easy to understand: whenever an api request is sending to the server, the request interceptor will add an Authorization header with the id_token, if it exists.

Server side

the server should return a 401 Unauthorized error if there is no valid token in the request, an expired token is not a valid one of course. if you’re using koa, there’s koa-jwt, or express-jwt if you’re using express.

Client side

we need to add a response interceptor to handle the 401 error:

api.interceptors.response.use(undefined, (error) => {
  if (error.response.status !== 401 || error.config.__isRetryRequest) {
    throw error
  }
  return refreshToken()
    .then((newToken) => {
      error.config.__isRetryRequest = true
      error.config.headers['Authorization'] = `Bearer ${newToken}`
      return axios(error.config)
    })
    .catch((error) => {
      // can't refresh the token, guide the user to login again
    })
})

as you can see, if the response is a 401 error, we’ll refresh the token and resend the request with the new token. So wha’t inside refreshToken?

import { WebAuth } from 'auth0-js'
import Promise from 'bluebird'
import { AUTH0_CLIENT_ID, AUTH0_DOMAIN } from './config'

const auth0 = new WebAuth({
  domain: AUTH0_DOMAIN,
  clientID: AUTH0_CLIENT_ID,
  responseType: 'token id_token'
})

function refreshToken () {
  return Promise.fromCallback(callback => {
    return auth0.renewAuth({
      scope: 'openid',
      redirectUri: 'https://example.com/silent-callback.html',
      usePostMessage: true
    }, callback)
  }).then(authResult => authResult.idToken)
}

...

Here we first initialized a WebAuth instance with out domain and clientID, then what the refreshToken function is doing is to call auth0.renewAuth and return the resulting token as a promise.

Note the redirectUri here, it’s a dedicated callback page for refreshing the token, so your application don’t need to be loaded again in an iframe. This is what it is like:

<!DOCTYPE html>
<html>
  <head>
    <script src="https://cdn.auth0.com/js/auth0/8.0.4/auth0.min.js"></script>
    <script type="text/javascript">
      var webAuth = new auth0.WebAuth({
        domain: '...',
        clientID: '...'
      })
      var result = webAuth.parseHash(window.location.hash, function(err, data) {
        parent.postMessage(err || data, "https://example.com/")
      })
    </script>
  </head>
  <body></body>
</html>

OK, so this is the approach of refreshing id_token I’m using, hope it will help someone.

Doora

当我第N次面临“把电脑上的文件传到手机”的困境的时候,我决定解决这个问题。

想了想,我需要的是一个很简单的网站,能快速的打开,拖拽上传,生成一个二维码,手机扫描下载。但是好像还没看到完全符合这个条件的网站(虽然我觉得肯定有...),于是就开始自己写了。

第二天,有了 Doora

名字

Doora,哆啦A梦,任意门,传送。。。感受一下。

七牛

上传后的文件是存在七牛上的,主要是为了提供比较好的下载速度,而且之前七牛搞活动的时候充过一些钱,不用白不用。 不过七牛的文档...,半天都找不到想找的内容,Python SDK的接口也不太友好。

过期

不过七牛没有提供设定文件上传后多久删除的功能,只能自己实现了。 现在的方案是通过rq-scheduler,上传后30分钟后,把文件的key放入删除队列里,然后由rqworker来消费删除。

速度

为了加快页面显示速度,尽可能减少了阻塞的css和js文件,现在只会加载一个css,由内嵌在HTML里的load.js来异步加载其它js,引用的静态都是放在http://www.staticfile.org/ CDN上的。

源码

https://github.com/wong2/doora