https://github.com/austinjhunt/express-oauth-oidc-demo

https://github.com/austinjhunt/express-oauth-oidc-demo

Science Score: 13.0%

This score indicates how likely this project is to be science-related based on various indicators:

  • CITATION.cff file
  • codemeta.json file
    Found codemeta.json file
  • .zenodo.json file
  • DOI references
  • Academic publication links
  • Academic email domains
  • Institutional organization owner
  • JOSS paper metadata
  • Scientific vocabulary similarity
    Low similarity (10.0%) to scientific vocabulary
Last synced: 10 months ago · JSON representation

Repository

Basic Info
  • Host: GitHub
  • Owner: austinjhunt
  • Language: JavaScript
  • Default Branch: main
  • Size: 1.72 MB
Statistics
  • Stars: 0
  • Watchers: 2
  • Forks: 0
  • Open Issues: 0
  • Releases: 0
Created almost 4 years ago · Last pushed almost 4 years ago
Metadata Files
Readme

README.md

Demonstration of Google's OIDC-conformant OAuth 2.0 Flow with Express and Google Drive

This is a simple lightweight app for demonstrating use of Google's OAuth 2.0 APIs (which conform to the OIDC specification) for authN and authZ with Express and Google Drive.

Overview

The goal of this lightweight ExpressJS app is to demonstrate the OpenID Connect Authorization flow against a Google Cloud Platform application created with very restricted read-only Google Drive scopes. More specifically, it follows the official Google OAuth 2.0 implementation for authentication which happens to conform to the OpenID Connect specification, and is OpenID Certified. In short, Google's OAuth 2.0 APIs can be used for both authentication (OIDC) and authorization (OAuth).

Implementation

GCP

First things first, I created a new project in Google called Simple OAuth Demo, configured a consent screen for it with authorized JS origin http://localhost:8080 and redirect URL http://localhost:8080/redirect_uri (which is the Express route that handles the redirect back from the consent screen). I then created an OAuth client ID in the credentials screen within the project, and downloaded the credentials JSON file (stored as a gitignored config.js in the repo).

From here, I moved to the Express development.

Express App

The app was implemented with the simple Express web framework for Node.js.

When run, you're first presented with a basic home page with a button that when clicked will trigger the OIDC-conforming OAuth 2.0 authorization flow documented by Google.

Starting home page with button to trigger auth flow

When you click the button (which is really a link to the /start-flow GET route), behind the scenes, the first thing that is happening is the generation of a random anti-forgery state token such that this value can be compared with the state value returned after Google redirects the user back to the app.

Then, the user gets redirected to the authentication endpoint on Google with a series of query parameters as defined below, coming from the (gitignored) config file pulled from GCP when creating the OAuth client. I made sure to include access_type: offline in this particular request to allow for the use of a refresh token without involving the user when the access token expires.

app.get("/start-flow", (req, res) => { let state = getRandomValue(24); req.session.state = state; let full_auth_url = `${config.auth_uri}?` + serialize({ response_type: "code", // should always be code for basic auth code flow client_id: config.client_id, access_type: "offline", // allow refresh token scope: scopes.join(" "), redirect_uri: config.redirect_uris[0], state: req.session.state, nonce: getRandomValue(24), // random value for replay protection }); res.redirect(full_auth_url); });

When redirected, the user gets prompted first with a login screen if not already logged into Google (the identity provider).

sign in page for Google

After logging in (ideally with MFA configured on the Google Account), the user then gets a warning about the client being unverified by Google which is definitely true since this is very much a small-scale test application. warning about unverified client

If the user clicks Continue, they then get prompted with the OAuth Consent Screen configured during the GCP setup. This page outlines every permission (scope) being requested by the client, and allows the user to check the specific permissions they want to grant to the client.

consent screen prompt

After the consent screen, Google then redirects the user back to the configured redirect_uri for the client which in the case of this app is the /redirect_uri route handled by the following code in index.js.

```

app.get("/redirecturi", (req, res) => { // redirect URI configured on GCP to which // auth endpoint response is sent after user consents // verify that state in query param matches session state generated before // sending user to auth endpoint if (req.query.state === req.session.state) { console.log("state matches"); req.session.authorized = true; exchangeAuthCodeForAccessToken(req, res).then(() => { fetch("https://www.googleapis.com/drive/v3/about?fields=user", { method: "GET", headers: { Authorization: `Bearer ${req.session.accesstoken}, Accept: "application/json", }, }) .then((response) => response.json()) .then((data) => { if (data.user) { console.log(data.user.emailAddress); req.session.user = data.user; req.session.emailAddress = data.user.emailAddress; req.session.photoLink = data.user.photoLink; req.session.displayName = data.user.displayName; } }) .then(() => { res.render("index", { authorized: req.session.authorized, emailAddress: req.session.emailAddress, photoLink: req.session.photoLink, displayName: req.session.displayName, }); }); }); } else { console.log(Returned state does not match session state ); res.redirect("/start-flow"); } }); ``

