Build a Desktop App with Electron and Authentication
Tags: electron, javascript, desktop, security
Published: 2018-09-24 18:43:34

---
title: Build a Desktop App with Electron and Authentication
published: true
tags: electron, javascript, desktop, security
canonical_url: https://developer.okta.com/blog/2018/09/17/desktop-app-electron-authentication
description: Learn how to build a desktop application using Electron and React, then add authentication with OIDC.
---

Electron is a framework for building cross-platform desktop applications with web technologies like JavaScript, HTML, and CSS. It was created for GitHub’s Atom editor and has achieved widespread adoption since. Electron powers several apps that I use on a daily basis: Slack, Kitematic, and Visual Studio Code to name a few.

Electron 2.0 was released in early May 2018, along with changes to the project to adhere to strict semantic versioning. This is good news for developers because it means patch releases will be more stable and new features will come in major versions only. When open source projects use semantic versioning correctly, end users don’t see breaking changes as often and tend to be productive.

Developing desktop applications with web technologies is an appealing notion. [This tweet](https://twitter.com/gerardsans/status/1026040566868529152) from Gerard Sans nails it in my opinion:

{% twitter 1026040566868529152 %}

Do you have web development skills? Great! You have what’s needed to build a desktop app with Electron!

In this article, I’ll show you how to create an [Electron](https://electronjs.org/) app with TypeScript, AppAuth-JS, and OpenID Connect (OIDC). You’ll learn how to add authentication and secure an Electron app for your users.

## What is AppAuth?

[AppAuth](https://appauth.io/) is a project that aims to create client SDKs for native apps. It makes it possible to implement authentication and authorization in your apps using OIDC and OAuth 2.0. It has SDKs available for iOS, macOS, Android, and JavaScript environments. AppAuth-JS is the SDK for JavaScript clients. AppAuth also supports the [PKCE extension](https://developer.okta.com/authentication-guide/auth-overview/#authorization-code-with-pkce-flow) to OAuth to make public clients more secure.

Pretty much every application depends upon a secure identity management system. For most developers who are building Electron apps, there’s a decision to be made between rolling your own authentication/authorization or plugging in a hosted identity service like Okta.

## Build Desktop Apps with Electron

I’m going to make things easy for you. Rather than building an app from scratch, you’ll use a pre-existing example. The AppAuth-JS project has two examples, a server-side example with Node at [src/node\_app/index.ts](https://github.com/openid/AppAuth-JS/blob/master/src/node_app/index.ts), and an [appauth-js-electron-sample](https://github.com/googlesamples/appauth-js-electron-sample). Clone the Electron example to begin.

```
git clone https://github.com/googlesamples/appauth-js-electron-sample.git okta-electron-example

```

Open the `package.json` in this project and make the following changes.

```
@@ -6,8 +6,8 @@
   "scripts": {
     "compile": "tsc",
     "watch": "tsc --watch",
- "start": "npm run-script compile && node_modules/.bin/electron .",
- "dev": "npm run-script watch & node_modules/.bin/electron ."
+ "start": "npm run compile && npx electron .",
+ "dev": "npm run compile && npm run watch & npx electron ."
   },
   "files": [
     "built/**"
@@ -22,10 +22,10 @@
     "@openid/appauth": "^0.3.5",
     "@types/react": "^16.3.17",
     "@types/react-dom": "^16.0.6",
- "electron": "^2.0.2",
     "material-design-lite": "^1.3.0"
   },
   "devDependencies": {
- "typescript": "^2.9.1"
+ "electron": "^2.0.2",
+ "typescript": "^3.0.3"
   }

```

These changes are not necessary, but they will make things easier. The changes in “scripts” make it so compilation will happen before you run `npm run dev` or `npm start`. You’re also moving the `electron` dependencies to be a `devDependency` and upgrading TypeScript to the latest version.

Navigate to the cloned directory, install dependencies with npm, and run the app.

```
cd okta-electron-example
npm i
npm run dev

```

It should start the app and show a Sign-In link.

![blog/electron-react-appauth-js/initial-load.png](https://developer.okta.com/assets/blog/electron-react-appauth-js/initial-load-d99e8f89254c517247bfd3334efbb227db0e524e09fc2321633ea2c7f6ce0bdd.png)

If you have a Google account, click **Sign-In** , log in, and you’ll be redirected back to your app. You should see your avatar and name displayed.

![After Google Sign-In](https://developer.okta.com/assets/blog/electron-react-appauth-js/after-sign-in-a7901afbf031985eb93357560c05e522989b34db6fb93b144b680ca4be65b9d5.png)

The diagram below shows how this authorization flow happens using OpenID Connect.

![OIDC Flow](https://developer.okta.com/assets/blog/electron-react-appauth-js/oidc-flow-9bf5b49890556ed301b657901ce0ed7329749d0bb4f0d56297c65c54befe12e1.png)

At this point, you can see that authentication with Google is working. In the next sections, I’ll show you how to add PKCE support to make this app more secure, and how to use Okta instead of Google.

## Why Use Okta for Authentication?

You might be asking: why should I use Okta when authentication with Google works? The reason is simple; if you want to manage the users of your app (and you inevitably will), Okta makes it possible. With Google, anyone with a Google Account can log in, but you have no way of revoking access or updating user’s permissions because you can’t manage your users through Google. Okta lets you manage your users, as well as modify their attributes and permissions. Better yet, you can still use Google as a social login mechanism with Okta!

## Use Okta for Authentication with OIDC in Your Desktop App

Okta’s goal is to make identity management a lot easier, more secure, and more scalable than what you’re used to. Okta is a cloud service that allows developers to create, edit, and securely store user accounts and user account data, and connect them with one or multiple applications. Our API enables you to:

- [Authenticate](https://developer.okta.com/product/authentication/) and [authorize](https://developer.okta.com/product/authorization/) your users
- Store data about your users
- Perform password-based and [social login](https://developer.okta.com/authentication-guide/social-login/)
- Secure your application with [multi-factor authentication](https://developer.okta.com/use_cases/mfa/)
- And much more! Check out our [product documentation](https://developer.okta.com/documentation/)

Are you ready? [Register for a forever-free developer account](https://developer.okta.com/signup/) today! When you’re finished, complete the steps below to create a Native OIDC app.

1. Log in to your developer account on [developer.okta.com](https://developer.okta.com).
2. Navigate to **Applications** and click on **Add Application**.
3. Select **Native** and click **Next**.
4. Give the application a name (e.g., `My Electron App`) and add `http://localhost:8000` as a Login redirect URI.
5. For Grant type allowed, select **Refresh Token** in addition to **Authorization Code**.
6. Click **Done**.

Now you can use your app settings to change from using Google to Okta. Modify `flow.ts` to use your Okta app’s settings.

```
const openIdConnectUrl = 'https://{yourOktaDomain}/oauth2/default';
const clientId = '{yourClientId}';
const redirectUri = 'http://localhost:8000';

```

You’ll also want to update `app.ts` to use your app’s `/userinfo` endpoint.

```
let request =
    new Request('https://{yourOktaDomain}/oauth2/default/v1/userinfo', {
      headers: new Headers({'Authorization': `Bearer ${accessToken}`}),
      method: 'GET',
      cache: 'no-cache'
    });

```

If you restart your app and try to log in, it will fail because you’re not using PKCE. You’ll see an error like the following in your launched browser’s address bar.

```
error=invalid_request&error_description=PKCE+code+challenge+is+required+when+the+token+endpoint+authentication+method+is+%27NONE%27.

```

## Add PKCE Support to Your Desktop App

PKCE (pronounced “pixy”) is a security extension for OAuth 2.0 for public clients on mobile (and desktop) clients. It’s designed to prevent interception of the authorization code by a malicious application that runs on the same device.

```
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +
    | End Device (e.g., Smartphone) |
    | |
    | +-------------+ +----------+ | (6) Access Token +----------+
    | |Legitimate | | Malicious|<--------------------| |
    | |OAuth 2.0 App| | App |-------------------->| |
    | +-------------+ +----------+ | (5) Authorization | |
    | | ^ ^ | Grant | |
    | | \ | | | |
    | | \ (4) | | | |
    | (1) | \ Authz| | | |
    | Authz| \ Code | | | Authz |
    | Request| \ | | | Server |
    | | \ | | | |
    | | \ | | | |
    | v \ | | | |
    | +----------------------------+ | | |
    | | | | (3) Authz Code | |
    | | Operating System/ |<--------------------| |
    | | Browser |-------------------->| |
    | | | | (2) Authz Request | |
    | +----------------------------+ | +----------+
    + ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ +

```

The PKCE working group provides an excellent explanation I’ve included below. You can read more in [the official RFC](https://tools.ietf.org/html/rfc7636).

“To mitigate this attack, PKCE uses a dynamically created cryptographically random key called a “code verifier”. A unique code verifier is created for every authorization request, and its transformed value, called “code challenge”, is sent to the authorization server to obtain the authorization code. The authorization code obtained is then sent to the token endpoint with the “code verifier”, and the server compares it with the previously received request code so that it can perform the proof of possession of the “code verifier” by the client. This works as the mitigation since the attacker would not know this one-time key since it is sent over TLS and cannot be intercepted.”

The diagram below shows how PKCE works with your app and Okta.

![Auth Code Flow with PKCE](https://developer.okta.com/assets/oauth_auth_code_flow_pkce-7ebc49bec20d71423cb6b9e85139164988a2d021a37277733d04ffac8eaabaad.png)

<!-- copied from _source/_authentication-guide/auth-overview/index.md -->

Now you’ll add PKCE to your Electron app! In `flow.ts`, add a `challengePair` variable for PKCE as a member variable of the `AuthFlow` class.

```
private challengePair: { verifier: string, challenge: string };

```

Add a line at the end of the constructor to initialize this variable.

```
this.challengePair = AuthService.getPKCEChallengePair();

```

Create `pkce.ts` to define the `AuthService` class.

```
const crypto = require('crypto');

export class AuthService {

  static getPKCEChallengePair() {
    let verifier = AuthService.base64URLEncode(crypto.randomBytes(32));
    let challenge = AuthService.base64URLEncode(AuthService.sha256(verifier));
    return {verifier, challenge};
  }

  static base64URLEncode(str: Buffer) {
    return str.toString('base64')
      .replace(/\+/g, '-')
      .replace(/\//g, '_')
      .replace(/=/g, '');
  }

  static sha256(buffer: string) : Buffer {
    return crypto.createHash('sha256').update(buffer).digest();
  }
}

```

Add an import for this class to `flow.ts`:

```
import { AuthService } from './pkce';

```

In the `makeAuthorizationRequest()` method, right after the `if (username) {}` logic, add the code challenge and method to the `extras` map.

```
// PKCE
extras['code_challenge'] = this.challengePair.challenge;
extras['code_challenge_method'] = 'S256';

```

In `makeRequestTokenRequest()`, add a `tokenRequestExtras` variable and send it in the request.

```
let tokenRequestExtras = { code_verifier: this.challengePair.verifier };

// use the code to make the token request.
let request = new TokenRequest(
  clientId,
  redirectUri,
  GRANT_TYPE_AUTHORIZATION_CODE,
  code,
  undefined,
  tokenRequestExtras
);

```

After making these changes, you should be able to log in. However, when you click on **USER INFO** , you won’t see your user’s name or avatar. Open Chrome Developer Tools with **View** > **Toggle Developer Tools** to see why.

![Electron's Developer Tools](https://developer.okta.com/assets/blog/electron-react-appauth-js/developer-tools-8419cf2ad4dd16e3c56867827289696f287ce6abc419df4594e41f56aa3a119a.png)

To fix this issue, change the `scope` variable in `flow.ts` to include `profile`. While you’re at it, add `offline_access` so your app can work without an internet connection.

```
const scope = 'openid profile offline_access';

```

Refresh your app (Command+R on Mac, Ctrl+R on Windows/Linux), and now you should see the name when clicking on **USER INFO**.

![Name from User Info Endpoint](https://developer.okta.com/assets/blog/electron-react-appauth-js/userinfo-name-f4bce0776429b416c7cb27ba3a5921c4a76e824bebf97a6e01b2d0d11f787a9c.png)

**NOTE:** I leveraged [these PKCE code samples](https://github.com/openid/AppAuth-JS/issues/28) to make all this work.

### Add an Avatar in Okta

You might notice that the user info endpoint doesn’t return an avatar. The code in `app.ts` sets the avatar based on a `picture` attribute.

```
private updateUi() {
  this.handleSignIn.textContent = SIGN_OUT;
  this.fetchUserInfo.style.display = '';
  if (this.userInfo) {
    this.userProfileImage.src = `${this.userInfo.picture}?sz=96`;
    this.userName.textContent = this.userInfo.name;
    this.showSnackBar(
        {message: `Welcome ${this.userInfo.name}`, timeout: 4000});
    this.userCard.style.display = '';
  }
}

```

_You can delete `?sz=96` in the above code since this example doesn’t use it._

To add a `picture` attribute to your user, log in to your Okta dashboard and navigate to **Users** > **Profile Editor**. Click on the first “user” and add a `picture` attribute. Click **Save**.

![Add Picture Attribute](https://developer.okta.com/assets/blog/electron-react-appauth-js/add-attribute-92ce1f547cffc692375ce3bf5bc3edbfadd466e0091f67cc921685615ce4ea0d.png)

Navigate back to the Profile Editor and click on **Mappings** for your Electron App. Create a mapping from `user.picture` to `picture` and select to apply the mapping on user create and update. Click **Save Mapping** and **Apply Updates Now**.

![Add Picture Mapping](https://developer.okta.com/assets/blog/electron-react-appauth-js/add-mapping-befaa43f84f9c2119a2b6b4ee283984cd78d63240a86eb90058949ce46f8c668.png)

Now go to **Users** > **People** , select a user, navigate to the **Profile** tab, and click **Edit**. Add a value for `picture` at the bottom. For example, you can use the URL of our Okta Developer logo.

```
https://www.okta.com/sites/all/themes/Okta/images/logos/developer/Dev_Logo-02_Large.png

```

Now if you click on the user info link, you should see an avatar associated with your user.

![User Info Picture](https://developer.okta.com/assets/blog/electron-react-appauth-js/userinfo-picture-772fce4b2aec8366fb32036c35741c379b6f3b4b7ccfa78d3b6fbf5759ff7871.png)

**TIP:** If you’re going to use this in production, I’d recommend you use a smaller image for the picture (e.g., with 150x150 dimensions). You can also [base64 encode](https://www.base64-image.de/) an image and use its value for your picture.

Below is a screenshot that shows this app with some additional polish and a base64 image value for my account.

![Polished Matt](https://developer.okta.com/assets/blog/electron-react-appauth-js/polished-matt-9566a63af2e502bff578c4811c8b8470037d45fb3ded934264365ec495a5d7df.png)

## Package Your Desktop App For Production

To package this app for production distribution, you can use [electron-builder](https://www.electron.build/). Install electron-builder with npm.

```
npm i -D electron-builder@20.28.4

```

Add a `build` section to your `package.json`:

```
"build": {
  "appId": "com.okta.developer.electron",
  "productName": "Electron Awesomeness",
  "mac": {
    "category": "public.app-category.developer-tools"
  }
}

```

Then add `pack`, `dist`, and `postinstall` scripts.

```
"scripts": {
  ...
  "pack": "npm run compile && electron-builder --dir",
  "dist": "npm run compile && electron-builder",
  "postinstall": "electron-builder install-app-deps"
}

```

To package your app for production, use the following commands:

- `npm run pack` will generate the package directory without really packaging it. This is useful for testing purposes.
- `npm run dist` will package in a distributable format (e.g., dmg, Windows installer, deb package).

**NOTE:** If the app doesn’t start after packaging, it’s likely because you don’t have [code signing](https://www.electron.build/code-signing) configured. To disable Code Signing when building for macOS, run `export CSC_IDENTITY_AUTO_DISCOVERY=false`. If you have an Apple Developer Account, open Xcode, go to **Preferences** > **Accounts** and make sure you’re logged in, and your development certificates are downloaded.

## Electron Example App Source Code

You can find the source code for this article at [https://github.com/oktadeveloper/okta-appauth-js-electron-example](https://github.com/oktadeveloper/okta-appauth-js-electron-example).

I made some minor adjustments (e.g., optimizing imports, changing double quotes to single quotes), but nothing major. To see a diff between this project and the original, [click here](https://github.com/googlesamples/appauth-js-electron-sample/compare/master...oktadeveloper:master).

## Learn More About Electron, AppAuth, and OIDC

There you have it! Go forth and use your web skills to create amazing desktop applications!

To learn more about Electron, AppAuth, React, and OIDC, check out the following resources:

- [Writing Your First Electron App](https://electronjs.org/docs/tutorial/first-app)
- [Build a Basic CRUD App in Android with Kotlin](/blog/2018/09/11/android-kotlin-crud)
- [Build a Health Tracking App with React, GraphQL, and User Authentication](/blog/2018/07/11/build-react-graphql-api-user-authentication)
- [Identity, Claims, & Tokens – An OpenID Connect Primer, Part 1 of 3](/blog/2017/07/25/oidc-primer-part-1)

Got questions? Please leave a comment below, [ping me on Twitter](https://twitter.com/mraible), or ask it on our [Developer Forums](https://devforum.okta.com/).

Like what you learned here? Follow [@oktadev](https://twitter.com/oktadev), like us [on Facebook](https://www.facebook.com/oktadevelopers/), follow us [on LinkedIn](https://www.linkedin.com/company/oktadev/), or [watch our videos on YouTube](https://www.youtube.com/channel/UC5AMiWqFVFxF1q9Ya1FuZ_Q).