UNPKG

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