ducky/web
2015-06-30
Child:9b97c908a706
ducky/web/src/helpers/oauth-refresh.js
Implement OAuth token refreshing helper. Create an ampersand-sync/XHR request wrapper that will detect when a request fails because an OAuth2 token has expired, and will try to refresh the token then retry the request.
1 import app from 'ampersand-app'
2 import xhr from 'xhr'
3 import qs from 'qs'
4 import Sync from 'ampersand-sync'
5 import config from '../config'
7 const getRefresh = (opts, callback) => {
8 const refreshOpts = {
9 url: config.urlBase + '/token',
10 method: 'POST',
11 headers: {
12 'Content-Type': 'application/x-www-form-urlencoded',
13 'Authorization': 'Basic ' + btoa(config.clientID + ':' + config.clientSecret),
14 },
15 data: qs.stringify({
16 'grant_type': 'refresh_token',
17 'refresh_token': app.me.refresh_token
18 }),
19 }
20 xhr(refreshOpts, function(err, resp, body) {
21 if (resp.statusCode != 200) {
22 callback(err, resp, body)
23 return
24 }
25 if (body && resp.headers['content-type'] == 'application/json') {
26 try {
27 body = JSON.parse(body)
28 } catch (err) {
29 app.trigger('token:refreshError', body)
30 callback(err, resp, body)
31 }
32 }
33 if (body.access_token) {
34 app.me.set(body)
35 } else {
36 app.trigger('token:refreshError', body)
37 }
38 callback(err, resp, body)
39 })
40 }
42 const shouldRefreshXHR = (err, resp, body) => {
43 if(body && resp.headers['Content-Type'] == 'application/json') {
44 try {
45 body = JSON.parse(body)
46 } catch (err) {
47 return false
48 }
49 }
50 if (resp.statusCode == 401 && body.errors && body.errors[0]
51 && body.errors[0].header == 'authorization' && body.errors[0].error == 'access_denied') {
52 return true
53 }
54 return false
55 }
57 const doRefreshXHR = (opts, callback) => {
58 opts.noRefresh = true
59 getRefresh(opts, function(err, resp, body) {
60 if (body.access_token) {
61 opts.headers['Authorization'] = 'Bearer '+body.access_token
62 xhr(opts, callback)
63 }
64 })
65 }
67 const refreshXHR = (opts, callback) => {
68 return xhr(opts, function(err, resp, body) {
69 if (opts.noRefresh || !shouldRefreshXHR(err, resp, body)) {
70 callback(err, resp, body)
71 return
72 }
73 doRefreshXHR(opts, callback)
74 })
75 xhr(opts, callback)
76 }
78 const shouldRefreshSync = (resp) => {
79 if (resp && resp.errors && resp.errors[0]
80 && resp.errors[0].header == 'authorization' && resp.errors[0].error == 'access_denied') {
81 return true
82 }
83 return false
84 }
86 const doRefreshSync = (action, moc, opts) => {
87 opts.noRefresh = true
88 getRefresh(opts, function(err, resp, body) {
89 if (body.access_token) {
90 Sync(action, moc, opts)
91 }
92 })
93 }
95 const refreshSync = (action, moc, opts) => {
96 const oldError = opts.error
97 opts.error = function(resp) {
98 if(opts.noRefresh || !shouldRefreshSync) {
99 oldError(resp)
100 return
101 }
102 doRefreshSync(action, moc, opts)
103 }
104 Sync(action, moc, opts)
105 }
107 const refresh = {
108 Sync: refreshSync,
109 XHR: refreshXHR,
110 }
112 export default refresh