infrastructure-components
Version:
Infrastructure-Components configure the infrastructure of your React-App as part of your React-Components.
451 lines • 23.7 kB
JavaScript
;
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
return new (P || (P = Promise))(function (resolve, reject) {
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
function step(result) { result.done ? resolve(result.value) : new P(function (resolve) { resolve(result.value); }).then(fulfilled, rejected); }
step((generator = generator.apply(thisArg, _arguments || [])).next());
});
};
var __importDefault = (this && this.__importDefault) || function (mod) {
return (mod && mod.__esModule) ? mod : { "default": mod };
};
var __importStar = (this && this.__importStar) || function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
result["default"] = mod;
return result;
};
Object.defineProperty(exports, "__esModule", { value: true });
const react_1 = __importDefault(require("react"));
const types_1 = __importDefault(require("../types"));
const middleware_component_1 = __importDefault(require("../middleware/middleware-component"));
const webapp_component_1 = __importStar(require("../webapp/webapp-component"));
const securedroute_component_1 = require("./securedroute-component");
const route_component_1 = __importStar(require("../route/route-component"));
const libs_1 = require("../libs");
const iso_libs_1 = require("../libs/iso-libs");
const auth_middleware_1 = require("./auth-middleware");
const body_parser_1 = __importDefault(require("body-parser"));
const securedservice_component_1 = require("./securedservice-component");
const service_component_1 = require("../service/service-component");
exports.AUTHENTICATION_INSTANCE_TYPE = "AuthenticationComponent";
exports.AuthenticationProvider = {
EMAIL: "AUTH_EMAIL",
GITHUB: "AUTH_GITHUB",
// TODO Medium Auth only partly implemented!!!
MEDIUM: "AUTH_MEDIUM"
};
exports.AUTH_RESPONSE = {
EMAIL_INVALID: "EMAIL_INVALID",
NOT_IMPLEMENTED: "NOT_IMPLEMENTED" // the authCallback is not implemented for this provider
};
exports.getProviderKey = (provider) => {
return provider + SUFFIX_SECRET;
};
exports.getClientSecret = (provider) => {
return process.env[exports.getProviderKey(provider)];
};
/**
* This creates a customized middleware that catches an error when the user is not logged in, i.e. it forwards her
* to the provider's login-page
*
* @param clientId as defined in the app of the provider
* @param callbackUrl as defined in the app of the provider
* @param provider to create the redirect Url for
*/
exports.createRequestLoginMiddleware = (clientId, callbackUrl, provider, loginUrl) => (err, req, res, next) => {
const path = require('path');
console.log("createRequestLoginMiddleware: ", err);
if (provider === exports.AuthenticationProvider.EMAIL) {
console.log("request login from: ", loginUrl);
const page = err && req.query && req.query.page ? req.query.page : req.url;
res.redirect(`${path.join(iso_libs_1.getBasename(), loginUrl)}?page=${page}${err ? `&message=${err}` : ""}`);
}
else if (provider === exports.AuthenticationProvider.GITHUB) {
res.redirect(`https://github.com/login/oauth/authorize?scope=user:email&client_id=${clientId}&redirect_uri=${callbackUrl}?page=${req.url}`);
}
else if (provider === exports.AuthenticationProvider.MEDIUM) {
res.redirect(`https://medium.com/m/oauth/authorize?client_id=${clientId}&scope=basicProfile,listPublications,publishPost&state=InteractiveMedium&response_type=code&redirect_uri=${callbackUrl}?page=${req.url}`);
}
return;
};
/**
* Create a function that creates a function that fetches the AccessToken of the provider
*/
exports.createFetchAccessTokenFunction = (clientId, callbackUrl, provider, senderEmail, getSubject, getHtmlText) => (req) => {
if (provider === exports.AuthenticationProvider.EMAIL) {
console.log("this is the EMAIL - createFetchAccessTokenFunction middleware");
const { email, password, page } = req.query;
if (email !== undefined && password !== undefined) {
// the function fFetch must call a service that responds with a json. this json is fed into createGetUserFunction
return {
redirectPage: page,
fFetch: function () {
return __awaiter(this, void 0, void 0, function* () {
const uuidv4 = require('uuid/v4');
const access_token = uuidv4();
// TODO: here we call the service to send an email to the user
// see: https://docs.aws.amazon.com/de_de/sdk-for-javascript/v2/developer-guide/ses-examples-sending-email.html
const AWS = require('aws-sdk');
// Create sendEmail params
var params = {
Destination: {
BccAddresses: [
senderEmail
],
CcAddresses: [],
ToAddresses: [
email
]
},
Message: {
Body: {
Html: {
Charset: "UTF-8",
Data: getHtmlText(email, `${callbackUrl}?${auth_middleware_1.EMAIL_CONFIRMATION_PARAM}=${access_token}&${auth_middleware_1.EMAIL_PARAM}=${email}`)
},
},
Subject: {
Charset: 'UTF-8',
Data: getSubject(email)
}
},
Source: senderEmail,
ReplyToAddresses: [
senderEmail
],
};
console.log("this is the fFetch of the mail-authentication");
// this is the response
return new Promise(function (resolve, reject) {
// Create the promise and SES service object
var sendPromise = new AWS.SES({ apiVersion: '2010-12-01' }).sendEmail(params).promise();
// Handle promise's fulfilled/rejected states
sendPromise.then(function (data) {
console.log(data.MessageId);
resolve({
id: email,
name: "",
username: "",
imageUrl: "",
access_token: access_token,
email: email,
encrypted_password: password,
status: auth_middleware_1.AUTH_STATUS.PENDING
});
}).catch(function (err) {
console.error(err, err.stack);
reject(err);
});
});
});
} /*fetch(callbackUrl,{
method: 'POST',
body: `code=${code}&client_id=${clientId}&client_secret=${getClientSecret(provider)}&grant_type=authorization_code&redirect_uri=${callbackUrl}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "application/json",
"Accept-Charset": "utf-8"
}
})*/
};
}
}
else if (provider === exports.AuthenticationProvider.GITHUB) {
// TODO check https://octokit.github.io/rest.js/ !!!
// see docs: https://developer.github.com/apps/building-oauth-apps/authorizing-oauth-apps/
//console.log("request: ", req);
const { state, code, error, page } = req.query;
// session_code = request.env['rack.request.query_hash']['code']
if (error !== undefined) {
// TODO handle an error, e.g. user does not authorize the app
console.log(error);
return;
}
else if (code !== undefined) {
console.log("found redirect page: ", page);
return {
redirectPage: page,
fFetch: function () {
return __awaiter(this, void 0, void 0, function* () {
return yield fetch('https://github.com/login/oauth/access_token', {
method: 'POST',
body: `code=${code}&client_id=${clientId}&client_secret=${exports.getClientSecret(provider)}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "application/json",
"Accept-Charset": "utf-8"
}
}).then(function (response) {
return response.json();
});
});
}
};
}
;
}
else if (provider === exports.AuthenticationProvider.MEDIUM) {
console.log("Medium callback: ", req.query);
const { state, code, error, page } = req.query;
if (error !== undefined) {
// TODO handle an error, e.g. user does not authorize the app
console.log(error);
return;
}
else if (code !== undefined) {
return {
redirectPage: page,
fFetch: fetch('https://api.medium.com/v1/tokens', {
method: 'POST',
body: `code=${code}&client_id=${clientId}&client_secret=${exports.getClientSecret(provider)}&grant_type=authorization_code&redirect_uri=${callbackUrl}`,
headers: {
"Content-Type": "application/x-www-form-urlencoded",
"Accept": "application/json",
"Accept-Charset": "utf-8"
}
}).then(function (response) {
return response.json();
})
};
}
}
};
exports.createGetUserFunction = (provider) => function (resJson) {
return __awaiter(this, void 0, void 0, function* () {
console.log("resJson: ", resJson);
if (provider === exports.AuthenticationProvider.EMAIL) {
// we just provide the response-json
return new Promise(function (resolve, reject) {
resolve(resJson);
});
}
else if (provider === exports.AuthenticationProvider.GITHUB) {
const { token_type, access_token, } = resJson;
// try the freshly acquired token and get the user's Medium.com id
return yield fetch('https://api.github.com/user', {
method: 'GET',
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
"Accept-Charset": "utf-8",
"Authorization": token_type + " " + access_token
}
}).then(function (response) {
// now parse the json
return response.json();
}).then(function (data) {
/*
"login":"frankzickert",
"id":10414265,
"node_id":"MDQ6VXNlcjEwNDE0MjY1",
"avatar_url":"https://avatars0.githubusercontent.com/u/10414265?v=4",
"gravatar_id":"",
"url":"https://api.github.com/users/frankzickert",
"html_url":"https://github.com/frankzickert",
"followers_url":"https://api.github.com/users/frankzickert/followers",
"following_url":"https://api.github.com/users/frankzickert/following{/other_user}",
"gists_url":"https://api.github.com/users/frankzickert/gists{/gist_id}",
"starred_url":"https://api.github.com/users/frankzickert/starred{/owner}{/repo}",
"subscriptions_url":"https://api.github.com/users/frankzickert/subscriptions",
"organizations_url":"https://api.github.com/users/frankzickert/orgs",
"repos_url":"https://api.github.com/users/frankzickert/repos",
"events_url":"https://api.github.com/users/frankzickert/events{/privacy}",
"received_events_url":"https://api.github.com/users/frankzickert/received_events",
"type":"User",
"site_admin":false,
"name":"Frank Zickert",
"company":null,
"blog":"https://www.lean-data-science.com/",
"location":"Germany",
"email":"frank.zickert@lean-data-science.awsapps.com",
"hireable":null,
"bio":"I have been working as an IT professional for 15 years...",
"public_repos":3,
"public_gists":5,
"followers":1,
"following":0,
"created_at":"2015-01-06T07:23:33Z",
"updated_at":"2019-05-08T20:37:43Z"}
*/
return {
id: data.id,
name: data.name,
username: data.login,
imageUrl: data.avatar_url,
email: data.email,
access_token: access_token,
status: auth_middleware_1.AUTH_STATUS.ACTIVE
};
});
}
else if (provider === exports.AuthenticationProvider.MEDIUM) {
const { token_type, access_token, refresh_token, scope, expires_at } = resJson;
// try the freshly acquired token and get the user's Medium.com id
return yield fetch('https://api.medium.com/v1/me', {
method: 'GET',
headers: {
"Content-Type": "application/json",
"Accept": "application/json",
"Accept-Charset": "utf-8",
"Authorization": token_type + " " + access_token
}
}).then(function (response) {
// now parse the json
return response.json();
}).then(function (data) {
return {
id: data.id,
name: data.name,
username: data.username,
imageUrl: data.imageUrl,
email: data.email,
access_token: access_token,
status: auth_middleware_1.AUTH_STATUS.ACTIVE
};
});
}
});
};
/**
* suffix to the provider. specifies the -env-variable to hold the clientSecret
*/
const SUFFIX_SECRET = "_SECRET";
/**
* The WebApp is a client that runs in the browser, SPA or SSR
*
* @param props
*/
exports.default = (props) => {
//console.log ("route: ",props );
const componentProps = {
infrastructureType: types_1.default.INFRASTRUCTURE_TYPE_COMPONENT,
instanceType: exports.AUTHENTICATION_INSTANCE_TYPE,
instanceId: props.id,
};
const authenticationProps = {
// set our storeData-Property
setStoreIdentityData: (storeIdentityData) => {
//console.log("setStoreIdentityData: ", storeIdentityData);
props.storeAuthData = storeIdentityData;
//console.log("auth-props: ", props)
},
setGetIdentityData: (getIdentityData) => {
props.getAuthData = getIdentityData;
},
authCallback: (email, password, page, onResponse) => {
console.log("this is the auth-Callback");
if (props.provider === exports.AuthenticationProvider.EMAIL) {
// verify the format of the e-mail address
if (!(/^([A-Za-z0-9_\-.])+@([A-Za-z0-9_\-.])+\.([A-Za-z]{2,})$/.test(email))) {
onResponse(exports.AUTH_RESPONSE.EMAIL_INVALID);
return;
}
// redirect to the provided url that is
// TODO encrypt the password!
window.location.href = `${props.callbackUrl}?${auth_middleware_1.EMAIL_PARAM}=${email}&${auth_middleware_1.PASSWORD_PARAM}=${password}&page=${page}`;
return;
}
onResponse(exports.AUTH_RESPONSE.NOT_IMPLEMENTED);
}
};
/**
* For this function is a callback on a middleware, it only sets the userId on the backend side!
* @param userid
*/
const onAuthenticated = (userid) => {
console.log("just authenticated, tell the securedEntries about it");
// we need to provide some data to the secured entries
libs_1.findComponentRecursively(props.children, (c) => c.setUserId !== undefined).forEach(se => {
//console.log("found secured entry: ", se);
se.setUserId(userid);
});
};
libs_1.findComponentRecursively(props.children, (c) => c.setMiddleware !== undefined).forEach(se => {
//console.log("found secured entry: ", se);
se.setMiddleware(middleware_component_1.default({ callback: auth_middleware_1.createAuthMiddleware(exports.getClientSecret(props.provider), onAuthenticated) }));
});
// we need to provide some data to the secured routes
libs_1.findComponentRecursively(props.children, securedroute_component_1.isSecuredRoute).forEach(sr => {
//console.log("found secured route: ", sr);
sr.middlewares = [
// this middleware checks whether the user is logged in
middleware_component_1.default({ callback: auth_middleware_1.createAuthMiddleware(exports.getClientSecret(props.provider), onAuthenticated) }),
// this middleware checks redirects the user to the login page, if she is not logged in
middleware_component_1.default({ callback: exports.createRequestLoginMiddleware(props.clientId, props.callbackUrl, props.provider, props.loginUrl) })
].concat(sr.middlewares);
// now that we have added the authentication middlewares, the route can be handled as a normal one
sr.instanceType = route_component_1.ROUTE_INSTANCE_TYPE;
});
// we need to provide some data to the secured routes
libs_1.findComponentRecursively(props.children, securedservice_component_1.isSecuredService).forEach(service => {
//console.log("found secured route: ", sr);
service.middlewares = [
// this middleware checks whether the user is logged in
middleware_component_1.default({ callback: auth_middleware_1.createAuthMiddleware(exports.getClientSecret(props.provider), onAuthenticated) }),
// this middleware checks redirects the user to the login page, if she is not logged in
middleware_component_1.default({ callback: exports.createRequestLoginMiddleware(props.clientId, props.callbackUrl, props.provider, props.loginUrl) })
].concat(service.middlewares);
// now that we have added the authentication middlewares, the route can be handled as a normal one
service.instanceType = service_component_1.SERVICE_INSTANCE_TYPE;
});
// we need to provide the AuthenticationId to webApps, these may be anywhere in the tree, not
// only direct children. So rather than mapping the children, we need to change them
libs_1.findComponentRecursively(props.children, (child) => child.setAuthenticationId !== undefined).forEach(child => {
child.setAuthenticationId(props.id);
});
/**
* The data-layer replaces the authentication component with its children
*/
const mappedChildren = {
// we provide the middlewares that we require
children: [
// we create a webapp to handle the callback
webapp_component_1.default({
id: "WEBAPP_" + props.provider,
path: props.callbackUrl.substring(props.callbackUrl.lastIndexOf("/")),
method: "GET",
children: [
// middleware required of parsing the json response
middleware_component_1.default({ callback: body_parser_1.default.json() }),
middleware_component_1.default({ callback: body_parser_1.default.urlencoded({
extended: true
}) }),
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
exports.createGetUserFunction(props.provider), function (request, key, val, jsonData) {
return __awaiter(this, void 0, void 0, function* () {
return yield props.storeAuthData(request, key, val, jsonData);
});
}, //props.storeAuthData
function (request, matchBrowserIdentity, key, val) {
return __awaiter(this, void 0, void 0, function* () {
return yield props.getAuthData(request, matchBrowserIdentity, key, val);
});
} //getAuthData
) }),
middleware_component_1.default({
callback: exports.createRequestLoginMiddleware(props.clientId, props.callbackUrl, props.provider, props.loginUrl)
}),
// the render function should never be called for the Callback-Middleware redirects to the
// page that was requested in the first request
route_component_1.default({
path: props.callbackUrl.substring(props.callbackUrl.lastIndexOf("/")),
name: "WEBAPP_" + props.provider,
render: (rp) => react_1.default.createElement("div", null, "This is the callback route!"),
})
]
})
//,
//(browserId: string) => redirectMiddleware("/dashboard")
].concat(libs_1.getChildrenArray(props.children))
};
console.log("authentication: ", libs_1.findComponentRecursively(props.children, webapp_component_1.isWebApp));
return Object.assign(props, componentProps, authenticationProps, mappedChildren);
};
exports.isAuthentication = (component) => {
return component !== undefined &&
component.instanceType === exports.AUTHENTICATION_INSTANCE_TYPE;
};
//# sourceMappingURL=authentication-component.js.map