Note that in this example we're assuming that the user has consented to the requested permissions/scopes before getting to this point, but usually this is the point where you want to check which permissions were granted and which ones were not such that portions of your system or application using denied scopes can be disabled for the user.

When redirected to /redirect_uri by Google, two important query parameters are included: state and code. The first thing that happens here is the verification of the state token by comparing it against the state token which was previously generated when starting the flow. If the token matches, we take the code query parameter (which is a one-time authorization code) and exchange it with Google's token endpoint for an access token and an ID token. We also update the user's session to indicate the authorization. Moreover, we use the newly obtained access token to hit the About API endpoint to pull some basic Google Account information about the user (namely first name and email address).

With that information pulled from Google via the API, EJS is then used to embed that information into the front end view. EJS is also used to conditionally show a button for pulling Google Drive File Information for users who are authorized (by embedding the boolean authorized session variable into the EJS view). If the user is authorized, they see the button (really a link to the /get-files route) to pull the basic metadata about their Google Drive Files.

When clicked, the /get-files route handler on the server side executes and pulls a files list from the Google Drive v3 API using the access token stored in the session. For each of the returned files, the JavaScript on the client site appends a basic container element with the file's name and mimetype displayed. files list obtained and displayed from Google Drive API v3

At this point, the user has consented to the app reading information from the user's Google account. The user can confirm this by looking at a number of screens on their Google Account:

Activity on Google Account

Activity on Google Account

Third party apps with access

Apps with Access

The user can optionally revoke that access as shown below: revoke access

Running the App

  1. Clone the repository and navigate into it via CLI.
  2. Run the following commands:

npm i node index.js

Owner

  • Name: Austin Hunt
  • Login: austinjhunt
  • Kind: user
  • Location: Greenville, SC
  • Company: College of Charleston

Portrait-artist-turned-computer-geek with a fused love for the visual and the technical, bringing experience with and excitement for web dev, automation, & art

GitHub Events

Total
Last Year

Issues and Pull Requests

Last synced: over 1 year ago

All Time
  • Total issues: 0
  • Total pull requests: 0
  • Average time to close issues: N/A
  • Average time to close pull requests: N/A
  • Total issue authors: 0
  • Total pull request authors: 0
  • Average comments per issue: 0
  • Average comments per pull request: 0
  • Merged pull requests: 0
  • Bot issues: 0
  • Bot pull requests: 0
Past Year
  • Issues: 0
  • Pull requests: 0
  • Average time to close issues: N/A
  • Average time to close pull requests: N/A
  • Issue authors: 0
  • Pull request authors: 0
  • Average comments per issue: 0
  • Average comments per pull request: 0
  • Merged pull requests: 0
  • Bot issues: 0
  • Bot pull requests: 0
Top Authors
Issue Authors
Pull Request Authors
Top Labels
Issue Labels
Pull Request Labels