npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

gothic

v1.0.5

Published

Google Authentication & Authorization Simplifier

Downloads

9

Readme

Gothic

Making the Easy things Easy

Introduction

When Google announced the planned migration to it's new Identity Service for api authorization and user authentication functionality, I had a devil of a time figuring out how to make it work. The documentation promised a streamlined user experience and a simplified developer process. But for the life of me, I could not figure out how to achieve either of these blissful results.

As a result, through extremely close attention to some of the more cryptic corners of the documentation, enormous trial and error, and some timely help from stack overflow, I wrote this library.

Gothic is intended to make the easy things easy.

You may find it doesn't serve your needs, but it may still serve as a reference point in writing your own implementation against Google's APIs.

For a full walkthrough of how this is implemented, see Seamless Integration with the Google Identity Service.

Dependencies

Google now uses JWT's to encode user information. Gothic leverages the simple jwt-decode library to handle this. There are no other dependencies.

Characteristics

Gothic is designed to support single-page web apps.

Gothic is completely self contained. You don't need to add any tags or scripts to your html.

Gothic strives to keep the console fairly clean of extraneous messages, and to communicate with the host app only on actionable events.

Use Cases

  • Recognize whether the user is likely to be a return visitor
  • Sign-in/Sign-up with a fully configurable google button
  • Sign-in/Sign-up with the Google one-tap flow
  • Authorize access to Google APIs
  • Maintain that authorization with the least possible friction across return visits and page loads
  • Ordinary sign out
  • Revoke authorization

Example:

This sample app will demonstrate sign-in by two methods, button for unrecognized users, or onetap for recognized users. It will demonstrate signout and revoke. It demonstrates the observer pattern. It demonstrates authentication of and access to Google API's (in this case, drive). It demonstrates low-friction (no-clicks) re-authentication for return visitors (page refresh).

What you would need to test this out would be your client-id and API-key, with the correct domain configuration to function as a legitimate google API client.

Barebones HTML
<!DOCTYPE html>
<html>
  <head>
    <title>Gothic API Quickstart</title>
    <meta charset="utf-8" />
    <script src="./app.js" type="module"></script>
  </head>
  <body>
    <p>Gothic API Quickstart</p>
    <div id="signin"></div>
    <div id="signout">
      <button id="signout_button">Sign Out</button>
      <button id="revoke_button">Sign Out</button>
    </div>
    <pre id="content" style="white-space: pre-wrap;"></pre>
  </body>
</html>
Skeleton App

Note that how you include gothic may vary in your implementation.

import Goth from 'gothic';

const CLI_ID = '992279172814-gmqt9g152hjab4rojlbt8niq1sn299vk.apps.googleusercontent.com';
const API_KEY = 'AIzaSyBSeLzjE2r3dt9yER5l9gNIJpLWr5vOTy8';
const SCOPES = 'https://www.googleapis.com/auth/drive.metadata'; // Space delimited if more than one
const DISCOVERY = 'https://www.googleapis.com/discovery/v1/apis/drive/v3/rest';

/**
 * The google libraries are loaded, and ready for action!
 */
function proceedAsLoaded() {
  if (Goth.recognize()) {
    Goth.onetap();
  } else {
    forceSignin();
  }
}

/**
 * They have to correctly get through the button click / sign up flow to proceed.
 */
function forceSignin() {
  Goth.button('signin', {type: 'standard', size: 'large', text: 'signup_with'});
}

function signoutEvent() {
  document.getElementById('content').innerHTML = '';
  document.getElementById('signout').style.display = 'none';
  document.getElementById('signin').style.display = 'block';
  forceSignin();
}

function proceedAsSignedIn() {
  document.getElementById('signin').style.display = 'none';
  document.getElementById('signout').style.display = 'block';
  list_files();
}
/**
 * Just to demonstrate that the APIs *can* successfully be called.
 */
async function list_files() {
  let response;
  try {
    response = await window.gapi.client.drive.files.list({
      'pageSize': 10,
      'fields': 'files(id, name)',
    });
  } catch (err) {
    document.getElementById('content').innerText = err.message;
    return;
  }
  const files = response.result.files;
  if (!files || files.length == 0) {
    document.getElementById('content').innerText = 'No files found.';
    return;
  }
  // Flatten to string to display
  const output = files.reduce(
      (str, file) => `${str}${file.name} (${file.id}\n`,
      'Files:\n');
  document.getElementById('content').innerText = output;
}

/**
 * Handle the lifecycle of authenticated status
 */
function gothWatch(event) {
  switch (event) {
    case 'signin':
      proceedAsSignedIn();
      break;
    case 'revoke':
    case 'signout': 
      signoutEvent();
      break;
    case 'loaded':
      proceedAsLoaded();
      break;
    case 'onetap_suppressed':
      forceSignin();  // If a user bypasses onetap flows, we land them with a button.
      break;
    default: 
      console.log("Well, this is a surprise!");
      console.log(event);
  }
}

/**
 * Wire up the main ux machinery.
 */
function main() {
  Goth.observe(gothWatch);
  Goth.load(CLI_ID, API_KEY, SCOPES, DISCOVERY);
  const signout = document.getElementById('signout');
  signout.style.display = 'none';
  const signout_btn = document.getElementById('signout_button');
  const revoke_btn = document.getElementById('revoke_button');
  signout_btn.onclick = Goth.signout;
  revoke_btn.onclick = Goth.revoke;
}

main();

Method Reference:

  • load(clientId, apiKey, scopes, discovery)
    • Loads all Google libraries, and prepares Goth to take action on your behalf. It will notify when everything is loaded. (TODO: if someone only needs authentication and does not plan on using google API's, we could take the absence of parameters beyond clientId as signal and drop execution of authorization flows.)
  • recognize()
    • Tests to see if we expect to recognize the incoming user. This is useful for deciding whether to display introductory text and a login button, or cut straight to one-tap.
  • button(parentId, parameters)
    • Fills the dom element named by parentId with a google button, configured by the provided parameter object, if any. These parameters are a direct pass through to google api's and documentation can be found here
  • onetap()
    • Initiates Google's one-tap flow, which should pass recognized users on to the app.
  • observe(cb)
    • Adds the provided callback function to Goth's list of observers, to be called when there is a sign-in or sign-out event, or if onetap is suppressed or bypassed.
  • unobserve(cb)
    • Removes the specified observer
  • signout()
    • Signals the user's intention to sign out of your app -- but it's on the app to honor that during the current session. A visitor who signs out will be prompted on return to sign in again, but will not have to re-authorize api access.
  • revoke()
    • Revokes authorization credentials, presumably on the user's request. On return visit, the user will have to sign in and re-authorize api access.
  • user()
    • This returns a lightweight user object with: email, family_name, given_name, picture, name properties.

Bugs

  • None identified

ToDo

  • Add JWT validation