如何在电子应用程序中使用外部浏览器(chrome,firefox等)实现auth0身份验证

时间:2019-06-18 05:58:26

标签: electron auth0

我正在电子应用程序中使用auth0身份验证。我无论如何都不使用电子应用程序的浏览器窗口。因此,我想在外部浏览器(chrome,Firefox等)或已安装的默认浏览器中打开auth0身份验证窗口。有什么办法吗?

2 个答案:

答案 0 :(得分:2)

您可以在此处使用一些电子功能。首先,您需要使用this Electron api

在外部浏览器中打开Auth0身份验证窗口

然后,您使用默认浏览器登录,并且必须在重定向URL中设置一个自定义协议,该协议使用this Electron api指向您的react应用,现在您的Electron应用中已经有令牌。

答案 1 :(得分:0)

方法1(无效)

我尝试了@Donflopez's answer中描述的方法,但到那里却只有一半。

问题是我正在为我的应用程序使用Google Auth,并且它在重定向URL中不支持自定义协议。 (Invalid parameter value for redirect_uri: Invalid scheme: my-custom-protocol://auth/handler


方法2(无效)

然后我的想法是使用这样的重定向URI(使用NodeJS express服务器接收数据):http://localhost:XXXX/auth/handler

登录标签的打开代码:

// not sure if this is the correct url to try, but it's what was opened when firebase.auth().signInWithRedirect() was called
require("electron").shell.openExternal(
    `https://${projectID}.firebaseapp.com/__/auth/handler`
    + `apiKey=${apiKey}&appName=%5BDEFAULT%5D&authType=signInViaRedirect`
    + `&providerId=google.com&scopes=profile&v=7.7.0`
    + `&redirectUrl=http%3A%2F%2Flocalhost%3A${oauthPort}%2Fauth%2Fhandler`);

OAuth数据接收器(NodeJS Express服务器):

const express = require("express");
const oauthPort = XXXX;
const redirectServer = express();
redirectServer.get("/auth/handler", (req, res)=>{
    debugger;
    // I inspected the request data with debugger, but found nothing useful!
});

但是,我无法正常工作。虽然我可以设置一个NodeJS Express服务器来尝试接收数据,但是当实际请求进入时(对于localhost:XXXX/auth/handler,在浏览器选项卡重定向到它之后),我在请求中没有看到任何内容网址或看起来像凭证数据的标头。

如果有人弄清楚如何将请求数据提取/转换为凭证数据,请告诉我! (同时,我在下面使用稍微复杂一些的方法。)


方法3(有效)

我的下一个想法(可行)是让我的Electron应用程序在外部浏览器中打开一个选项卡,指向与Electron应用程序相同的域,但是指向一个特殊的页面,该页面随后将启动Google登录弹出窗口用于网站:

require("electron").shell.openExternal(`${electronApp_domain}/sign-in-helper`);

(如果您的电子页面是在本地提供的,例如[file:///],则可能需要添加本地快递服务器来为“外部浏览器”选项卡提供登录帮助页面-不确定是否Google登录可以在file:///页上进行。)

然后该页面检索Google id令牌和访问令牌,如下所示:

const firebase = require("firebase");
firebase.auth().signInWithPopup(provider).then(result=>{
    const idToken = result.credential.idToken;
    const accessToken = result.credential.accessToken;

    // You now have the id-token and access-token.
    // Either one is sufficient to sign-in your Electron app, just need to transfer it.
    TransferTokensToElectronApp(idToken, accessToken);
});

为了将其转移到Electron,我这样做:

const desktopApp_receiveTokensPort = XXXX;
function TransferTokensToElectronApp(idToken, accessToken) {
    const script = document.createElement("script");
    script.src = `http://localhost:${desktopApp_receiveTokensPort}/receive-tokens/?idToken=${idToken}&accessToken=${accessToken}`;
    document.head.appendChild(script);

    // after 1s, remove the script element, and close the page
    setTimeout(()=>{
        script.remove();
        window.close();
    }, 1000);
}

以及电子应用程序中的接收代码:

const express = require("express");
const receiveTokensServer = express();
const receiveTokensPort = XXXX;
const receiveTokensServer_domainStr = `http://localhost:${receiveWebSignInPort}`;

receiveWebSignInServer.get("/receive-tokens", (req, res)=>{
    const url = new URL(`${receiveTokensServer_domainStr}/${req.url}`);
    const idToken = url.searchParams.get("idToken");
    const accessToken = url.searchParams.get("accessToken");

    console.log("Received sign-in data from web helper:", {idToken, accessToken});
    SendTokensToRenderer(idToken, accessToken);
});
receiveWebSignInServer.listen(receiveWebSignInPort);

SendTokensToFrontEnd函数的实现取决于您在应用程序中执行通信的方式,但以使用Electron的ipc system为例:

function SendTokensToRenderer(idToken, accessToken) {
    browserWindow.webContents.send("token-transfer-channel", {idToken, accessToken});
}

最后,要在渲染器/前端中接收这些令牌,并使用其登录Firebase:

const {ipcRenderer} = require("electron");
const firebase = require("firebase");

ipcRenderer.on("token-transfer-channel", (event, data)=>{
    const cred = firebase.auth.GoogleAuthProvider.credential(data.idToken, data.accessToken);
    firebase.auth().signInWithCredential(cred);
});

编辑:此后不久便找到了一个教程,该教程使用了类似的方法(但它是通过共享数据库而不是localhost“脚本”请求将凭据转移到前端的) :https://pragli.com/blog/how-to-authenticate-with-google-in-electron