UNPKG

infrastructure-components

Version:

Infrastructure-Components configure the infrastructure of your React-App as part of your React-Components.

238 lines • 13.1 kB
"use strict"; var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } 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) : adopt(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 }; }; Object.defineProperty(exports, "__esModule", { value: true }); const authentication_component_1 = require("./authentication-component"); const jsonwebtoken_1 = __importDefault(require("jsonwebtoken")); const iso_libs_1 = require("../libs/iso-libs"); /** * token handed over to the user's browser, serves as password to encrypt/decrypt the Medium-access token * @type {string} */ exports.IC_WEB_TOKEN = "IC_WEB_TOKEN"; /** * unique id of the user, comes from the provider (GitHub, Medium, etc) * @type {string} */ exports.IC_USER_ID = 'IC_USER_ID'; exports.EMAIL_CONFIRMATION_PARAM = "confirmationtoken"; exports.EMAIL_PARAM = "email"; exports.PASSWORD_PARAM = "password"; exports.AUTH_STATUS = { PENDING: "pending", ACTIVE: "active" // the authentication is active }; /** * This is an Express middleware that checks whether there is a cookie in the header that contains valid login * data * * @param req * @param res * @param next * @returns if successful, it calls the next middleware, if not, it throws an exception that causes the * next error handler to be called */ exports.createAuthMiddleware = (clientSecret, onAuthenticated) => (req, res, next) => { //console.log("createAuthMiddleware", req.universalCookies); const webtoken = req.universalCookies.get(exports.IC_WEB_TOKEN); const userId = req.universalCookies.get(exports.IC_USER_ID); if (webtoken !== undefined && userId !== undefined) { //console.log("webtoken: ", webtoken); //console.log("userId: ", userId); try { const decoded = jsonwebtoken_1.default.verify(webtoken, clientSecret); if (decoded !== undefined) { const { id } = decoded; //console.log("id: ", id); // we might have numbers... then the "===" comparison does not work if (id.toString() === userId.toString()) { // the token contains the correct id //console.log("token matches :-)") onAuthenticated(id.toString()); return next(); } } return next(authentication_component_1.AUTH_MESSAGE.FAILED); //throw new Error("UserId in Token does not match UserId in cookie"); } catch (err) { return next(authentication_component_1.AUTH_MESSAGE.FAILED); //throw new Error(err); } } else { return next(authentication_component_1.AUTH_MESSAGE.NOTLOGGEDIN); //throw new Error('No token present!'); } }; const getEncryptedAccessToken = (id, clientSecret, access_token) => { const today = new Date(); const expirationDate = new Date(today); expirationDate.setDate(today.getDate() + 60); // we use the clientSecret to sign the webtoken const webtoken = jsonwebtoken_1.default.sign({ id: id, exp: expirationDate.getTime() / 1000, }, clientSecret); // now let's use the webtoken to encrypt the access token const encryptedAccessToken = jsonwebtoken_1.default.sign({ id: id, accessToken: access_token, exp: expirationDate.getTime() / 1000, }, webtoken); return { webtoken: webtoken, encryptedAccessToken: encryptedAccessToken }; }; /** * Use this middleware at the endpoint that is specified as the callback-url. * * @param fetchAccessToken function that can be called to fetch the access Token * @param getUserData function to get the userData, takes as input the response from the accessToken-request * @param clientSecret * @param callbackUrl * @param storeAuthData * @returns {any} */ exports.createCallbackMiddleware = (clientSecret, fetchAccessToken, getUserData, storeAuthData, getAuthData, loginUrl) => function (req, res, next) { return __awaiter(this, void 0, void 0, function* () { const path = require('path'); //console.log("THIS IS THE AUTH CALLBACK - authMiddleware"); // we use this middleware also as endpoint for email confirmation, then the token-parameter must be specified const email_confirmation = req.query[exports.EMAIL_CONFIRMATION_PARAM]; const email_param = req.query[exports.EMAIL_PARAM]; const password_param = req.query[exports.PASSWORD_PARAM]; const page = req.query["page"]; //console.log("received params: ", email_confirmation, email_param, password_param); if (email_param) { // get the entry of the database const authDataList = yield getAuthData(req, // request: any false, //matchBrowserIdentity -- we do not want to match the browser identity, the user might use another browser to confirm he mail address exports.IC_USER_ID, // key: string email_param //val: any, ); //console.log("retrieved auth-data-list: ", authDataList); // check whether the user already exists const parsedAuthDataList = authDataList.map(raw => JSON.parse(raw.jsonData)); // the user logs in with her email and password if (password_param !== undefined && parsedAuthDataList.length > 0) { const authData = parsedAuthDataList .reduce((result, cur) => result !== undefined ? ( // check whether we have a better state! cur.status == exports.AUTH_STATUS.ACTIVE ? cur : result) : ( // check whether the password is correct cur.encrypted_password === password_param ? cur : undefined), undefined); if (authData !== undefined) { if (authData.status == exports.AUTH_STATUS.PENDING) { return next(authentication_component_1.AUTH_MESSAGE.VERIFICATIONPENDING); } // create a new webtoken, i.e. other browser will be logged out! const { webtoken, encryptedAccessToken } = getEncryptedAccessToken(email_param, clientSecret, password_param); // put the encrypted web token into the database, this is user (browser)-specific data! const storeResult = yield storeAuthData(req, // request: any exports.IC_USER_ID, // key: string email_param, //val: any, Object.assign({}, authData, { encryptedAccessToken: encryptedAccessToken })); req.universalCookies.set(exports.IC_WEB_TOKEN, webtoken, { path: '/' }); req.universalCookies.set(exports.IC_USER_ID, email_param, { path: '/' }); //console.log("store password verified result: ", storeResult); res.redirect(`${path.join(iso_libs_1.getBasename(), page !== undefined ? page : loginUrl)}?message=${authentication_component_1.AUTH_MESSAGE.SUCCESS}`); } else { //console.log ("could not verify password, ", password_param,email_param); return next(authentication_component_1.AUTH_MESSAGE.FAILED); } return; } else if (email_confirmation && parsedAuthDataList.length > 0) { // the user clicks the link from within the confirmation email const authData = parsedAuthDataList .reduce((result, cur) => result !== undefined ? result : (cur.encryptedAccessToken === email_confirmation ? cur : undefined), undefined); //console.log("retrieved auth-data: ", authData); if (authData !== undefined) { const { webtoken, encryptedAccessToken } = getEncryptedAccessToken(email_param, clientSecret, email_confirmation); // put the encrypted web token into the database, this is user (browser)-specific data! const storeResult = yield storeAuthData(req, // request: any exports.IC_USER_ID, // key: string email_param, //val: any, Object.assign({}, authData, { status: exports.AUTH_STATUS.ACTIVE, encryptedAccessToken: encryptedAccessToken })); //console.log("webtoken: ", webtoken, email_param) req.universalCookies.set(exports.IC_WEB_TOKEN, webtoken, { path: '/' }); req.universalCookies.set(exports.IC_USER_ID, email_param, { path: '/' }); //console.log("store email verified result: ", storeResult); res.redirect(`${path.join(iso_libs_1.getBasename(), page !== undefined ? page : loginUrl)}?message=${authentication_component_1.AUTH_MESSAGE.MAILVERIFIED}`); } else { //console.log ("could not verify access token, ", email_confirmation,email_param); return next(authentication_component_1.AUTH_MESSAGE.FAILED); } return; } } const { redirectPage, fFetch } = fetchAccessToken(req); // store the redirectPage in the request for further processing console.log("redirect to: ", redirectPage); req["redirectPage"] = redirectPage; yield fFetch().then(function (resJson) { return __awaiter(this, void 0, void 0, function* () { //const { token_type, access_token /*, refresh_token, scope, expires_at */} = resJson; // try the freshly acquired token and get the user's Medium.com id yield getUserData(resJson).then(function (data) { return __awaiter(this, void 0, void 0, function* () { //console.log("get user data: ", JSON.stringify(data)); const { id, name, username, imageUrl, access_token, email, status } = data; //console.log("id: ", id); //console.log("name: ", name); const { webtoken, encryptedAccessToken } = getEncryptedAccessToken(id, clientSecret, access_token); //console.log("encryptedAccessToken: ", encryptedAccessToken); // TODO id may be undefined when the token expired! //console.log("storeAuthData: ", storeAuthData) // put the encrypted web token into the database, this is user (browser)-specific data! const storeResult = yield storeAuthData(req, // request: any exports.IC_USER_ID, // key: string id, //val: any, Object.assign({ /** We only store the encrypted token when we have an active status, i.e. a auth-provider * we keep it in clear-text for e-mail */ encryptedAccessToken: status === exports.AUTH_STATUS.ACTIVE ? encryptedAccessToken : access_token, name: name, username: username, imageUrl: imageUrl, email: email, status: status, }, password_param ? { encrypted_password: password_param } : {}) //jsonData: any ); //console.log("storeResult: ", storeResult); // give the webtoken to back to the user - if the account is valid, only! if (status === exports.AUTH_STATUS.ACTIVE) { req.universalCookies.set(exports.IC_WEB_TOKEN, webtoken, { path: '/' }); req.universalCookies.set(exports.IC_USER_ID, id, { path: '/' }); } //console.log("done") //'http://' +path.join(req.headers.host + + res.redirect(path.join(iso_libs_1.getBasename(), redirectPage)); return; }); }); }); }); }); }; //# sourceMappingURL=auth-middleware.js.map