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

sails-hook-wish

v0.0.9

Published

The OAuth hook you wish exists for Sails

Downloads

30

Readme

wish

wish is the OAuth Sails hook you wish(pun intended) exists for Sails. wish provides a simple, convenient way to authenticate with OAuth providers.

Supported OAuth Providers

Installation

In your Sails project run the below command to install wish and it's node-fetch peer-dependency:

npm i --save sails-hook-wish @sailscasts/sails-hook-node-fetch

GitHub

To setup up a GitHub OAuth for your app, wish expects the following key and property in either config/local.js or config/custom.js. For example you can have a development GitHub clientId and clientSecret in config/local.js

Do make sure to get the needed clientId and clientSecret credentials from GitHub. You can see here for instructions on how to get those credentials

github: {
    clientId: 'CLIENT_ID',
    clientSecret: 'CLIENT_SECRET',
    redirect: 'http://localhost:1337/auth/callback',
  },

You can override this value for production in either custom.js or in an environment specific custom.js. I personally set this up for https://sailscasts.com to override the local.js value so I can have 3 environments with 3 different clientId, clientSecret, and redirect values.

// custom.js
github: {
    clientId: process.env.GITHUB_CLIENT_ID,
    clientSecret: process.env.GITHUB_CLIENT_SECRET,
    redirect: 'https://example.com/auth/callback',
  },

Notice I am using environment variables as it's best practice not to commit your secret credentials. In the case of local.js that's okay because that file is never committed to version control.

The redirect

A typical flow is to have a button on your website say like "Sign in with GitHub". A good example can be found here

Clicking that button should call a redirect route you've set in routes.js

 'GET /auth/redirect': 'auth/redirect',

Now let's author this auth/redirect action:

module.exports = {
  friendlyName: 'Redirect',

  description: 'Redirect auth.',

  inputs: {},

  exits: {
    success: {
      responseType: 'redirect',
    },
  },

  fn: async function () {
    return sails.wish.provider('github').redirect()
  },
}

Notice the redirect is a one-line of code and when this action is called, it will redirect to GitHub to begin the OAuth process.

The callback

Note the callback URL we set above that wish will callback? Let's also implement that starting from the route in routes.js

'GET /auth/callback': 'auth/callback',
module.exports = {
  friendlyName: 'Callback',

  description: 'Callback auth.',

  inputs: {
    code: {
      type: 'string',
      required: true,
    },
  },

  exits: {
    success: {
      responseType: 'redirect',
    },
  },

  fn: async function ({ code }, exits) {
    const req = this.req

    // Get the GitHub user info
    const githubUser = await sails.wish.provider('github').user(code)

    User.findOrCreate(
      { githubId: githubUser.id },
      {
        id: sails.helpers.getUuid(),
        githubId: githubUser.id,
        email: githubUser.email,
        name: githubUser.name,
        githubAvatarUrl: githubUser.avatar_url,
        githubAccessToken: githubUser.accessToken,
      }
    ).exec(async (error, user, wasCreated) => {
      if (error) throw error

      // Checks if the user email has changed since last log in
      // And then update the email change candidate which will be used be used to prompt the user to update their email
      if (!wasCreated && user.email !== githubUser.email) {
        await User.updateOne({ id: user.id }).set({
          emailChangeCandidate: githubUser.email,
        })
      }

      // Checks if the user name has changed since last log in
      // And then update the name if changed
      if (!wasCreated && user.name !== githubUser.name) {
        await User.updateOne({ id: user.id }).set({
          name: githubUser.name,
        })
      }

      if (!wasCreated && user.githubAvatarUrl !== githubUser.avatar_url) {
        await User.updateOne({ id: user.id }).set({
          githubAvatarUrl: githubUser.avatar_url,
        })
      }

      if (!wasCreated && user.githubAccessToken !== githubUser.accessToken) {
        await User.updateOne({ id: user.id }).set({
          githubAccessToken: githubUser.accessToken,
        })
      }

      // Modify the active session instance.
      // (This will be persisted when the response is sent.)
      req.session.userId = user.id
      return exits.success('/')
    })
  },
}

