DEV Community

an-object-is-a
an-object-is-a

Posted on • Updated on

Discord OAuth2 Login for Chrome Extensions

Use Discord OAuth2 login for Chrome Extensions and avoid building your own.


This tutorial assumes you know how Google Chrome Extensions work. Learn more here.


Discord Login for Chrome Extensions

Let's Begin.

Before we even touch a line of code, we need to setup our development work space so that we have the ability to use Discord's OAuth2 Endpoint.

Navigate to https://discord.com/developers/applications/ and login.

Click 'New Application' in the top right.
Name it whatever you want.
Keep this window open, we'll need that 'CLIENT ID' a little later.

Navigate to chrome://extensions and make sure your Chrome Extension is loaded.
Copy the 'ID' of your extension and head back to the Discord Developer Portal.

Click the 'OAuth2' link on the left sidebar.
Click on 'Add Redirect' and add the url, https://.chromiumapp.org/ where is the extension id you copied earlier.

Maker sure to click Save Changes.

Discord Developer console

We can now use Discord OAuth2 Login for Chrome Extensions.


Let's do some web development work before we get to actual Chrome Extension work.

We'll create two pages: A 'Sign In' page and a 'Sign Out' page. Nothing fancy.

/* popup-sign-in.html */
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <style>
            body {
                width: 300px;
                height: 600px;
                margin: 0;
                padding: 0;
                overflow: hidden;
            }

            div {
                align-items: center;
                display: flex;
                width: 100%;
                height: 100%;
                justify-content: center;
                text-align: center;
                margin: auto;
                box-sizing: border-box;
                background-color: #fcee54;
            }

            button {
                font-size: 200%;
                background-color: #f5c2e0;
                border-radius: 5px;
                border: none;
                text-align: center;
                color: black;
                font-family: monospace;
                font-weight: bold;
                transition-duration: 0.3s;
                padding: 10px;
            }
        </style>
    </head>
    <body>
        <div>
            <button type="submit">Sign In</button>
        </div>
        <script src="./popup-sign-in-script.js"></script>
    </body>
</html>
Enter fullscreen mode Exit fullscreen mode
/* popup-sign-out.html */
<!DOCTYPE html>
<html lang="en">
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <title>Document</title>
        <style>
            body {
                width: 300px;
                height: 600px;
                margin: 0;
                padding: 0;
                overflow: hidden;
            }

            div {
                align-items: center;
                display: flex;
                width: 100%;
                height: 100%;
                justify-content: center;
                text-align: center;
                background-color: #00ffa7;
                transition-duration: 0.5s;
            }

            button {
                font-size: 200%;
                background-color: #f5c2e0;
                border-radius: 5px;
                border: none;
                text-align: center;
                color: black;
                font-family: monospace;
                font-weight: bold;
                transition-duration: 0.3s;
                padding: 10px;
            }
        </style>
    </head>
    <body>
        <div>
            <button type="submit">Sign Out</button>
        </div>
        <script src="./popup-sign-out-script.js"></script>
    </body>
</html>
Enter fullscreen mode Exit fullscreen mode

Note:
Make sure you include the body CSS properties.
The other CSS you don't really need. It's just used to make the page look good.

Notice that we've attached scripts to each of our HTML pages…

/* popup-sign-in-script.js */
const button = document.querySelector('button');

button.addEventListener('mouseover', () => {
    button.style.backgroundColor = 'black';
    button.style.color = 'white';
    button.style.transform = 'scale(1.3)';
});

button.addEventListener('mouseleave', () => {
    button.style.backgroundColor = '#f5c2e0';
    button.style.color = 'black';
    button.style.transform = 'scale(1)';
});

button.addEventListener('click', () => {
});
Enter fullscreen mode Exit fullscreen mode
/* popup-sign-out-script.js */
const button = document.querySelector('button');

button.addEventListener('mouseover', () => {
    button.style.backgroundColor = 'black';
    button.style.color = 'white';
    button.style.transform = 'scale(1.3)';

    document.querySelector('div').style.backgroundColor = '#ee2f64';
});

button.addEventListener('mouseleave', () => {
    button.style.backgroundColor = '#f5c2e0';
    button.style.color = 'black';
    button.style.transform = 'scale(1)';

    document.querySelector('div').style.backgroundColor = '#fcee54';
});

button.addEventListener('click', () => {
});
Enter fullscreen mode Exit fullscreen mode

Note:
A lot of this code is completely unnecessary. It's just used to make the page look nice and animate.
The only code that matters in the scripts are the 'click' listeners for the buttons.


Now that we have the Web Dev portion out of the way, let's take a look at our 'manifest.json'.

{
    "name": "obj ext",
    "description": "my ext",
    "version": "0.1.0",
    "manifest_version": 2,
    "icons": {
        "16": "./obj-16x16.png",
        "32": "./obj-32x32.png",
        "48": "./obj-48x48.png",
        "128": "./obj-128x128.png"
    },
    "background": {
        "scripts": ["./background.js"]
    },
    "options_page": "./options.html",
    "browser_action": {
        "default_popup": "./popup-sign-in.html"
    },
    "permissions": [
        "identity"
    ] 
}
Enter fullscreen mode Exit fullscreen mode

**Note:

  1. The "default_popup" property of the "browser_action" is set to the "Sign In" page.
  2. We need the 'identity' permission in order to use Chrome’s 'launchWebAuthFlow()' method.

Let's do some actual Chrome Extension programming.

We'll start by coding the basic skeletal logic flow of our app.

In the 'popup-sign-in-script.js', when the user clicks on the button, we'll send a message to the 'background' script asking to "login".

If we get a "success" from the 'background' we'll change the page to the "Sign Out" page.

// popup-sign-in-script.js

const button = document.querySelector('button');

