Skip to content

Instantly share code, notes, and snippets.

@buchizo
Created July 25, 2018 07:25
Show Gist options
  • Save buchizo/9cec7d8711f10f6ecdb65afd01cc1f95 to your computer and use it in GitHub Desktop.
Save buchizo/9cec7d8711f10f6ecdb65afd01cc1f95 to your computer and use it in GitHub Desktop.
app.js using passport-azure-ad 3.0
/**
* Copyright (c) Microsoft Corporation
* All Rights Reserved
* Apache License 2.0
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*
* @flow
*/
'use strict';
/**
* Module dependencies.
*/
var express = require('express');
var cookieParser = require('cookie-parser');
var expressSession = require('express-session');
var bodyParser = require('body-parser');
var passport = require('passport');
var util = require('util');
var bunyan = require('bunyan');
var config = require('./config');
// Start QuickStart here
var OIDCStrategy = require('passport-azure-ad').OIDCStrategy;
var log = bunyan.createLogger({
name: 'Microsoft OIDC Example Web Application'
});
// Passport session setup. (Section 2)
// To support persistent login sessions, Passport needs to be able to
// serialize users into and deserialize users out of the session. Typically,
// this will be as simple as storing the user ID when serializing, and finding
// the user by ID when deserializing.
// Use the OIDCStrategy within Passport. (Section 2)
//
// Strategies in passport require a `validate` function, which accept
// credentials (in this case, an OpenID identifier), and invoke a callback
// with a user object.
// Use the OIDCStrategy within Passport. (Section 2)
//
// Strategies in passport require a `validate` function that accepts
// credentials (in this case, an OpenID identifier), and invokes a callback
// with a user object.
passport.use(new OIDCStrategy({
identityMetadata: config.creds.identityMetadata,
clientID: config.creds.clientID,
responseType: config.creds.responseType,
responseMode: config.creds.responseMode,
redirectUrl: config.creds.redirectUrl,
allowHttpForRedirectUrl: config.creds.allowHttpForRedirectUrl,
clientSecret: config.creds.clientSecret,
validateIssuer: config.creds.validateIssuer,
isB2C: config.creds.isB2C,
issuer: config.creds.issuer,
passReqToCallback: config.creds.passReqToCallback,
scope: config.creds.scope,
loggingLevel: config.creds.loggingLevel,
loggingNoPII: config.creds.loggingNoPII,
nonceLifetime: config.creds.nonceLifetime,
nonceMaxAmount: config.creds.nonceMaxAmount,
useCookieInsteadOfSession: config.creds.useCookieInsteadOfSession,
cookieEncryptionKeys: config.creds.cookieEncryptionKeys,
clockSkew: config.creds.clockSkew,
},
function(iss, sub, profile, accessToken, refreshToken, done) {
if (!profile.oid) {
return done(new Error("No oid found"), null);
}
// asynchronous verification, for effect...
process.nextTick(function () {
findByOid(profile.oid, function(err, user) {
if (err) {
return done(err);
}
if (!user) {
// "Auto-registration"
users.push(profile);
return done(null, profile);
}
return done(null, user);
});
});
}
));
// configure Express (Section 2)
// Passport session setup. (Section 2)
// To support persistent sign-in sessions, Passport needs to be able to
// serialize users into the session and deserialize them out of the session. Typically,
// this is done simply by storing the user ID when serializing and finding
// the user by ID when deserializing.
passport.serializeUser(function(user, done) {
done(null, user.oid);
});
passport.deserializeUser(function(id, done) {
findByOid(id, function (err, user) {
done(err, user);
});
});
// array to hold signed-in users
var users = [];
var findByEmail = function(email, fn) {
for (var i = 0, len = users.length; i < len; i++) {
var user = users[i];
log.info('we are using user: ', user);
if (user.oid === oid) {
return fn(null, user);
}
}
return fn(null, null);
};
// configure Express (section 2)
var app = express();
app.configure(function() {
app.set('views', __dirname + '/views');
app.set('view engine', 'ejs');
app.use(express.logger());
app.use(express.methodOverride());
app.use(cookieParser());
app.use(expressSession({ secret: 'keyboard cat', resave: true, saveUninitialized: false }));
app.use(bodyParser.urlencoded({ extended : true }));
// Initialize Passport! Also use passport.session() middleware, to support
// persistent login sessions (recommended).
app.use(passport.initialize());
app.use(passport.session());
app.use(app.router);
app.use(express.static(__dirname + '/../../public'));
});
//Routes (Section 4)
//Routes (section 4)
app.get('/', function(req, res){
res.render('index', { user: req.user });
});
app.get('/account', ensureAuthenticated, function(req, res){
res.render('account', { user: req.user });
});
app.get('/login',
passport.authenticate('azuread-openidconnect', { failureRedirect: '/' }),
function(req, res) {
log.info('Login was called in the Sample');
res.redirect('/');
});
// POST /auth/openid/return
// Use passport.authenticate() as route middleware to authenticate the
// request. If authentication fails, the user will be redirected back to the
// home page. Otherwise, the primary route function function will be called,
// which, in this example, will redirect the user to the home page.
app.post('/auth/openid/return',
passport.authenticate('azuread-openidconnect', { failureRedirect: '/' }),
function(req, res) {
log.info('call post auth return');
res.redirect('/');
});
app.get('/logout', function(req, res){
req.logout();
res.redirect('/');
});
// POST /auth/openid/return
// Use passport.authenticate() as route middleware to authenticate the
// request. If authentication fails, the user will be redirected back to the
// login page. Otherwise, the primary route function function will be called,
// which, in this example, will redirect the user to the home page.
// Simple route middleware to ensure user is authenticated. (Section 4)
// Use this route middleware on any resource that needs to be protected. If
// the request is authenticated (typically via a persistent login session),
// the request will proceed. Otherwise, the user will be redirected to the
// login page.
// Simple route middleware to ensure user is authenticated. (section 4)
// Use this route middleware on any resource that needs to be protected. If
// the request is authenticated (typically via a persistent sign-in session),
// the request proceeds. Otherwise, the user is redirected to the
// sign-in page.
function ensureAuthenticated(req, res, next) {
if (req.isAuthenticated()) { return next(); }
res.redirect('/login')
}
var findByOid = function(oid, fn) {
for (var i = 0, len = users.length; i < len; i++) {
var user = users[i];
log.info('we are using user: ', user);
if (user.oid === oid) {
return fn(null, user);
}
}
return fn(null, null);
};
app.listen(3000);
// Don't commit this file to your public repos. This config is for first-run
//
exports.creds = {
redirectUrl: 'http://localhost:3000/auth/openid/return',
identityMetadata: 'https://login.microsoftonline.com/<あなたのテナント>/.well-known/openid-configuration', // For using Microsoft you should never need to change this.
clientID: '<AADのアプリケーションのApplication ID(ポータルのブレードで見れるID)、もしくはManifest内のappID>',
clientSecret: '<生成したClientSecret>', // if you are doing code or id_token code
skipUserProfile: true, // for AzureAD should be set to true.
responseType: 'id_token code', // for login only flows use id_token. For accessing resources use `id_token code`
responseMode: 'form_post', // For login only flows we should have token passed back to us in a POST
passReqToCallback: false,
allowHttpForRedirectUrl: true, // HTTPで実行する場合はtrue必須。
loggingLevel: 'info'
//scope: ['email', 'profile'] // additional scopes you may wish to pass
};
@CKeene78
Copy link

"allowHttpForRedirectUrl: true" within the config.js was the missing piece for me. Obviously will update to 'false' when pushed to production. Thank you for the help

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment