Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

how implement auth0 authentication using external browser(chrome,firefox etc.) in electron application

Tags:

electron

auth0

I am using using auth0 authentication in my electron app. I am not using browser window of electron app in any case. So, I want want to open my auth0 authentication window in external browser(chrome,Firefox etc.) or installed default browser. Is there any way?

like image 862
Tulshi Das Avatar asked Jun 18 '19 05:06

Tulshi Das


2 Answers

You can use a few electron features here. First you need to open the Auth0 auth windows in the external browser using this Electron api

Then you login using your default browser and you must set in your redirection URL a custom protocol that points to your react app using this Electron api, now you have the token in your Electron app.

like image 147
Donflopez Avatar answered Oct 10 '22 03:10

Donflopez


Approach 1 (didn't work)

I tried the approach described in @Donflopez's answer, and it got me half the way there.

The problem is that I'm using Google Auth for my app, and it doesn't support custom-protocols in the redirect url. (Invalid parameter value for redirect_uri: Invalid scheme: my-custom-protocol://auth/handler)


Approach 2 (didn't work)

My idea was then to use a redirect URI like this (with a NodeJS express server to receive the data): http://localhost:XXXX/auth/handler

Sign-in-tab opening code:

// 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 data receiver (NodeJS express server):

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!
});

However, I wasn't able to get that working. While I was able to set up a NodeJS express server to try to receive the data, when the actual request came in (for localhost:XXXX/auth/handler, after the browser tab redirected to it), I didn't see anything in the request url or headers that looked like credential data.

If someone figures out how to extract/convert the request data into credential data, let me know! (In the meantime, I use the slightly more complex approach below.)


Approach 3 (worked)

My next idea (which works) was to just have my Electron app open a tab in an external browser, pointed to the same domain as in the Electron app, but to a special page that then launches the Google sign-in popup for the website:

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

(If your Electron page is served locally [eg. file:///], you may need to add a local express server to serve the sign-in-helper page for the external-browser tab -- not sure if Google sign-in works on file:/// pages.)

That page then retrieves the Google id-token and access-token like so:

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);
});

For transferring it to Electron, I did this:

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);
}

And the receiving code in the Electron app:

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

receiveTokensServer.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);
});
receiveTokensServer.listen(receiveTokensPort);

Implementation of the SendTokensToFrontEnd function depends on how you perform communication in your app, but an example using Electron's ipc system:

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

And then finally, to receive those tokens in your renderer/frontend, and use it to sign-in to 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);
});

EDIT: Found a tutorial soon after, that uses a similar approach (except it transfers the credentials to the frontend through a shared database, rather than a localhost "script" request): https://pragli.com/blog/how-to-authenticate-with-google-in-electron

like image 30
Venryx Avatar answered Oct 10 '22 04:10

Venryx