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

@keystone-next/auth-passport-legacy

v6.0.1

Published

Provides Social Authentication Strategies based on PassportJS.

Downloads

8

Readme

Passport auth strategy

View changelog

This feature is currently in progress. While passport works for the GraphQLApp, only password authentication is supported for the AdminUIApp. Please see this issue for more details.

Enable Keystone authentication via services such as Google, Twitter, Facebook, GitHub, and any others supported by passport.js.

Authentication flows

This package enables three authentication flows;

  1. Single Step Account Creation & Authentication
  2. Multi Step Account Creation & Authentication
  3. Existing Account Authentication

Single-step account creation and authentication

When creating a new account in Keystone, the service (Google, Twitter, etc) provides some basic user information such as name and, if enabled, email. Often this information alone is enough to create a new Keystone account, so it is the default authentication flow known as Single Step Account Creation.

For example, when logging in with Google, the user will;

  • Click "Login with Google"
  • Be redirected to google.com's authentication page if not already logged in
  • Be asked to grant permission for your Keystone app
  • Be redirected to your application's callback URL
  • This package will create a new account & authenticate the user, then trigger onAuthenticated({ token, item, isNewItem }) with isNewItem = true (see the API docs below)

Multi-step account creation and authentication

Sometimes the information provided by the service is not enough to create a new account in Keystone. For example, your application may require the user's age, or want to confirm the email address provided by the service.

The default Single Step Flow can be extended to pause account creation while we gather the extra information from the user. Pausing even works across page refreshes. This is known as Multi Step Account Creation.

For example, when logging in with Google, the user will (differences from the Single Step Flow are bolded):

  • Click "Login with Google"
  • Be redirected to google.com's authentication page if not already logged in
  • Be asked to grant permission for your Keystone app
  • Be redirected to your application's callback URL
  • Be redirected to a form to gather more information
  • Submit the form which will resume account creation & authentication
  • This package will create a new account & authenticate the user, then trigger onAuthenticated({ token, item, isNewItem }) with isNewItem = true (see the API docs below)

Existing Account Authentication

When a user has previously created an account via a given service, this package will use information stored in the idField (see the API docs below) to match the account and authenticate them:

  • Click "Login with Google"
  • Be redirected to google.com's authentication page if not already logged in
  • Be redirected to your application's callback URL
  • This package will authenticate the existing user, then trigger onAuthenticated({ token, item, isNewItem }) with isNewItem = false (see the API docs below)

API

The API needs documentation.

Usage

Single-step with google

Note: The documentation below applies to any of the supported strategies (Twitter, Facebook, etc)

To run this example: keystone dev, then visit http://localhost:3000/auth/google to start the Google authentication process.

index.js

const { Keystone } = require('@keystone-next/keystone-legacy');
const { MongooseAdapter } = require('@keystone-next/adapter-mongoose-legacy');
const { GraphQLApp } = require('@keystone-next/app-graphql-legacy');
const { AdminUIApp } = require('@keystone-next/app-admin-ui-legacy');

const { GoogleAuthStrategy } = require('@keystone-next/auth-passport-legacy');

const cookieSecret = '<Something super secret>';

const keystone = new Keystone({
  adapter: new MongooseAdapter(),
  cookieSecret,
});

keystone.createList('User', {
  fields: {
    name: { type: Text },
    email: { type: Text },

    // This field name must match the `idField` setting passed to the auth
    // strategy constructor below
    googleId: { type: Text },
  },
});

const googleStrategy = keystone.createAuthStrategy({
  type: GoogleAuthStrategy,
  list: 'User',
  config: {
    idField: 'googleId',
    appId: '<Your Google App Id>',
    appSecret: '<Your Google App Secret>',
    loginPath: '/auth/google',
    callbackPath: '/auth/google/callback',
    callbackHost: 'http://localhost:3000',

    // Once a user is found/created and successfully matched to the
    // googleId, they are authenticated, and the token is returned here.
    // NOTE: By default Keystone sets a `keystone.sid` which authenticates the
    // user for the API domain. If you want to authenticate via another domain,
    // you must pass the `token` as a Bearer Token to GraphQL requests.
    onAuthenticated: ({ token, item, isNewItem }, req, res) => {
      console.log(token);
      res.redirect('/');
    },

    // If there was an error during any of the authentication flow, this
    // callback is executed
    onError: (error, req, res) => {
      console.error(error);
      res.redirect('/?error=Uh-oh');
    },
  },
});

module.exports = {
  keystone,
  apps: [
    new GraphQLApp(),
    new AdminUIApp({
      name: 'Login With Google Example',
      authStrategy: googleStrategy,
    }),
  ],
};

Multi-step with google

Note: The documentation below applies to any of the supported strategies (Twitter, Facebook, etc)

Due to the extra route used for gathering the user's name, this example implements an All-in-one Custom Server and should be run with node server.js, then visit http://localhost:3000/auth/google to start the Google authentication process.

server.js

