UNPKG

24.2 kBJavaScriptView Raw
1"use strict";
2var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3 return new (P || (P = Promise))(function (resolve, reject) {
4 function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
5 function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
6 function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
7 step((generator = generator.apply(thisArg, _arguments || [])).next());
8 });
9};
10var __importDefault = (this && this.__importDefault) || function (mod) {
11 return (mod && mod.__esModule) ? mod : { "default": mod };
12};
13var __importStar = (this && this.__importStar) || function (mod) {
14 if (mod && mod.__esModule) return mod;
15 var result = {};
16 if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
17 result["default"] = mod;
18 return result;
19};
20Object.defineProperty(exports, "__esModule", { value: true });
21const react_1 = __importDefault(require("react"));
22const types_1 = __importDefault(require("../types"));
23const middleware_component_1 = __importDefault(require("../middleware/middleware-component"));
24const webapp_component_1 = __importStar(require("../webapp/webapp-component"));
25const securedroute_component_1 = require("./securedroute-component");
26const route_component_1 = __importStar(require("../route/route-component"));
27const libs_1 = require("../libs");
28const iso_libs_1 = require("../libs/iso-libs");
29const auth_middleware_1 = require("./auth-middleware");
30const body_parser_1 = __importDefault(require("body-parser"));
31const securedservice_component_1 = require("./securedservice-component");
32const service_component_1 = require("../service/service-component");
33exports.AUTHENTICATION_INSTANCE_TYPE = "AuthenticationComponent";
34exports.AuthenticationProvider = {
35 EMAIL: "AUTH_EMAIL",
36 GITHUB: "AUTH_GITHUB",
37 // TODO Medium Auth only partly implemented!!!
38 MEDIUM: "AUTH_MEDIUM"
39};
40exports.AUTH_RESPONSE = {
41 EMAIL_INVALID: "EMAIL_INVALID",
42 NOT_IMPLEMENTED: "NOT_IMPLEMENTED" // the authCallback is not implemented for this provider
43};
44exports.getProviderKey = (provider) => {
45 return provider + SUFFIX_SECRET;
46};
47exports.getClientSecret = (provider) => {
48 return process.env[exports.getProviderKey(provider)];
49};
50/**
51 * This creates a customized middleware that catches an error when the user is not logged in, i.e. it forwards her
52 * to the provider's login-page
53 *
54 * @param clientId as defined in the app of the provider
55 * @param callbackUrl as defined in the app of the provider
56 * @param provider to create the redirect Url for
57 */
58exports.createRequestLoginMiddleware = (clientId, callbackUrl, provider, loginUrl) => (err, req, res, next) => {
59 const path = require('path');
60 console.log("createRequestLoginMiddleware: ", err);
61 if (provider === exports.AuthenticationProvider.EMAIL) {
62 console.log("request login from: ", loginUrl);
63 const page = err && req.query && req.query.page ? req.query.page : req.url;
64 res.redirect(`${path.join(iso_libs_1.getBasename(), loginUrl)}?page=${page}${err ? `&message=${err}` : ""}`);
65 }
66 else if (provider === exports.AuthenticationProvider.GITHUB) {
67 res.redirect(`https://github.com/login/oauth/authorize?scope=user:email&client_id=${clientId}&redirect_uri=${callbackUrl}?page=${req.url}`);
68 }
69 else if (provider === exports.AuthenticationProvider.MEDIUM) {
70 res.redirect(`https://medium.com/m/oauth/authorize?client_id=${clientId}&scope=basicProfile,listPublications,publishPost&state=${req.url}&response_type=code&redirect_uri=${callbackUrl}`);
71 }
72 return;
73};
74/**
75 * Create a function that creates a function that fetches the AccessToken of the provider
76 */
77exports.createFetchAccessTokenFunction = (clientId, callbackUrl, provider, senderEmail, getSubject, getHtmlText) => (req) => {
78 if (provider === exports.AuthenticationProvider.EMAIL) {
79 console.log("this is the EMAIL - createFetchAccessTokenFunction middleware");
80 const { email, password, page } = req.query;
81 if (email !== undefined && password !== undefined) {
82 // the function fFetch must call a service that responds with a json. this json is fed into createGetUserFunction
83 return {
84 redirectPage: page,
85 fFetch: function () {
86 return __awaiter(this, void 0, void 0, function* () {
87 const uuidv4 = require('uuid/v4');
88 const access_token = uuidv4();
89 // TODO: here we call the service to send an email to the user
90 // see: https://docs.aws.amazon.com/de_de/sdk-for-javascript/v2/developer-guide/ses-examples-sending-email.html
91 const AWS = require('aws-sdk');
92 // Create sendEmail params
93 var params = {
94 Destination: {
95 BccAddresses: [
96 senderEmail
97 ],
98 CcAddresses: [],
99 ToAddresses: [
100 email
101 ]
102 },
103 Message: {
104 Body: {
105 Html: {
106 Charset: "UTF-8",
107 Data: getHtmlText(email, `${callbackUrl}?${auth_middleware_1.EMAIL_CONFIRMATION_PARAM}=${access_token}&${auth_middleware_1.EMAIL_PARAM}=${email}`)
108 },
109 },
110 Subject: {
111 Charset: 'UTF-8',
112 Data: getSubject(email)
113 }
114 },
115 Source: senderEmail,
116 ReplyToAddresses: [
117 senderEmail
118 ],
119 };
120 console.log("this is the fFetch of the mail-authentication");
121 // this is the response
122 return new Promise(function (resolve, reject) {
123 // Create the promise and SES service object
124 var sendPromise = new AWS.SES({ apiVersion: '2010-12-01' }).sendEmail(params).promise();
125 // Handle promise's fulfilled/rejected states
126 sendPromise.then(function (data) {
127 console.log(data.MessageId);
128 resolve({
129 id: email,
130 name: "",
131 username: "",
132 imageUrl: "",
133 access_token: access_token,
134 email: email,
135 encrypted_password: password,
136 status: auth_middleware_1.AUTH_STATUS.PENDING
137 });
138 }).catch(function (err) {
139 console.error(err, err.stack);
140 reject(err);
141 });
142 });
143 });
144 } /*fetch(callbackUrl,{
145 method: 'POST',
146 body: `code=${code}&client_id=${clientId}&client_secret=${getClientSecret(provider)}&grant_type=authorization_code&redirect_uri=${callbackUrl}`,
147 headers: {
148 "Content-Type": "application/x-www-form-urlencoded",
149 "Accept": "application/json",
150 "Accept-Charset": "utf-8"
151 }
152 })*/
153 };
154 }
155 }
156 else if (provider === exports.AuthenticationProvider.GITHUB) {
157 // TODO check https://octokit.github.io/rest.js/ !!!
158 // see docs: https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/
159 //console.log("request: ", req);
160 const { state, code, error, page } = req.query;
161 // session_code = request.env['rack.request.query_hash']['code']
162 if (error !== undefined) {
163 // TODO handle an error, e.g. user does not authorize the app
164 console.log(error);
165 return;
166 }
167 else if (code !== undefined) {
168 console.log("found redirect page: ", page);
169 return {
170 redirectPage: page,
171 fFetch: function () {
172 return __awaiter(this, void 0, void 0, function* () {
173 return yield fetch('https://github.com/login/oauth/access_token', {
174 method: 'POST',
175 body: `code=${code}&client_id=${clientId}&client_secret=${exports.getClientSecret(provider)}`,
176 headers: {
177 "Content-Type": "application/x-www-form-urlencoded",
178 "Accept": "application/json",
179 "Accept-Charset": "utf-8"
180 }
181 }).then(function (response) {
182 return response.json();
183 });
184 });
185 }
186 };
187 }
188 ;
189 }
190 else if (provider === exports.AuthenticationProvider.MEDIUM) {
191 console.log("Medium callback: ", req.query);
192 const { state, code, error } = req.query;
193 if (error !== undefined) {
194 // TODO handle an error, e.g. user does not authorize the app
195 console.log(error);
196 return;
197 }
198 else if (code !== undefined) {
199 return {
200 redirectPage: state,
201 fFetch: function () {
202 return __awaiter(this, void 0, void 0, function* () {
203 return yield fetch('https://api.medium.com/v1/tokens', {
204 method: 'POST',
205 body: `code=${code}&client_id=${clientId}&client_secret=${exports.getClientSecret(provider)}&grant_type=authorization_code&redirect_uri=${callbackUrl}`,
206 headers: {
207 "Content-Type": "application/x-www-form-urlencoded",
208 "Accept": "application/json",
209 "Accept-Charset": "utf-8"
210 }
211 }).then(function (response) {
212 console.log("request data: ", response);
213 return response.json();
214 });
215 });
216 }
217 };
218 }
219 }
220};
221exports.createGetUserFunction = (provider) => function (resJson) {
222 return __awaiter(this, void 0, void 0, function* () {
223 console.log("createGetUserFunction: ", resJson);
224 if (provider === exports.AuthenticationProvider.EMAIL) {
225 // we just provide the response-json
226 return new Promise(function (resolve, reject) {
227 resolve(resJson);
228 });
229 }
230 else if (provider === exports.AuthenticationProvider.GITHUB) {
231 const { token_type, access_token, } = resJson;
232 // try the freshly acquired token and get the user's Medium.com id
233 return yield fetch('https://api.github.com/user', {
234 method: 'GET',
235 headers: {
236 "Content-Type": "application/json",
237 "Accept": "application/json",
238 "Accept-Charset": "utf-8",
239 "Authorization": token_type + " " + access_token
240 }
241 }).then(function (response) {
242 // now parse the json
243 return response.json();
244 }).then(function (data) {
245 /*
246 "login":"frankzickert",
247 "id":10414265,
248 "node_id":"MDQ6VXNlcjEwNDE0MjY1",
249 "avatar_url":"https://avatars0.githubusercontent.com/u/10414265?v=4",
250 "gravatar_id":"",
251 "url":"https://api.github.com/users/frankzickert",
252 "html_url":"https://github.com/frankzickert",
253 "followers_url":"https://api.github.com/users/frankzickert/followers",
254 "following_url":"https://api.github.com/users/frankzickert/following{/other_user}",
255 "gists_url":"https://api.github.com/users/frankzickert/gists{/gist_id}",
256 "starred_url":"https://api.github.com/users/frankzickert/starred{/owner}{/repo}",
257 "subscriptions_url":"https://api.github.com/users/frankzickert/subscriptions",
258 "organizations_url":"https://api.github.com/users/frankzickert/orgs",
259 "repos_url":"https://api.github.com/users/frankzickert/repos",
260 "events_url":"https://api.github.com/users/frankzickert/events{/privacy}",
261 "received_events_url":"https://api.github.com/users/frankzickert/received_events",
262 "type":"User",
263 "site_admin":false,
264 "name":"Frank Zickert",
265 "company":null,
266 "blog":"https://www.lean-data-science.com/",
267 "location":"Germany",
268 "email":"frank.zickert@lean-data-science.awsapps.com",
269 "hireable":null,
270 "bio":"I have been working as an IT professional for 15 years...",
271 "public_repos":3,
272 "public_gists":5,
273 "followers":1,
274 "following":0,
275 "created_at":"2015-01-06T07:23:33Z",
276 "updated_at":"2019-05-08T20:37:43Z"}
277 */
278 return {
279 id: data.id,
280 name: data.name,
281 username: data.login,
282 imageUrl: data.avatar_url,
283 email: data.email,
284 access_token: access_token,
285 status: auth_middleware_1.AUTH_STATUS.ACTIVE
286 };
287 });
288 }
289 else if (provider === exports.AuthenticationProvider.MEDIUM) {
290 console.log("fetch user data of medium-user");
291 const { token_type, access_token, refresh_token, scope, expires_at } = resJson;
292 // try the freshly acquired token and get the user's Medium.com id
293 return yield fetch('https://api.medium.com/v1/me', {
294 method: 'GET',
295 headers: {
296 "Content-Type": "application/json",
297 "Accept": "application/json",
298 "Accept-Charset": "utf-8",
299 "Authorization": token_type + " " + access_token
300 }
301 }).then(function (response) {
302 //console.log("user data fetched: ", response);
303 // now parse the json
304 return response.json();
305 }).then(function ({ data }) {
306 console.log("user data parsed: ", data);
307 return {
308 id: data.id,
309 name: data.name,
310 username: data.username,
311 imageUrl: data.imageUrl,
312 email: data.email,
313 access_token: access_token,
314 status: auth_middleware_1.AUTH_STATUS.ACTIVE
315 };
316 });
317 }
318 });
319};
320/**
321 * suffix to the provider. specifies the -env-variable to hold the clientSecret
322 */
323const SUFFIX_SECRET = "_SECRET";
324/**
325 * The WebApp is a client that runs in the browser, SPA or SSR
326 *
327 * @param props
328 */
329exports.default = (props) => {
330 //console.log ("route: ",props );
331 const componentProps = {
332 infrastructureType: types_1.default.INFRASTRUCTURE_TYPE_COMPONENT,
333 instanceType: exports.AUTHENTICATION_INSTANCE_TYPE,
334 instanceId: props.id,
335 };
336 const authenticationProps = {
337 // set our storeData-Property
338 setStoreIdentityData: (storeIdentityData) => {
339 //console.log("setStoreIdentityData: ", storeIdentityData);
340 props.storeAuthData = storeIdentityData;
341 //console.log("auth-props: ", props)
342 },
343 setGetIdentityData: (getIdentityData) => {
344 props.getAuthData = getIdentityData;
345 },
346 authCallback: (email, password, page, onResponse) => {
347 console.log("this is the auth-Callback");
348 if (props.provider === exports.AuthenticationProvider.EMAIL) {
349 // verify the format of the e-mail address
350 if (!(/^([A-Za-z0-9_\-.])+@([A-Za-z0-9_\-.])+\.([A-Za-z]{2,})$/.test(email))) {
351 onResponse(exports.AUTH_RESPONSE.EMAIL_INVALID);
352 return;
353 }
354 // redirect to the provided url that is
355 // TODO encrypt the password!
356 window.location.href = `${props.callbackUrl}?${auth_middleware_1.EMAIL_PARAM}=${email}&${auth_middleware_1.PASSWORD_PARAM}=${password}&page=${page}`;
357 return;
358 }
359 onResponse(exports.AUTH_RESPONSE.NOT_IMPLEMENTED);
360 }
361 };
362 /**
363 * For this function is a callback on a middleware, it only sets the userId on the backend side!
364 * @param userid
365 */
366 const onAuthenticated = (userid) => {
367 console.log("just authenticated, tell the securedEntries about it");
368 // we need to provide some data to the secured entries
369 libs_1.findComponentRecursively(props.children, (c) => c.setUserId !== undefined).forEach(se => {
370 //console.log("found secured entry: ", se);
371 se.setUserId(userid);
372 });
373 };
374 libs_1.findComponentRecursively(props.children, (c) => c.setMiddleware !== undefined).forEach(se => {
375 //console.log("found secured entry: ", se);
376 se.setMiddleware(middleware_component_1.default({ callback: auth_middleware_1.createAuthMiddleware(exports.getClientSecret(props.provider), onAuthenticated) }));
377 });
378 // we need to provide some data to the secured routes
379 libs_1.findComponentRecursively(props.children, securedroute_component_1.isSecuredRoute).forEach(sr => {
380 //console.log("found secured route: ", sr);
381 sr.middlewares = [
382 // this middleware checks whether the user is logged in
383 middleware_component_1.default({ callback: auth_middleware_1.createAuthMiddleware(exports.getClientSecret(props.provider), onAuthenticated) }),
384 // this middleware checks redirects the user to the login page, if she is not logged in
385 middleware_component_1.default({ callback: exports.createRequestLoginMiddleware(props.clientId, props.callbackUrl, props.provider, props.loginUrl) })
386 ].concat(sr.middlewares);
387 // now that we have added the authentication middlewares, the route can be handled as a normal one
388 sr.instanceType = route_component_1.ROUTE_INSTANCE_TYPE;
389 });
390 // we need to provide some data to the secured routes
391 libs_1.findComponentRecursively(props.children, securedservice_component_1.isSecuredService).forEach(service => {
392 //console.log("found secured route: ", sr);
393 service.middlewares = [
394 // this middleware checks whether the user is logged in
395 middleware_component_1.default({ callback: auth_middleware_1.createAuthMiddleware(exports.getClientSecret(props.provider), onAuthenticated) }),
396 // this middleware checks redirects the user to the login page, if she is not logged in
397 middleware_component_1.default({ callback: exports.createRequestLoginMiddleware(props.clientId, props.callbackUrl, props.provider, props.loginUrl) })
398 ].concat(service.middlewares);
399 // now that we have added the authentication middlewares, the route can be handled as a normal one
400 service.instanceType = service_component_1.SERVICE_INSTANCE_TYPE;
401 });
402 // we need to provide the AuthenticationId to webApps, these may be anywhere in the tree, not
403 // only direct children. So rather than mapping the children, we need to change them
404 libs_1.findComponentRecursively(props.children, (child) => child.setAuthenticationId !== undefined).forEach(child => {
405 child.setAuthenticationId(props.id);
406 });
407 /**
408 * The data-layer replaces the authentication component with its children
409 */
410 const mappedChildren = {
411 // we provide the middlewares that we require
412 children: [
413 // we create a webapp to handle the callback
414 webapp_component_1.default({
415 id: "WEBAPP_" + props.provider,
416 path: props.callbackUrl.substring(props.callbackUrl.lastIndexOf("/")),
417 method: "GET",
418 children: [
419 // middleware required of parsing the json response
420 middleware_component_1.default({ callback: body_parser_1.default.json() }),
421 middleware_component_1.default({ callback: body_parser_1.default.urlencoded({
422 extended: true
423 }) }),
424 middleware_component_1.default({ callback: auth_middleware_1.createCallbackMiddleware(exports.getClientSecret(props.provider), exports.createFetchAccessTokenFunction(props.clientId, props.callbackUrl, props.provider, props.senderEmail, props.getSubject, props.getHtmlText), //fetchAccessToken
425 exports.createGetUserFunction(props.provider), function (request, key, val, jsonData) {
426 return __awaiter(this, void 0, void 0, function* () {
427 return yield props.storeAuthData(request, key, val, jsonData);
428 });
429 }, //props.storeAuthData
430 function (request, matchBrowserIdentity, key, val) {
431 return __awaiter(this, void 0, void 0, function* () {
432 return yield props.getAuthData(request, matchBrowserIdentity, key, val);
433 });
434 } //getAuthData
435 ) }),
436 middleware_component_1.default({
437 callback: exports.createRequestLoginMiddleware(props.clientId, props.callbackUrl, props.provider, props.loginUrl)
438 }),
439 // the render function should never be called for the Callback-Middleware redirects to the
440 // page that was requested in the first request
441 route_component_1.default({
442 path: props.callbackUrl.substring(props.callbackUrl.lastIndexOf("/")),
443 name: "WEBAPP_" + props.provider,
444 render: (rp) => react_1.default.createElement("div", null, "This is the callback route!"),
445 })
446 ]
447 })
448 //,
449 //(browserId: string) => redirectMiddleware("/dashboard")
450 ].concat(libs_1.getChildrenArray(props.children))
451 };
452 console.log("authentication: ", libs_1.findComponentRecursively(props.children, webapp_component_1.isWebApp));
453 return Object.assign(props, componentProps, authenticationProps, mappedChildren);
454};
455exports.isAuthentication = (component) => {
456 return component !== undefined &&
457 component.instanceType === exports.AUTHENTICATION_INSTANCE_TYPE;
458};
459//# sourceMappingURL=authentication-component.js.map
\No newline at end of file