ducky/web
ducky/web/src/helpers/oauth-refresh.js
Update our profile model to use our refresh helper. Update our profile model to send the correct authorization header when making requests, and if the request fails because the OAuth token has expired, try to use the refresh token to obtain a new access 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