const { Keystone } = require('@keystone-next/keystone-legacy');
const { MongooseAdapter } = require('@keystone-next/adapter-mongoose-legacy');
const { GraphQLApp } = require('@keystone-next/app-graphql-legacy');
const { AdminUIApp } = require('@keystone-next/app-admin-ui-legacy');
const express = require('express');

const { GoogleAuthStrategy } = require('@keystone-next/auth-passport-legacy');

const cookieSecret = '<Something super secret>';

const keystone = new Keystone({
  adapter: new MongooseAdapter(),
  cookieSecret,
});

keystone.createList('User', {
  fields: {
    name: { type: Text },
    email: { type: Text },

    // This field name must match the `idField` setting passed to the auth
    // strategy constructor below
    googleId: { type: Text },
  },
});

const googleStrategy = keystone.createAuthStrategy({
  type: GoogleAuthStrategy,
  list: 'User',
  config: {
    idField: 'googleId',
    appId: '<Your Google App Id>',
    appSecret: '<Your Google App Secret>',
    loginPath: '/auth/google',
    callbackPath: '/auth/google/callback',
    callbackHost: 'http://localhost:3000',

    loginPathMiddleware: (req, res, next) => {
      // An express middleware executed before the Passport social signin flow
      // begins. Useful for setting cookies, etc.
      // Don't forget to call `next()`!
      next();
    },

    callbackPathMiddleware: (req, res, next) => {
      // An express middleware executed before the callback route is run. Useful
      // for logging, etc.
      // Don't forget to call `next()`!
      next();
    },

    // Called when there's no existing user for the given googleId
    // Default: resolveCreateData: () => ({})
    resolveCreateData: ({ createData, actions: { pauseAuthentication } }, req, res) => {
      // If we don't have the right data to continue with a creation
      if (!createData.name) {
        // then we pause the flow
        pauseAuthentication();
        // And redirect the user to a page where they can enter the data.
        // Later, the `resolveCreateData()` method will be re-executed this
        // time with the complete data.
        res.redirect(`/auth/google/step-2`);
        return;
      }

      return createData;
    },

    // Once a user is found/created and successfully matched to the
    // googleId, they are authenticated, and the token is returned here.
    // NOTE: By default Keystone sets a `keystone.sid` which authenticates the
    // user for the API domain. If you want to authenticate via another domain,
    // you must pass the `token` as a Bearer Token to GraphQL requests.
    onAuthenticated: ({ token, item, isNewItem }, req, res) => {
      console.log(token);
      res.redirect('/');
    },

    // If there was an error during any of the authentication flow, this
    // callback is executed
    onError: (error, req, res) => {
      console.error(error);
      res.redirect('/?error=Uh-oh');
    },
  },
});

keystone
  .prepare({
    apps: [
      new GraphQLApp(),
      new AdminUIApp({
        name: 'Login With Google Example',
        authStrategy: googleStrategy,
      }),
    ],
    dev: process.env.NODE_ENV !== 'production',
  })
  .then(async ({ middlewares }) => {
    await keystone.connect();
    const app = express();
    app.use(middlewares);

    // Sample page to collect a name, submits to the completion step which will
    // create a user
    app.use(`/auth/google/step-2`, express.urlencoded({ extended: true }), (req, res, next) => {
      if (req.method === 'POST') {
        const { name } = req.body;
        // Continue the authentication flow with additional data the user
        // submitted.
        // This data is merged into other data required by Keystone and will
        // trigger the resolveCreateData() method again.
        return googleStrategy.resumeAuthentication({ name }, req, res, next);
      }

      res.send(`
          <form method="post">
            <label>
              What is your name? <input type="text" name="name" />
            </label>
            <button type="submit">Submit</button>
          </form>
        `);
    });

    app.listen(port, error => {
      if (error) throw error;
      console.log(`Ready. App available at http://localhost:${port}`);
    });
  })
  .catch(error => {
    console.error(error);
    process.exit(1);
  });

Sometimes you just need to get the profile data provided by the auth provider. In that case, you have to call the resolveCreateData with serviceProfile property. See an example below:

// Here we wait for our serviceProfile to get the data provided by the auth provider
resolveCreateData: ({ createData, serviceProfile }) => {
  // Once we had our seviceProfile, we set it on our list fields

  // Google will return the profile data inside the _json key
  // Each provider can return the user profile data in a different way.
  // Check how it's returned on your provider documentation
  createData.name = serviceProfile._json.name;
  createData.profilePicture = serviceProfile._json.picture;

  return createData;
};

Using other Passport strategies

You can create your own strategies to work with Keystone by extending the PassportAuthStrategy class:

const PassportWordPress = require('passport-wordpress').Strategy;
const { PassportAuthStrategy } = require('@keystone-next/auth-passport-legacy');

class WordPressAuthStrategy extends PassportAuthStrategy {
  constructor(keystone, listKey, config) {
    super(WordPressAuthStrategy.authType, keystone, listKey, config, PassportWordPress);
  }
}

WordPressAuthStrategy.authType = 'wordpress';

module.exports = WordPressAuthStrategy;