1
0
mirror of https://github.com/Unleash/unleash.git synced 2024-10-28 19:06:12 +01:00
unleash.unleash/website/docs/using-unleash/deploy/google-auth-v3.md
Drew Gorton b2b19e4970
Navigation refactor (#5227)
## About the changes
Refactor the main nav, with the following goals: 
* Communicate the value of each section vs the format (ex:
“Understanding Unleash” vs “Topic Guides”)
* Make space for the Feature Flag tutorials section that we’re starting
to build
* Scope updates to navigation and pages that need updates based on new
URLs & organization
* Update URLs to follow the new hierarchy without breaking links (adding
redirects & editing internal links between pages as needed)

### Important files
sidebar.js
docusaurus.config.js

## Discussion points
* Redirects can't be tested out of prod, which is a bummer :/
* Some URLs have been preserved untouched while we monitor for potential
negative SEO impact of client-side redirects
* It's a large PR (sorry). Nav changes and file movements impacted lots
of files.

---------

Co-authored-by: Thomas Heartman <thomas@getunleash.ai>
2023-10-31 09:38:03 -05:00

6.7 KiB

title
Google Auth v3

You can also find the complete source code for this guide in the unleash-examples project.

This part of the tutorial shows how to create a sign-in flow for users and integrate with Unleash server project. The implementation assumes that I am working in localhost with 4242 port.

This is a simple index.ts server file.

const unleash = require('unleash-server');

unleash.start(options).then((unleash) => {
  console.log(`Unleash started on http://localhost:${unleash.app.get('port')}`);
});

Creating a web application client ID

  1. Go to the credentials section in the Google Cloud Platform Console.

  2. Click OAuth consent screen. Type a product name. Fill in any relevant optional fields. Click Save.

  3. Click Create credentials > OAuth client ID.

  4. Under Application type, select Web Application.

  5. Type the Name.

  6. Under Authorized redirect URIs enter the following URLs, one at a time.

http://localhost:4242/api/auth/callback
  1. Click Create.

  2. Copy the CLIENT ID and CLIENT SECRET and save them for later use.

Add dependencies

Add two dependencies @passport-next/passport and @passport-next/passport-google-oauth2 inside index.ts file

const unleash = require('unleash-server');
const passport = require('@passport-next/passport');
const GoogleOAuth2Strategy =
  require('@passport-next/passport-google-oauth2').Strategy;

Configure the Google strategy for use by Passport.js

OAuth 2-based strategies require a verify function which receives the credential (accessToken) for accessing the Google API on the user's behalf, along with the user's profile. The function must invoke cb with a user object, which will be set at req.user in route handlers after authentication.

const GOOGLE_CLIENT_ID = '...';
const GOOGLE_CLIENT_SECRET = '...';
const GOOGLE_CALLBACK_URL = 'http://localhost:4242/api/auth/callback';

passport.use(
  new GoogleOAuth2Strategy(
    {
      clientID: GOOGLE_CLIENT_ID,
      clientSecret: GOOGLE_CLIENT_SECRET,
      callbackURL: GOOGLE_CALLBACK_URL,
    },
    function (accessToken, refreshToken, profile, cb) {
      // Extract the minimal profile information we need from the profile object
      // and connect with Unleash to get name and email.
      cb(
        null,
        new unleash.User({
          name: profile.displayName,
          email: profile.emails[0].value,
        }),
      );
    },
  ),
);

Add googleAdminAuth() function and other options

function googleAdminAuth(app) {}

let options = {
  adminAuthentication: 'custom',
  preRouterHook: googleAdminAuth,
};

unleash.start(options).then((instance) => {
  console.log(
    `Unleash started on http://localhost:${instance.app.get('port')}`,
  );
});

In googleAdminAuth function

Configure passport package.

function googleAdminAuth(app) {
  app.use(passport.initialize());
  app.use(passport.session());
  passport.serializeUser((user, done) => done(null, user));
  passport.deserializeUser((user, done) => done(null, user));
  // ...
}

Implement a preRouter hook for /auth/google/login. It's necessary for login with Google.

function googleAdminAuth(app) {
  // ...
  app.get(
    '/auth/google/login',
    passport.authenticate('google', { scope: ['email'] }),
  );
  // ...
}

Implement a preRouter hook for /api/auth/callback. It's a callback when the login is executed.

function googleAdminAuth(app) {
  // ...
  app.get(
    '/api/auth/callback',
    passport.authenticate('google', {
      failureRedirect: '/api/admin/error-login',
    }),
    (req, res) => {
      // Successful authentication, redirect to your app.
      res.redirect('/');
    },
  );
  // ...
}

Implement a preRouter hook for /api/admin.

function googleAdminAuth(app) {
  // ...
  app.use('/api/admin/', (req, res, next) => {
    if (req.user) {
      next();
    } else {
      // Instruct unleash-frontend to pop-up auth dialog
      return res
        .status('401')
        .json(
          new unleash.AuthenticationRequired({
            path: '/auth/google/login',
            type: 'custom',
            message: `You have to identify yourself in order to use Unleash. Click the button and follow the instructions.`,
          }),
        )
        .end();
    }
  });
  // ...
}

The complete code

The index.ts server file.

'use strict';

const unleash = require('unleash-server');
const passport = require('@passport-next/passport');
const GoogleOAuth2Strategy = require('@passport-next/passport-google-oauth2');

const GOOGLE_CLIENT_ID = '...';
const GOOGLE_CLIENT_SECRET = '...';
const GOOGLE_CALLBACK_URL = 'http://localhost:4242/api/auth/callback';

passport.use(
  new GoogleOAuth2Strategy(
    {
      clientID: GOOGLE_CLIENT_ID,
      clientSecret: GOOGLE_CLIENT_SECRET,
      callbackURL: GOOGLE_CALLBACK_URL,
    },
    (accessToken, refreshToken, profile, cb) => {
      cb(
        null,
        new unleash.User({
          name: profile.displayName,
          email: profile.emails[0].value,
        }),
      );
    },
  ),
);

function googleAdminAuth(app) {
  app.use(passport.initialize());
  app.use(passport.session());
  passport.serializeUser((user, done) => done(null, user));
  passport.deserializeUser((user, done) => done(null, user));

  app.get(
    '/auth/google/login',
    passport.authenticate('google', { scope: ['email'] }),
  );
  app.get(
    '/api/auth/callback',
    passport.authenticate('google', {
      failureRedirect: '/api/admin/error-login',
    }),
    (req, res) => {
      res.redirect('/');
    },
  );

  app.use('/api/admin/', (req, res, next) => {
    if (req.user) {
      next();
    } else {
      return res
        .status('401')
        .json(
          new unleash.AuthenticationRequired({
            path: '/auth/google/login',
            type: 'custom',
            message: `You have to identify yourself in order to use Unleash. Click the button and follow the instructions.`,
          }),
        )
        .end();
    }
  });
}

const options = {
  adminAuthentication: 'custom',
  preRouterHook: googleAdminAuth,
};

unleash.start(options).then((instance) => {
  console.log(
    `Unleash started on http://localhost:${instance.app.get('port')}`,
  );
});