1 |
|
2 |
|
3 | import { __assign, __awaiter, __generator, __read } from "tslib";
|
4 | import { parse } from 'url';
|
5 | import { launchUri } from './urlOpener';
|
6 | import * as oAuthStorage from './oauthStorage';
|
7 | import { Buffer } from 'buffer';
|
8 | import { isCognitoHostedOpts, CognitoHostedUIIdentityProvider, } from '../types/Auth';
|
9 | import { ConsoleLogger as Logger, Hub, urlSafeEncode } from '@aws-amplify/core';
|
10 | import { Sha256 } from '@aws-crypto/sha256-js';
|
11 | var AMPLIFY_SYMBOL = (typeof Symbol !== 'undefined' && typeof Symbol.for === 'function'
|
12 | ? Symbol.for('amplify_default')
|
13 | : '@@amplify_default');
|
14 | var dispatchAuthEvent = function (event, data, message) {
|
15 | Hub.dispatch('auth', { event: event, data: data, message: message }, 'Auth', AMPLIFY_SYMBOL);
|
16 | };
|
17 | var logger = new Logger('OAuth');
|
18 | var OAuth = (function () {
|
19 | function OAuth(_a) {
|
20 | var config = _a.config, cognitoClientId = _a.cognitoClientId, _b = _a.scopes, scopes = _b === void 0 ? [] : _b;
|
21 | this._urlOpener = config.urlOpener || launchUri;
|
22 | this._config = config;
|
23 | this._cognitoClientId = cognitoClientId;
|
24 | if (!this.isValidScopes(scopes))
|
25 | throw Error('scopes must be a String Array');
|
26 | this._scopes = scopes;
|
27 | }
|
28 | OAuth.prototype.isValidScopes = function (scopes) {
|
29 | return (Array.isArray(scopes) && scopes.every(function (scope) { return typeof scope === 'string'; }));
|
30 | };
|
31 | OAuth.prototype.oauthSignIn = function (responseType, domain, redirectSignIn, clientId, provider, customState) {
|
32 | if (responseType === void 0) { responseType = 'code'; }
|
33 | if (provider === void 0) { provider = CognitoHostedUIIdentityProvider.Cognito; }
|
34 | var generatedState = this._generateState(32);
|
35 | |
36 |
|
37 |
|
38 |
|
39 |
|
40 |
|
41 | var state = customState
|
42 | ? generatedState + "-" + urlSafeEncode(customState)
|
43 | : generatedState;
|
44 | oAuthStorage.setState(state);
|
45 | var pkce_key = this._generateRandom(128);
|
46 | oAuthStorage.setPKCE(pkce_key);
|
47 | var code_challenge = this._generateChallenge(pkce_key);
|
48 | var code_challenge_method = 'S256';
|
49 | var scopesString = this._scopes.join(' ');
|
50 | 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 } : {})))
|
51 | .map(function (_a) {
|
52 | var _b = __read(_a, 2), k = _b[0], v = _b[1];
|
53 | return encodeURIComponent(k) + "=" + encodeURIComponent(v);
|
54 | })
|
55 | .join('&');
|
56 | var URL = "https://" + domain + "/oauth2/authorize?" + queryString;
|
57 | logger.debug("Redirecting to " + URL);
|
58 | this._urlOpener(URL, redirectSignIn);
|
59 | };
|
60 | OAuth.prototype._handleCodeFlow = function (currentUrl) {
|
61 | return __awaiter(this, void 0, void 0, function () {
|
62 | var code, currentUrlPathname, redirectSignInPathname, oAuthTokenEndpoint, client_id, redirect_uri, code_verifier, oAuthTokenBody, body, _a, access_token, refresh_token, id_token, error;
|
63 | return __generator(this, function (_b) {
|
64 | switch (_b.label) {
|
65 | case 0:
|
66 | code = (parse(currentUrl).query || '')
|
67 | .split('&')
|
68 | .map(function (pairings) { return pairings.split('='); })
|
69 | .reduce(function (accum, _a) {
|
70 | var _b;
|
71 | var _c = __read(_a, 2), k = _c[0], v = _c[1];
|
72 | return (__assign(__assign({}, accum), (_b = {}, _b[k] = v, _b)));
|
73 | }, { code: undefined }).code;
|
74 | currentUrlPathname = parse(currentUrl).pathname || '/';
|
75 | redirectSignInPathname = parse(this._config.redirectSignIn).pathname || '/';
|
76 | if (!code || currentUrlPathname !== redirectSignInPathname) {
|
77 | return [2 ];
|
78 | }
|
79 | oAuthTokenEndpoint = 'https://' + this._config.domain + '/oauth2/token';
|
80 | dispatchAuthEvent('codeFlow', {}, "Retrieving tokens from " + oAuthTokenEndpoint);
|
81 | client_id = isCognitoHostedOpts(this._config)
|
82 | ? this._cognitoClientId
|
83 | : this._config.clientID;
|
84 | redirect_uri = isCognitoHostedOpts(this._config)
|
85 | ? this._config.redirectSignIn
|
86 | : this._config.redirectUri;
|
87 | code_verifier = oAuthStorage.getPKCE();
|
88 | oAuthTokenBody = __assign({ grant_type: 'authorization_code', code: code,
|
89 | client_id: client_id,
|
90 | redirect_uri: redirect_uri }, (code_verifier ? { code_verifier: code_verifier } : {}));
|
91 | logger.debug("Calling token endpoint: " + oAuthTokenEndpoint + " with", oAuthTokenBody);
|
92 | body = Object.entries(oAuthTokenBody)
|
93 | .map(function (_a) {
|
94 | var _b = __read(_a, 2), k = _b[0], v = _b[1];
|
95 | return encodeURIComponent(k) + "=" + encodeURIComponent(v);
|
96 | })
|
97 | .join('&');
|
98 | return [4 , fetch(oAuthTokenEndpoint, {
|
99 | method: 'POST',
|
100 | headers: {
|
101 | 'Content-Type': 'application/x-www-form-urlencoded',
|
102 | },
|
103 | body: body,
|
104 | })];
|
105 | case 1: return [4 , (_b.sent()).json()];
|
106 | case 2:
|
107 | _a = _b.sent(), access_token = _a.access_token, refresh_token = _a.refresh_token, id_token = _a.id_token, error = _a.error;
|
108 | if (error) {
|
109 | throw new Error(error);
|
110 | }
|
111 | return [2 , {
|
112 | accessToken: access_token,
|
113 | refreshToken: refresh_token,
|
114 | idToken: id_token,
|
115 | }];
|
116 | }
|
117 | });
|
118 | });
|
119 | };
|
120 | OAuth.prototype._handleImplicitFlow = function (currentUrl) {
|
121 | return __awaiter(this, void 0, void 0, function () {
|
122 | var _a, id_token, access_token;
|
123 | return __generator(this, function (_b) {
|
124 | _a = (parse(currentUrl).hash || '#')
|
125 | .substr(1)
|
126 | .split('&')
|
127 | .map(function (pairings) { return pairings.split('='); })
|
128 | .reduce(function (accum, _a) {
|
129 | var _b;
|
130 | var _c = __read(_a, 2), k = _c[0], v = _c[1];
|
131 | return (__assign(__assign({}, accum), (_b = {}, _b[k] = v, _b)));
|
132 | }, {
|
133 | id_token: undefined,
|
134 | access_token: undefined,
|
135 | }), id_token = _a.id_token, access_token = _a.access_token;
|
136 | dispatchAuthEvent('implicitFlow', {}, "Got tokens from " + currentUrl);
|
137 | logger.debug("Retrieving implicit tokens from " + currentUrl + " with");
|
138 | return [2 , {
|
139 | accessToken: access_token,
|
140 | idToken: id_token,
|
141 | refreshToken: null,
|
142 | }];
|
143 | });
|
144 | });
|
145 | };
|
146 | OAuth.prototype.handleAuthResponse = function (currentUrl) {
|
147 | return __awaiter(this, void 0, void 0, function () {
|
148 | var urlParams, error, error_description, state, _a, _b, e_1;
|
149 | return __generator(this, function (_c) {
|
150 | switch (_c.label) {
|
151 | case 0:
|
152 | _c.trys.push([0, 5, , 6]);
|
153 | urlParams = currentUrl
|
154 | ? __assign(__assign({}, (parse(currentUrl).hash || '#')
|
155 | .substr(1)
|
156 | .split('&')
|
157 | .map(function (entry) { return entry.split('='); })
|
158 | .reduce(function (acc, _a) {
|
159 | var _b = __read(_a, 2), k = _b[0], v = _b[1];
|
160 | return ((acc[k] = v), acc);
|
161 | }, {})), (parse(currentUrl).query || '')
|
162 | .split('&')
|
163 | .map(function (entry) { return entry.split('='); })
|
164 | .reduce(function (acc, _a) {
|
165 | var _b = __read(_a, 2), k = _b[0], v = _b[1];
|
166 | return ((acc[k] = v), acc);
|
167 | }, {}))
|
168 | : {};
|
169 | error = urlParams.error, error_description = urlParams.error_description;
|
170 | if (error) {
|
171 | throw new Error(error_description);
|
172 | }
|
173 | state = this._validateState(urlParams);
|
174 | logger.debug("Starting " + this._config.responseType + " flow with " + currentUrl);
|
175 | if (!(this._config.responseType === 'code')) return [3 , 2];
|
176 | _a = [{}];
|
177 | return [4 , this._handleCodeFlow(currentUrl)];
|
178 | case 1: return [2 , __assign.apply(void 0, [__assign.apply(void 0, _a.concat([(_c.sent())])), { state: state }])];
|
179 | case 2:
|
180 | _b = [{}];
|
181 | return [4 , this._handleImplicitFlow(currentUrl)];
|
182 | case 3: return [2 , __assign.apply(void 0, [__assign.apply(void 0, _b.concat([(_c.sent())])), { state: state }])];
|
183 | case 4: return [3 , 6];
|
184 | case 5:
|
185 | e_1 = _c.sent();
|
186 | logger.error("Error handling auth response.", e_1);
|
187 | throw e_1;
|
188 | case 6: return [2 ];
|
189 | }
|
190 | });
|
191 | });
|
192 | };
|
193 | OAuth.prototype._validateState = function (urlParams) {
|
194 | if (!urlParams) {
|
195 | return;
|
196 | }
|
197 | var savedState = oAuthStorage.getState();
|
198 | var returnedState = urlParams.state;
|
199 |
|
200 | if (savedState && savedState !== returnedState) {
|
201 | throw new Error('Invalid state in OAuth flow');
|
202 | }
|
203 | return returnedState;
|
204 | };
|
205 | OAuth.prototype.signOut = function () {
|
206 | return __awaiter(this, void 0, void 0, function () {
|
207 | var oAuthLogoutEndpoint, client_id, signout_uri;
|
208 | return __generator(this, function (_a) {
|
209 | oAuthLogoutEndpoint = 'https://' + this._config.domain + '/logout?';
|
210 | client_id = isCognitoHostedOpts(this._config)
|
211 | ? this._cognitoClientId
|
212 | : this._config.oauth.clientID;
|
213 | signout_uri = isCognitoHostedOpts(this._config)
|
214 | ? this._config.redirectSignOut
|
215 | : this._config.returnTo;
|
216 | oAuthLogoutEndpoint += Object.entries({
|
217 | client_id: client_id,
|
218 | logout_uri: encodeURIComponent(signout_uri),
|
219 | })
|
220 | .map(function (_a) {
|
221 | var _b = __read(_a, 2), k = _b[0], v = _b[1];
|
222 | return k + "=" + v;
|
223 | })
|
224 | .join('&');
|
225 | dispatchAuthEvent('oAuthSignOut', { oAuth: 'signOut' }, "Signing out from " + oAuthLogoutEndpoint);
|
226 | logger.debug("Signing out from " + oAuthLogoutEndpoint);
|
227 | return [2 , this._urlOpener(oAuthLogoutEndpoint, signout_uri)];
|
228 | });
|
229 | });
|
230 | };
|
231 | OAuth.prototype._generateState = function (length) {
|
232 | var result = '';
|
233 | var i = length;
|
234 | var chars = '0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ';
|
235 | for (; i > 0; --i)
|
236 | result += chars[Math.round(Math.random() * (chars.length - 1))];
|
237 | return result;
|
238 | };
|
239 | OAuth.prototype._generateChallenge = function (code) {
|
240 | var awsCryptoHash = new Sha256();
|
241 | awsCryptoHash.update(code);
|
242 | var resultFromAWSCrypto = awsCryptoHash.digestSync();
|
243 | var b64 = Buffer.from(resultFromAWSCrypto).toString('base64');
|
244 | var base64URLFromAWSCrypto = this._base64URL(b64);
|
245 | return base64URLFromAWSCrypto;
|
246 | };
|
247 | OAuth.prototype._base64URL = function (string) {
|
248 | return string.replace(/=/g, '').replace(/\+/g, '-').replace(/\//g, '_');
|
249 | };
|
250 | OAuth.prototype._generateRandom = function (size) {
|
251 | var CHARSET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789-._~';
|
252 | var buffer = new Uint8Array(size);
|
253 | if (typeof window !== 'undefined' && !!window.crypto) {
|
254 | window.crypto.getRandomValues(buffer);
|
255 | }
|
256 | else {
|
257 | for (var i = 0; i < size; i += 1) {
|
258 | buffer[i] = (Math.random() * CHARSET.length) | 0;
|
259 | }
|
260 | }
|
261 | return this._bufferToString(buffer);
|
262 | };
|
263 | OAuth.prototype._bufferToString = function (buffer) {
|
264 | var CHARSET = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789';
|
265 | var state = [];
|
266 | for (var i = 0; i < buffer.byteLength; i += 1) {
|
267 | var index = buffer[i] % CHARSET.length;
|
268 | state.push(CHARSET[index]);
|
269 | }
|
270 | return state.join('');
|
271 | };
|
272 | return OAuth;
|
273 | }());
|
274 | export default OAuth;
|
275 |
|
\ | No newline at end of file |