UNPKG

12.4 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};
13Object.defineProperty(exports, "__esModule", { value: true });
14const jsonwebtoken_1 = __importDefault(require("jsonwebtoken"));
15const iso_libs_1 = require("../libs/iso-libs");
16/**
17 * token handed over to the user's browser, serves as password to encrypt/decrypt the Medium-access token
18 * @type {string}
19 */
20exports.IC_WEB_TOKEN = "IC_WEB_TOKEN";
21/**
22 * unique id of the user, comes from the provider (GitHub, Medium, etc)
23 * @type {string}
24 */
25exports.IC_USER_ID = 'IC_USER_ID';
26exports.EMAIL_CONFIRMATION_PARAM = "confirmationtoken";
27exports.EMAIL_PARAM = "email";
28exports.PASSWORD_PARAM = "password";
29exports.AUTH_STATUS = {
30 PENDING: "pending",
31 ACTIVE: "active" // the authentication is active
32};
33/**
34 * This is an Express middleware that checks whether there is a cookie in the header that contains valid login
35 * data
36 *
37 * @param req
38 * @param res
39 * @param next
40 * @returns if successful, it calls the next middleware, if not, it throws an exception that causes the
41 * next error handler to be called
42 */
43exports.createAuthMiddleware = (clientSecret, onAuthenticated) => (req, res, next) => {
44 console.log("createAuthMiddleware", req.universalCookies);
45 const webtoken = req.universalCookies.get(exports.IC_WEB_TOKEN);
46 const userId = req.universalCookies.get(exports.IC_USER_ID);
47 if (webtoken !== undefined && userId !== undefined) {
48 console.log("webtoken: ", webtoken);
49 console.log("userId: ", userId);
50 try {
51 const decoded = jsonwebtoken_1.default.verify(webtoken, clientSecret);
52 if (decoded !== undefined) {
53 const { id } = decoded;
54 console.log("id: ", id);
55 // we might have numbers... then the "===" comparison does not work
56 if (id.toString() === userId.toString()) {
57 // the token contains the correct id
58 console.log("token matches :-)");
59 onAuthenticated(id.toString());
60 return next();
61 }
62 }
63 return next("UserId in Token does not match UserId in cookie");
64 //throw new Error("UserId in Token does not match UserId in cookie");
65 }
66 catch (err) {
67 return next(err);
68 //throw new Error(err);
69 }
70 }
71 else {
72 return next('No token present!');
73 //throw new Error('No token present!');
74 }
75};
76const getEncryptedAccessToken = (id, clientSecret, access_token) => {
77 const today = new Date();
78 const expirationDate = new Date(today);
79 expirationDate.setDate(today.getDate() + 60);
80 // we use the clientSecret to sign the webtoken
81 const webtoken = jsonwebtoken_1.default.sign({
82 id: id,
83 exp: expirationDate.getTime() / 1000,
84 }, clientSecret);
85 // now let's use the webtoken to encrypt the access token
86 const encryptedAccessToken = jsonwebtoken_1.default.sign({
87 id: id,
88 accessToken: access_token,
89 exp: expirationDate.getTime() / 1000,
90 }, webtoken);
91 return {
92 webtoken: webtoken,
93 encryptedAccessToken: encryptedAccessToken
94 };
95};
96/**
97 * Use this middleware at the endpoint that is specified as the callback-url.
98 *
99 * @param fetchAccessToken function that can be called to fetch the access Token
100 * @param getUserData function to get the userData, takes as input the response from the accessToken-request
101 * @param clientSecret
102 * @param callbackUrl
103 * @param storeAuthData
104 * @returns {any}
105 */
106exports.createCallbackMiddleware = (clientSecret, fetchAccessToken, getUserData, storeAuthData, getAuthData) => function (req, res, next) {
107 return __awaiter(this, void 0, void 0, function* () {
108 const path = require('path');
109 console.log("THIS IS THE AUTH CALLBACK");
110 // we use this middleware also as endpoint for email confirmation, then the token-parameter must be specified
111 const email_confirmation = req.query[exports.EMAIL_CONFIRMATION_PARAM];
112 const email_param = req.query[exports.EMAIL_PARAM];
113 const password_param = req.query[exports.PASSWORD_PARAM];
114 const page = req.query["page"];
115 console.log("received params: ", email_confirmation, email_param, password_param);
116 if (email_param) {
117 // get the entry of the database
118 const authDataList = yield getAuthData(req, // request: any
119 false, //matchBrowserIdentity -- we do not want to match the browser identity, the user might use another browser to confirm he mail address
120 exports.IC_USER_ID, // key: string
121 email_param //val: any,
122 );
123 console.log("retrieved auth-data-list: ", authDataList);
124 // check whether the user already exists
125 const parsedAuthDataList = authDataList.map(raw => JSON.parse(raw.jsonData));
126 // the user logs in with her email and password
127 if (password_param !== undefined && parsedAuthDataList.length > 0) {
128 const authData = parsedAuthDataList
129 .reduce((result, cur) => result !== undefined ? result : (
130 // check whether the password is correct
131 cur.encrypted_password === password_param ? cur : undefined), undefined);
132 if (authData !== undefined) {
133 // create a new webtoken, i.e. other browser will be logged out!
134 const { webtoken, encryptedAccessToken } = getEncryptedAccessToken(email_param, clientSecret, password_param);
135 // put the encrypted web token into the database, this is user (browser)-specific data!
136 const storeResult = yield storeAuthData(req, // request: any
137 exports.IC_USER_ID, // key: string
138 email_param, //val: any,
139 Object.assign({}, authData, {
140 encryptedAccessToken: encryptedAccessToken
141 }));
142 req.universalCookies.set(exports.IC_WEB_TOKEN, webtoken, { path: '/' });
143 req.universalCookies.set(exports.IC_USER_ID, email_param, { path: '/' });
144 console.log("store password verified result: ", storeResult);
145 res.redirect(`${path.join(iso_libs_1.getBasename(), page !== undefined ? page : "/")}?message=success`);
146 }
147 else {
148 console.log("could not verify password, ", password_param, email_param);
149 return next("login failure");
150 }
151 return;
152 }
153 else if (email_confirmation && parsedAuthDataList.length > 0) {
154 // the user clicks the link from within the confirmation email
155 const authData = parsedAuthDataList
156 .reduce((result, cur) => result !== undefined ? result : (cur.encryptedAccessToken === email_confirmation ? cur : undefined), undefined);
157 console.log("retrieved auth-data: ", authData);
158 if (authData !== undefined) {
159 const { webtoken, encryptedAccessToken } = getEncryptedAccessToken(email_param, clientSecret, email_confirmation);
160 // put the encrypted web token into the database, this is user (browser)-specific data!
161 const storeResult = yield storeAuthData(req, // request: any
162 exports.IC_USER_ID, // key: string
163 email_param, //val: any,
164 Object.assign({}, authData, {
165 status: exports.AUTH_STATUS.ACTIVE,
166 encryptedAccessToken: encryptedAccessToken
167 }));
168 console.log("webtoken: ", webtoken, email_param);
169 req.universalCookies.set(exports.IC_WEB_TOKEN, webtoken, { path: '/' });
170 req.universalCookies.set(exports.IC_USER_ID, email_param, { path: '/' });
171 console.log("store email verified result: ", storeResult);
172 res.redirect(`${path.join(iso_libs_1.getBasename(), page !== undefined ? page : "/")}?message=mailverified`);
173 }
174 else {
175 console.log("could not verify access token, ", email_confirmation, email_param);
176 return next("access token is wrong");
177 }
178 return;
179 }
180 }
181 const { redirectPage, fFetch } = fetchAccessToken(req);
182 // store the redirectPage in the request for further processing
183 console.log("redirect to: ", redirectPage);
184 req["redirectPage"] = redirectPage;
185 yield fFetch().then(function (resJson) {
186 return __awaiter(this, void 0, void 0, function* () {
187 //const { token_type, access_token /*, refresh_token, scope, expires_at */} = resJson;
188 // try the freshly acquired token and get the user's Medium.com id
189 yield getUserData(resJson).then(function (data) {
190 return __awaiter(this, void 0, void 0, function* () {
191 console.log("get user data: ", JSON.stringify(data));
192 const { id, name, username, imageUrl, access_token, email, status } = data;
193 console.log("id: ", id);
194 console.log("name: ", name);
195 const { webtoken, encryptedAccessToken } = getEncryptedAccessToken(id, clientSecret, access_token);
196 //console.log("encryptedAccessToken: ", encryptedAccessToken);
197 // TODO id may be undefined when the token expired!
198 //console.log("storeAuthData: ", storeAuthData)
199 // put the encrypted web token into the database, this is user (browser)-specific data!
200 const storeResult = yield storeAuthData(req, // request: any
201 exports.IC_USER_ID, // key: string
202 id, //val: any,
203 Object.assign({
204 /** We only store the encrypted token when we have an active status, i.e. a auth-provider
205 * we keep it in clear-text for e-mail */
206 encryptedAccessToken: status === exports.AUTH_STATUS.ACTIVE ? encryptedAccessToken : access_token,
207 name: name,
208 username: username,
209 imageUrl: imageUrl,
210 email: email,
211 status: status,
212 }, password_param ? {
213 encrypted_password: password_param
214 } : {}) //jsonData: any
215 );
216 console.log("storeResult: ", storeResult);
217 // give the webtoken to back to the user - if the account is valid, only!
218 if (status === exports.AUTH_STATUS.ACTIVE) {
219 req.universalCookies.set(exports.IC_WEB_TOKEN, webtoken, { path: '/' });
220 req.universalCookies.set(exports.IC_USER_ID, id, { path: '/' });
221 }
222 console.log("done"); //'http://' +path.join(req.headers.host + +
223 res.redirect(path.join(iso_libs_1.getBasename(), redirectPage));
224 return;
225 });
226 });
227 });
228 });
229 });
230};
231//# sourceMappingURL=auth-middleware.js.map
\No newline at end of file