"use strict";
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 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 = {
    GITHUB: "AUTH_GITHUB",
    // TODO Medium Auth only partly implemented!!!
    MEDIUM: "AUTH_MEDIUM"
};
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) => (err, req, res, next) => {
    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) => (req) => {
    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"
                            }
                        });
                    });
                }
            };
        }
        ;
    }
    else if (provider === exports.AuthenticationProvider.MEDIUM) {
        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"
                    }
                })
            };
        }
    }
};
exports.createGetUserFunction = (provider) => function (resJson) {
    return __awaiter(this, void 0, void 0, function* () {
        console.log("resJson: ", resJson);
        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
                };
            });
        }
        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
                };
            });
        }
    });
};
/**
 * 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: undefined,
    };
    // set our storeData-Property
    const authenticationProps = {
        setStoreIdentityData: (storeIdentityData) => {
            //console.log("setStoreIdentityData: ", storeIdentityData);
            props.storeAuthData = storeIdentityData;
            //console.log("auth-props: ", props)
        }
    };
    /**
     * 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) })
        ].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) })
        ].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;
    });
    /**
     * 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), //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
                        ) }),
                    // 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