button.addEventListener('mouseover', () => {
    button.style.backgroundColor = 'black';
    button.style.color = 'white';
    button.style.transform = 'scale(1.3)';
});

button.addEventListener('mouseleave', () => {
    button.style.backgroundColor = '#f5c2e0';
    button.style.color = 'black';
    button.style.transform = 'scale(1)';
});

button.addEventListener('click', () => {
    chrome.runtime.sendMessage({ message: 'login' }, function (response) {
        if (response === 'success') window.location.replace("./popup-sign-out.html");
    });
});
Enter fullscreen mode Exit fullscreen mode

The 'popup-sign-out-script.js' is almost identical.

In the 'popup-sign-out-script.js', when the user clicks on the button, we'll send a message to the 'background' script asking to "logout".

If we get a "success" from the 'background' we'll change the page to the
"Sign In" page.

// popup-sign-out-script.js

const button = document.querySelector('button');

button.addEventListener('mouseover', () => {
    button.style.backgroundColor = 'black';
    button.style.color = 'white';
    button.style.transform = 'scale(1.3)';

    document.querySelector('div').style.backgroundColor = '#ee2f64';
});

button.addEventListener('mouseleave', () => {
    button.style.backgroundColor = '#f5c2e0';
    button.style.color = 'black';
    button.style.transform = 'scale(1)';

    document.querySelector('div').style.backgroundColor = '#fcee54';
});

button.addEventListener('click', () => {
    chrome.runtime.sendMessage({ message: 'logout' }, function (response) {
        if (response === 'success') window.location.replace("./popup-sign-in.html");
    });
});
Enter fullscreen mode Exit fullscreen mode

This file is done. You can close it.

Moving to the 'background.js' script, we'll create the Discord OAuth2 Endpoint that's we'll use for 3rd party login credentials.

We're going to need 6 CONSTANTS and 1 VARIABLE.
And while we're at it, a variable to keep track of the user's login status and we'll create a function to bring all of this information together.

// background.js

const DISCORD_URI_ENDPOINT = 'https://discord.com/api/oauth2/authorize';
const CLIENT_ID = encodeURIComponent('');
const RESPONSE_TYPE = encodeURIComponent('token');
const REDIRECT_URI = encodeURIComponent('https://pcojhoejgkedcoikfdehlpfefhagppnf.chromiumapp.org/');
const SCOPE = encodeURIComponent('identify email');
const STATE = encodeURIComponent('meet' + Math.random().toString(36).substring(2, 15));

let user_signed_in = false;

function create_auth_endpoint() {
    let nonce = encodeURIComponent(Math.random().toString(36).substring(2, 15) + Math.random().toString(36).substring(2, 15));

let endpoint_url =
    `${DISCORD_URI_ENDPOINT}
?client_id=${CLIENT_ID}
&redirect_uri=${REDIRECT_URI}
&response_type=${RESPONSE_TYPE}
&scope=${SCOPE}
&nonce=${nonce}`;

    return endpoint_url;
}
Enter fullscreen mode Exit fullscreen mode

Note:

  1. DISCORD_URI_ENDPOINT — how we get to Discord's OAuth2 Endpoint
  2. CLIENT_ID — tells Discord we're allowed to use their OAuth2 Endpoint
  3. RESPONSE_TYPE — asks Discord for a specific category of information
  4. REDIRECT_URI — where to redirect the user after giving us the token
  5. SCOPE — asks Discord for specific data
  6. STATE — helps personalize our request

We have that last variable, 'nonce', created in the function.
'nonce' is simply a string that gets randomly generated every time we need to use the Discord OAuth2 Endpoint.
It needs to be different every time. This is why it's not a CONSTANT.


Let's bring this all together.

When our 'background.js' script gets the message to "login", we'll call the 'chrome.identity.launchWebAuthFlow()' function.

It takes two arguments.

  1. an object with our constructed OAuth2 endpoint and the 'interactive' flag of true (this allows the user to see the Discord prompt for credentials).

  2. a callback function that gives us a 'redirect uri' from Discord’s servers. We can use a "token" delivered to us to gain access to the user-who-logged-in's Discord data. We won’t be doing that in this video; we're simply using this endpoint to *"Authenticate"** the user, not "Authorize" us.*

// background.js

chrome.runtime.onMessage.addListener((request, sender, sendResponse) => {
    if (request.message === 'login') {
        chrome.identity.launchWebAuthFlow({
            url: create_auth_endpoint(),
            interactive: true
        }, function (redirect_uri) {
            if (chrome.runtime.lastError || redirect_uri.includes('access_denied')) {
                console.log("Could not authenticate.");
                sendResponse('fail');
            } else {
                user_signed_in = true;
                sendResponse('success');
            }
        });
        return true;
    } 
//...
Enter fullscreen mode Exit fullscreen mode

Note: **
In addition to checking for a chrome.runtime error, we also check that the user signed in successfully. It they don't, an **"error=access_denied"
string will be found in the 'redirect_uri'.

The "logout" branch is really simple.

Just flip the 'user_signed_in' flag to false and send a response of "success".

// background.js

//...
    } else if (request.message === 'logout') {
        user_signed_in = false;
        sendResponse('success');
    }
});
Enter fullscreen mode Exit fullscreen mode

We're done.

When the user hits the "Sign In" button, they'll be greeted with Discord's Login System.
If they successfully sign in, they'll be shown our "Sign Out" page.

Discord login finished


You can find the source files here.

If you want a more in-depth guide, check out my full video tutorial on YouTube, An Object Is A.

Discord Login System with Chrome Extensions | OAuth2/OpenID Connect

Top comments (1)

Collapse
 
kenodev_ profile image
Keno

How can I stay logged in? Like always if I start the extension I need to sign in again and thats annoy.