OAuth flow in Electron Apps

The purpose of OAuth is to obtain a token that allows your app to make requests to a third-party service on behalf of a user. This let’s you do things like access their GitHub repositories for code analysis, access their Slack messages to create a bot, or access their Google Sheets to import them into your tools.

Typical OAuth flow

When obtaining an OAuth token, the flow is typically something like this:

Typical OAuth flow for webapps.
Typical OAuth flow for webapps.
  1. The user starts the OAuth flow. The user enters your app, and you request permission to access their data stored in a third-party service. If they accept, the OAuth flow begins.

  2. Your app opens the third-party login page. Your app either in this browser tab or a separate one, opens the login URL for the third-party. In the URL that you open, you typically also add a redirect URL parameter that the third-party service will use later on.

  3. The user enters their credentials. The user puts in their username and password in this screen to authenticate with the third-party service, indicating that they are willing to allow your app to access their data.

  4. The login page passes the credentials onto the third-party service. This allows the third-party service to verify the user’s credentials.

  5. The third-party service provides an authorization code. This code represents that the user has authenticated with the service.

  6. The login page sends the authorization code to the redirect URL. The redirect URL from step 1 receives the authorization code. Typically, this URL points to an endpoint on your web server. Along with the URL, the login page will add the code as a URL parameter, something like: http://example.com/api/oauthcallback?code=xxxx.

  7. Your web server requests an OAuth token from the third-party service. Using the authorization code, the web server is able to obtain an OAuth token (as well as a refresh token in some cases). The token allows your app to make requests to the third-party service on behalf of the user.

  8. The third-party service responds with a token.

  9. The web server redirects back to the web app. After storing the OAuth token for future use, your web server should typically send a (302) redirect to have the browser display your web app to the user, indicating that the OAuth flow has successfully completed.

This flow is already complicated when you have a web server and web app. But, how do you do it in an Electron app without a web server? What kind of redirect URL do you use? It’s simple. You can use any URL, because you can intercept any URL from a browser window opened by Electron.

OAuth flow in Electron apps

First, you can show the login screen to the user by opening a new BrowserWindow. When you provide the redirect URL to the third-party service, you can really choose anything you like. I usually use something like http://127.0.0.1:9999/oauthcallback. There’s usually nothing running on that URL (even if there was, this isn’t a problem, I’ll explain why).

const authWindow = new BrowserWindow({
    webPreferences: {
        nodeIntegration: false,
    }
});
const url = new URL(THIRD_PARTY_LOGIN_URL);
url.searchParams.set(
    "redirect_uri",
    "http://127.0.0.1:9999/oauthcallback/");
authWindow.loadURL(url.toString());

When the third-party login screen tries to redirect to the URL we provided, we simply intercept the request before the BrowserWindow can send the request.

const {
    session: { webRequest },
} = authWindow.webContents;
const filter = { urls: ["http://127.0.0.1:9999/oauthcallback/*"] };
webRequest.onBeforeRequest(filter, async ({ url }) => {
    const parsedUrl = new URL(url);
    authWindow.close();
    const code = parsedUrl.searchParams.get("code");
    // Do the rest of the authorization flow with the code.
});

After receiving the code, the rest of the steps happen as usual. With this method, you can fetch an OAuth token without a server inside of your Electron app.