The above is an actual real world use case of wish in https://sailscasts.com. You can perform any business logic you want.

There you have it, a GitHub OAuth flow with just two routes and one line of code each to both redirect to GitHub and get the OAuth user details.

Google

To setup up a Google OAuth for your app, wish expects the following key and property in either config/local.js or config/custom.js. For example you can have a development Google clientId and clientSecret in config/local.js

Do make sure to get the needed clientId and clientSecret credentials from the Google Console. You can see here for instructions on how to get those credentials

google: {
    clientId: 'CLIENT_ID',
    clientSecret: 'CLIENT_SECRET',
    redirect: 'http://localhost:1337/auth/callback',
  },

You can override this value for production in either custom.js or in an environment specific custom.js. I personally set this up for https://sailscasts.com to override the local.js value so I can have 3 environments with 3 different clientId, clientSecret, and redirect values.

// custom.js
google: {
    clientId: process.env.GOOGLE_CLIENT_ID,
    clientSecret: process.env.GOOGLE_CLIENT_SECRET,
    redirect: 'https://example.com/auth/callback',
  },

Notice I am using environment variables as it's best practice not to commit your secret credentials. In the case of local.js that's okay because that file is never committed to version control.

The redirect

A typical flow is to have a button on your website say like "Sign in with Google". A good example is implemented in The Boring JavaScript Stack mellow template

Clicking that button should call a redirect route you've set in routes.js

 'GET /auth/redirect': 'auth/redirect',

Now let's author this auth/redirect action:

module.exports = {
  friendlyName: 'Redirect',

  description: 'Redirect auth.',

  inputs: {},

  exits: {
    success: {
      responseType: 'redirect',
    },
  },

  fn: async function () {
    return sails.wish.provider('google').redirect()
  },
}

Notice the redirect is a one-line of code and when this action is called, it will redirect to GitHub to begin the OAuth process.

The callback

Note the callback URL we set above that wish will callback? Let's also implement that starting from the route in routes.js

'GET /auth/callback': 'auth/callback',
module.exports = {
  friendlyName: 'Callback',

  description: 'Callback auth.',

  inputs: {
    code: {
      type: 'string',
      required: true,
    },
  },

  exits: {
    success: {
      responseType: 'redirect',
    },
  },

  fn: async function ({ code }, exits) {
    const req = this.req

    // Get the Google user info
    const googleUser = await sails.wish.provider('google').user(code)

    User.findOrCreate(
      { googleId: googleUser.id },
      {
        id: sails.helpers.getUuid(),
        googleId: googleUser.id,
        email: googleUser.email,
        name: googleUser.name,
        googleAvatarUrl: googleUser.picture,
        googleAccessToken: googleUser.accessToken,
        googleIdToken: googleUser.idToken,
      }
    ).exec(async (error, user, wasCreated) => {
      if (error) throw error

      // Checks if the user email has changed since last log in
      // And then update the email change candidate which will be used be used to prompt the user to update their email
      if (!wasCreated && user.email !== googleUser.email) {
        await User.updateOne({ id: user.id }).set({
          emailChangeCandidate: googleUser.email,
        })
      }

      if (!wasCreated && user.name !== googleUser.name) {
        await User.updateOne({ id: user.id }).set({
          name: googleUser.name,
        })
      }

      if (!wasCreated && user.googleAvatarUrl !== googleUser.picture) {
        await User.updateOne({ id: user.id }).set({
          googleAvatarUrl: googleUser.picture,
        })
      }

      if (!wasCreated && user.googleAccessToken !== googleUser.accessToken) {
        await User.updateOne({ id: user.id }).set({
          googleAccessToken: googleUser.accessToken,
        })
      }

      if (!wasCreated && user.googleIdToken !== googleUser.idToken) {
        await User.updateOne({ id: user.id }).set({
          googleIdToken: googleUser.idToken,
        })
      }

      // Modify the active session instance.
      // (This will be persisted when the response is sent.)
      req.session.userId = user.id
      return exits.success('/')
    })
  },
}

There you have it, a Google OAuth flow with just two routes and one line of code each to both redirect to Google and get the OAuth user details.

License

wish is open-sourced software licensed under the MIT license.