UNPKG

18.3 kBJavaScriptView Raw
1/*
2 * Copyright 2017-2017 Amazon.com, Inc. or its affiliates. All Rights Reserved.
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License"). You may not use this file except in compliance with
5 * the License. A copy of the License is located at
6 *
7 * http://aws.amazon.com/apache2.0/
8 *
9 * or in the "license" file accompanying this file. This file is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
10 * CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions
11 * and limitations under the License.
12 */
13var __assign = (this && this.__assign) || function () {
14 __assign = Object.assign || function(t) {
15 for (var s, i = 1, n = arguments.length; i < n; i++) {
16 s = arguments[i];
17 for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
18 t[p] = s[p];
19 }
20 return t;
21 };
22 return __assign.apply(this, arguments);
23};
24var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
25 function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
26 return new (P || (P = Promise))(function (resolve, reject) {
27 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
28 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
29 function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
30 step((generator = generator.apply(thisArg, _arguments || [])).next());
31 });
32};
33var __generator = (this && this.__generator) || function (thisArg, body) {
34 var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
35 return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
36 function verb(n) { return function (v) { return step([n, v]); }; }
37 function step(op) {
38 if (f) throw new TypeError("Generator is already executing.");
39 while (_) try {
40 if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
41 if (y = 0, t) op = [op[0] & 2, t.value];
42 switch (op[0]) {
43 case 0: case 1: t = op; break;
44 case 4: _.label++; return { value: op[1], done: false };
45 case 5: _.label++; y = op[1]; op = [0]; continue;
46 case 7: op = _.ops.pop(); _.trys.pop(); continue;
47 default:
48 if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
49 if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
50 if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
51 if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
52 if (t[2]) _.ops.pop();
53 _.trys.pop(); continue;
54 }
55 op = body.call(thisArg, _);
56 } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
57 if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
58 }
59};
60var __read = (this && this.__read) || function (o, n) {
61 var m = typeof Symbol === "function" && o[Symbol.iterator];
62 if (!m) return o;
63 var i = m.call(o), r, ar = [], e;
64 try {
65 while ((n === void 0 || n-- > 0) && !(r = i.next()).done) ar.push(r.value);
66 }
67 catch (error) { e = { error: error }; }
68 finally {
69 try {
70 if (r && !r.done && (m = i["return"])) m.call(i);
71 }
72 finally { if (e) throw e.error; }
73 }
74 return ar;
75};
76import { parse } from 'url'; // Used for OAuth parsing of Cognito Hosted UI
77import { launchUri } from './urlOpener';
78import * as oAuthStorage from './oauthStorage';
79import { isCognitoHostedOpts, CognitoHostedUIIdentityProvider, } from '../types/Auth';
80import { ConsoleLogger as Logger, Hub, urlSafeEncode } from '@aws-amplify/core';
81import sha256 from 'crypto-js/sha256';
82import Base64 from 'crypto-js/enc-base64';
83var AMPLIFY_SYMBOL = (typeof Symbol !== 'undefined' &&
84 typeof Symbol.for === 'function'
85 ? Symbol.for('amplify_default')
86 : '@@amplify_default');
87var dispatchAuthEvent = function (event, data, message) {
88 Hub.dispatch('auth', { event: event, data: data, message: message }, 'Auth', AMPLIFY_SYMBOL);
89};
90var logger = new Logger('OAuth');
91var OAuth = /** @class */ (function () {
92 function OAuth(_a) {
93 var config = _a.config, cognitoClientId = _a.cognitoClientId, _b = _a.scopes, scopes = _b === void 0 ? [] : _b;
94 this._urlOpener = config.urlOpener || launchUri;
95 this._config = config;
96 this._cognitoClientId = cognitoClientId;
97 if (!this.isValidScopes(scopes))
98 throw Error('scopes must be a String Array');
99 this._scopes = scopes;
100 }
101 OAuth.prototype.isValidScopes = function (scopes) {
102 return (Array.isArray(scopes) && scopes.every(function (scope) { return typeof scope === 'string'; }));
103 };
104 OAuth.prototype.oauthSignIn = function (responseType, domain, redirectSignIn, clientId, provider, customState) {
105 if (responseType === void 0) { responseType = 'code'; }
106 if (provider === void 0) { provider = CognitoHostedUIIdentityProvider.Cognito; }
107 var generatedState = this._generateState(32);
108 /* encodeURIComponent is not URL safe, use urlSafeEncode instead. Cognito
109 single-encodes/decodes url on first sign in and double-encodes/decodes url
110 when user already signed in. Using encodeURIComponent, Base32, Base64 add
111 characters % or = which on further encoding becomes unsafe. '=' create issue
112 for parsing query params.
113 Refer: https://github.com/aws-amplify/amplify-js/issues/5218 */
114 var state = customState
115 ? generatedState + "-" + urlSafeEncode(customState)
116 : generatedState;
117 oAuthStorage.setState(state);
118 var pkce_key = this._generateRandom(128);
119 oAuthStorage.setPKCE(pkce_key);
120 var code_challenge = this._generateChallenge(pkce_key);
121 var code_challenge_method = 'S256';
122 var scopesString = this._scopes.join(' ');
123 var queryString = Object.entries(__assign(__assign({ redirect_uri: redirectSignIn, response_type: responseType, client_id: clientId, identity_provider: provider, scope: scopesString, state: state }, (responseType === 'code' ? { code_challenge: code_challenge } : {})), (responseType === 'code' ? { code_challenge_method: code_challenge_method } : {})))
124 .map(function (_a) {
125 var _b = __read(_a, 2), k = _b[0], v = _b[1];
126 return encodeURIComponent(k) + "=" + encodeURIComponent(v);
127 })
128 .join('&');
129 var URL = "https://" + domain + "/oauth2/authorize?" + queryString;
130 logger.debug("Redirecting to " + URL);
131 this._urlOpener(URL, redirectSignIn);
132 };
133 OAuth.prototype._handleCodeFlow = function (currentUrl) {
134 return __awaiter(this, void 0, void 0, function () {
135 var code, currentUrlPathname, redirectSignInPathname, oAuthTokenEndpoint, client_id, redirect_uri, code_verifier, oAuthTokenBody, body, _a, access_token, refresh_token, id_token, error;
136 return __generator(this, function (_b) {
137 switch (_b.label) {
138 case 0:
139 code = (parse(currentUrl).query || '')
140 .split('&')
141 .map(function (pairings) { return pairings.split('='); })
142 .reduce(function (accum, _a) {
143 var _b;
144 var _c = __read(_a, 2), k = _c[0], v = _c[1];
145 return (__assign(__assign({}, accum), (_b = {}, _b[k] = v, _b)));
146 }, { code: undefined }).code;
147 currentUrlPathname = parse(currentUrl).pathname || '/';
148 redirectSignInPathname = parse(this._config.redirectSignIn).pathname || '/';
149 if (!code || currentUrlPathname !== redirectSignInPathname) {
150 return [2 /*return*/];
151 }
152 oAuthTokenEndpoint = 'https://' + this._config.domain + '/oauth2/token';
153 dispatchAuthEvent('codeFlow', {}, "Retrieving tokens from " + oAuthTokenEndpoint);
154 client_id = isCognitoHostedOpts(this._config)
155 ? this._cognitoClientId
156 : this._config.clientID;
157 redirect_uri = isCognitoHostedOpts(this._config)
158 ? this._config.redirectSignIn
159 : this._config.redirectUri;
160 code_verifier = oAuthStorage.getPKCE();
161 oAuthTokenBody = __assign({ grant_type: 'authorization_code', code: code,
162 client_id: client_id,
163 redirect_uri: redirect_uri }, (code_verifier ? { code_verifier: code_verifier } : {}));
164 logger.debug("Calling token endpoint: " + oAuthTokenEndpoint + " with", oAuthTokenBody);
165 body = Object.entries(oAuthTokenBody)
166 .map(function (_a) {
167 var _b = __read(_a, 2), k = _b[0], v = _b[1];
168 return encodeURIComponent(k) + "=" + encodeURIComponent(v);
169 })
170 .join('&');
171 return [4 /*yield*/, fetch(oAuthTokenEndpoint, {
172 method: 'POST',
173 headers: {
174 'Content-Type': 'application/x-www-form-urlencoded',
175 },
176 body: body,
177 })];
178 case 1: return [4 /*yield*/, (_b.sent()).json()];
179 case 2:
180 _a = _b.sent(), access_token = _a.access_token, refresh_token = _a.refresh_token, id_token = _a.id_token, error = _a.error;
181 if (error) {
182 throw new Error(error);
183 }
184 return [2 /*return*/, {
185 accessToken: access_token,
186 refreshToken: refresh_token,
187 idToken: id_token,
188 }];
189 }
190 });
191 });
192 };
193 OAuth.prototype._handleImplicitFlow = function (currentUrl) {
194 return __awaiter(this, void 0, void 0, function () {
195 var _a, id_token, access_token;
196 return __generator(this, function (_b) {
197 _a = (parse(currentUrl).hash || '#')
198 .substr(1) // Remove # from returned code
199 .split('&')
200 .map(function (pairings) { return pairings.split('='); })
201 .reduce(function (accum, _a) {
202 var _b;
203 var _c = __read(_a, 2), k = _c[0], v = _c[1];
204 return (__assign(__assign({}, accum), (_b = {}, _b[k] = v, _b)));
205 }, {
206 id_token: undefined,
207 access_token: undefined,
208 }), id_token = _a.id_token, access_token = _a.access_token;
209 dispatchAuthEvent('implicitFlow', {}, "Got tokens from " + currentUrl);
210 logger.debug("Retrieving implicit tokens from " + currentUrl + " with");
211 return [2 /*return*/, {
212 accessToken: access_token,
213 idToken: id_token,
214 refreshToken: null,
215 }];
216 });
217 });
218 };
219 OAuth.prototype.handleAuthResponse = function (currentUrl) {
220 return __awaiter(this, void 0, void 0, function () {
221 var urlParams, error, error_description, state, _a, _b, e_1;
222 return __generator(this, function (_c) {
223 switch (_c.label) {
224 case 0:
225 _c.trys.push([0, 5, , 6]);
226 urlParams = currentUrl
227 ? __assign(__assign({}, (parse(currentUrl).hash || '#')
228 .substr(1)
229 .split('&')
230 .map(function (entry) { return entry.split('='); })
231 .reduce(function (acc, _a) {
232 var _b = __read(_a, 2), k = _b[0], v = _b[1];
233 return ((acc[k] = v), acc);
234 }, {})), (parse(currentUrl).query || '')
235 .split('&')
236 .map(function (entry) { return entry.split('='); })
237 .reduce(function (acc, _a) {
238 var _b = __read(_a, 2), k = _b[0], v = _b[1];
239 return ((acc[k] = v), acc);
240 }, {}))
241 : {};
242 error = urlParams.error, error_description = urlParams.error_description;
243 if (error) {
244 throw new Error(error_description);
245 }
246 state = this._validateState(urlParams);
247 logger.debug("Starting " + this._config.responseType + " flow with " + currentUrl);
248 if (!(this._config.responseType === 'code')) return [3 /*break*/, 2];
249 _a = [{}];
250 return [4 /*yield*/, this._handleCodeFlow(currentUrl)];
251 case 1: return [2 /*return*/, __assign.apply(void 0, [__assign.apply(void 0, _a.concat([(_c.sent())])), { state: state }])];
252 case 2:
253 _b = [{}];
254 return [4 /*yield*/, this._handleImplicitFlow(currentUrl)];
255 case 3: return [2 /*return*/, __assign.apply(void 0, [__assign.apply(void 0, _b.concat([(_c.sent())])), { state: state }])];
256 case 4: return [3 /*break*/, 6];
257 case 5:
258 e_1 = _c.sent();
259 logger.error("Error handling auth response.", e_1);
260 throw e_1;
261 case 6: return [2 /*return*/];
262 }
263 });
264 });
265 };
266 OAuth.prototype._validateState = function (urlParams) {
267 if (!urlParams) {
268 return;
269 }
270 var savedState = oAuthStorage.getState();
271 var returnedState = urlParams.state;
272 // This is because savedState only exists if the flow was initiated by Amplify
273 if (savedState && savedState !== returnedState) {
274 throw new Error('Invalid state in OAuth flow');
275 }
276 return returnedState;
277 };
278 OAuth.prototype.signOut = function () {
279 return __awaiter(this, void 0, void 0, function () {
280 var oAuthLogoutEndpoint, client_id, signout_uri;
281 return __generator(this, function (_a) {
282 oAuthLogoutEndpoint = 'https://' + this._config.domain + '/logout?';
283 client_id = isCognitoHostedOpts(this._config)
284 ? this._cognitoClientId
285 : this._config.oauth.clientID;
286 signout_uri = isCognitoHostedOpts(this._config)
287 ? this._config.redirectSignOut
288 : this._config.returnTo;
289 oAuthLogoutEndpoint += Object.entries({
290 client_id: client_id,
291 logout_uri: encodeURIComponent(signout_uri),
292 })
293 .map(function (_a) {
294 var _b = __read(_a, 2), k = _b[0], v = _b[1];
295 return k + "=" + v;
296 })
297 .join('&');
298 dispatchAuthEvent('oAuthSignOut', { oAuth: 'signOut' }, "Signing out from " + oAuthLogoutEndpoint);
299 logger.debug("Signing out from " + oAuthLogoutEndpoint);
300 return [2 /*return*/, this._urlOpener(oAuthLogoutEndpoint, signout_uri)];
301 });
302 });
303 };
304 OAuth.prototype._generateState = function (length) {
305 var result = '';
306 var i = length;
307 var chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
308 for (; i > 0; --i)
309 result += chars[Math.round(Math.random() * (chars.length - 1))];
310 return result;
311 };
312 OAuth.prototype._generateChallenge = function (code) {
313 return this._base64URL(sha256(code));
314 };
315 OAuth.prototype._base64URL = function (string) {
316 return string
317 .toString(Base64)
318 .replace(/=/g, '')
319 .replace(/\+/g, '-')
320 .replace(/\//g, '_');
321 };
322 OAuth.prototype._generateRandom = function (size) {
323 var CHARSET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';
324 var buffer = new Uint8Array(size);
325 if (typeof window !== 'undefined' && !!window.crypto) {
326 window.crypto.getRandomValues(buffer);
327 }
328 else {
329 for (var i = 0; i < size; i += 1) {
330 buffer[i] = (Math.random() * CHARSET.length) | 0;
331 }
332 }
333 return this._bufferToString(buffer);
334 };
335 OAuth.prototype._bufferToString = function (buffer) {
336 var CHARSET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
337 var state = [];
338 for (var i = 0; i < buffer.byteLength; i += 1) {
339 var index = buffer[i] % CHARSET.length;
340 state.push(CHARSET[index]);
341 }
342 return state.join('');
343 };
344 return OAuth;
345}());
346export default OAuth;
347//# sourceMappingURL=OAuth.js.map
\No newline at end of file