ducky/web

Paddy 2015-06-30 Parent:3bdc03963abe Child:9b97c908a706

13:d51e39bf909c Go to Latest

ducky/web/src/helpers/oauth-refresh.js

Update our profiles collection to use ampersand-rest-collection. Use ampersand-rest-collection instead of ampersand-collection, so we can take advantage of the excellent "getOrFetch" function to fall back on the server when looking for a member of the collection that isn't downloaded yet. Also, use the correct Authorization header when making profile collection requests. Finally, if the profile collection requests fail due to an expired access token, use the refresh token to acquire a new access token, then retry the request.

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