forked from cujojs/rest
-
Notifications
You must be signed in to change notification settings - Fork 0
/
oAuth.js
135 lines (116 loc) · 4.19 KB
/
oAuth.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
/*
* Copyright 2012-2016 the original author or authors
* @license MIT, see LICENSE.txt for details
*
* @author Scott Andrews
*/
'use strict';
var interceptor, UrlBuilder, pubsub;
interceptor = require('../interceptor');
UrlBuilder = require('../UrlBuilder');
pubsub = require('../util/pubsub');
function defaultOAuthCallback(hash) {
var params, queryString, regex, m;
queryString = hash.indexOf('#') === 0 ? hash.substring(1) : hash;
params = {};
regex = /([^&=]+)=([^&]*)/g;
m = regex.exec(queryString);
do {
params[decodeURIComponent(m[1])] = decodeURIComponent(m[2]);
m = regex.exec(queryString);
} while (m);
/*jshint camelcase:false */
pubsub.publish(params.state, params.token_type + ' ' + params.access_token);
}
function defaultWindowStrategy(url) {
var w = window.open(url, '_blank', 'width=500,height=400');
return function () {
w.close();
};
}
function authorize(config) {
var state, url, dismissWindow;
return new Promise(function (resolve) {
state = Math.random() * new Date().getTime();
url = new UrlBuilder(config.authorizationUrlBase).build({
'response_type': 'token',
'redirect_uri': config.redirectUrl,
'client_id': config.clientId,
'scope': config.scope,
'state': state
});
dismissWindow = config.windowStrategy(url);
pubsub.subscribe(state, function (authorization) {
dismissWindow();
resolve(authorization);
});
});
}
/**
* OAuth implicit flow support
*
* Authorizes request with the OAuth authorization token. Tokens are
* requested from the authorization server as needed if there isn't a
* token, or the token is expired.
*
* A custom window strategy can be provided to replace the default popup
* window. The window strategy is a function that must accept a URL as an
* argument and returns a function to close and cleanup the window. A
* common custom strategy would be to use an iframe in a dialog.
*
* The callback function must be invoked when the authorization server
* redirects the browser back to the application.
*
* NOTE: Registering a handler to receive the redirect is required and
* outside the scope of this interceptor. The implementer must collect the
* URL fragment and pass it to the callback function on the 'opener', or
* 'parent' window.
*
* @param {Client} [target] client to wrap
* @param {string} [config.token] pre-configured authentication token
* @param {string} config.clientId OAuth clientId
* @param {string} config.scope OAuth scope
* @param {string} config.authorizationUrlBase URL of the authorization server
* @param {string} [config.redirectUrl] callback URL from the authorization server. Will be converted to a fully qualified, absolute URL, if needed. Default's to the window's location or base href.
* @param {Function} [config.windowStrategy] strategy for opening the authorization window, defaults to window.open
* @param {string} [config.oAuthCallbackName='oAuthCallback'] name to register the callback as in global scope
* @param {Function} [config.oAuthCallback] callback function to receive OAuth URL fragment
*
* @returns {Client}
*/
module.exports = interceptor({
init: function (config) {
config.redirectUrl = new UrlBuilder(config.redirectUrl).fullyQualify().build();
config.windowStrategy = config.windowStrategy || defaultWindowStrategy;
config.oAuthCallback = config.oAuthCallback || defaultOAuthCallback;
config.oAuthCallbackName = config.oAuthCallbackName || 'oAuthCallback';
window[config.oAuthCallbackName] = config.oAuthCallback;
return config;
},
request: function (request, config) {
request.headers = request.headers || {};
if (config.token) {
request.headers.Authorization = config.token;
return request;
}
else {
return authorize(config).then(function (authorization) {
request.headers.Authorization = config.token = authorization;
return request;
});
}
},
response: function (response, config, meta) {
if (response.status.code === 401) {
// token probably expired, reauthorize
return authorize(config).then(function (authorization) {
config.token = authorization;
return meta.client(response.request);
});
}
else if (response.status.code === 403) {
return Promise.reject(response);
}
return response;
}
});