## 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>
7.4 KiB
title |
---|
Google Auth Hook |
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.
If you are still using Unleash v3 you need to follow the google-auth-hook-v3
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
-
Go to the credentials section in the Google Cloud Platform Console.
-
Click OAuth consent screen. Type a product name. Fill in any relevant optional fields. Click Save.
-
Click Create credentials > OAuth client ID.
-
Under Application type, select Web Application.
-
Type the Name.
-
Under Authorized redirect URIs enter the following URLs, one at a time.
http://localhost:4242/api/auth/callback
-
Click Create.
-
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;
Add googleAdminAuth()
function and other options. Make sure to also accept the services argument to get access to the userService
.
function googleAdminAuth(app, config, services) {
const { baseUriPath } = config.server;
const { userService } = services;
}
let options = {
authentication: {
type: 'custom',
customAuthHandler: googleAdminAuth,
},
};
unleash.start(options).then((instance) => {
console.log(
`Unleash started on http://localhost:${instance.app.get('port')}`,
);
});
In googleAdminAuth
function: 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';
function googleAdminAuth(app, config, services) {
const { baseUriPath } = config.server;
const { userService } = services;
passport.use(
new GoogleOAuth2Strategy(
{
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
callbackURL: GOOGLE_CALLBACK_URL,
},
async function (accessToken, refreshToken, profile, cb) {
// Extract the minimal profile information we need from the profile object
// and connect with Unleash
const email = profile.emails[0].value;
const user = await userService.loginUserWithoutPassword(email, true);
cb(null, user);
},
),
);
}
In googleAdminAuth
function
Configure passport
package.
function googleAdminAuth(app, config, services) {
// ...
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, config, services) {
// ...
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, config, services) {
// ...
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/
.
function googleAdminAuth(app, config, services) {
// ...
app.use('/api/', (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
You can also find the complete source code for this guide in the unleash-examples project.
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';
function googleAdminAuth(app, config, services) {
const { baseUriPath } = config.server;
const { userService } = services;
passport.use(
new GoogleOAuth2Strategy(
{
clientID: GOOGLE_CLIENT_ID,
clientSecret: GOOGLE_CLIENT_SECRET,
callbackURL: GOOGLE_CALLBACK_URL,
},
async (accessToken, refreshToken, profile, cb) => {
const email = profile.emails[0].value;
const user = await userService.loginUserWithoutPassword(email, true);
cb(null, user);
},
),
);
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/', (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 = {
authentication: {
type: 'custom',
customAuthHandler: googleAdminAuth,
},
};
unleash.start(options);