Skip to content

Google

To set up Google OAuth for your app, you'll need to get your clientId and clientSecret credentials from the Google Console. See Google OAuth 2.0 for instructions.

Configuration

Add your Google provider in config/wish.js:

js
// config/wish.js
module.exports.wish = {
  provider: 'google',
  providers: {
    google: {
      clientId: 'your-client-id',
      clientSecret: 'your-client-secret',
      redirect: 'http://localhost:1337/auth/callback'
    }
  }
}

Or use environment variables (Wish automatically detects these):

VariableDescription
GOOGLE_CLIENT_IDYour Google OAuth Client ID
GOOGLE_CLIENT_SECRETYour Google OAuth Client Secret
GOOGLE_CALLBACK_URLThe callback URL registered with Google
js
// config/wish.js - credentials loaded from env vars
module.exports.wish = {
  provider: 'google',
  providers: {
    google: {}
  }
}

Customizing Scopes

To request additional permissions from Google:

js
// config/wish.js
module.exports.wish = {
  provider: 'google',
  providers: {
    google: {
      scopes: [
        'https://www.googleapis.com/auth/userinfo.profile',
        'https://www.googleapis.com/auth/userinfo.email',
        'https://www.googleapis.com/auth/calendar.readonly'
      ]
    }
  }
}

See Google OAuth Scopes for available scopes.

Defaults

Wish provides these defaults for Google (override only what you need):

OptionDefault
scopes['...userinfo.profile', '...userinfo.email']
scopeSeparator (space)
tokenUrlhttps://oauth2.googleapis.com/token
userUrlhttps://www.googleapis.com/oauth2/v2/userinfo?alt=json

The Redirect

A typical flow is to have a button on your website 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:

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

Now let's author this auth/redirect action:

js
module.exports = {
  friendlyName: 'Redirect',

  description: 'Redirect auth.',

  inputs: {},

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

  fn: async function () {
    return sails.wish.redirect()
  }
}

INFO

Notice the redirect is a one-line of code and when this action is called, it will redirect to Google 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:

js
'GET /auth/callback': 'auth/callback',
js
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.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
      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.
      req.session.userId = user.id
      return exits.success('/')
    })
  }
}

There you have it, a Google OAuth flow with just two routes!

All open source projects are released under the MIT License.