Skip to content

Commit d67532e

Browse files
committed
Completed add AAD auth step
1 parent c9fe329 commit d67532e

6 files changed

Lines changed: 409 additions & 287 deletions

File tree

demo/graph-tutorial/.env.example

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
OAUTH_APP_ID=YOUR_APP_ID_HERE
2+
OAUTH_APP_PASSWORD=YOUR_APP_PASSWORD_HERE
3+
OAUTH_REDIRECT_URI=http://localhost:3000/auth/callback
4+
OAUTH_SCOPES='profile offline_access user.read calendars.read'
5+
OAUTH_AUTHORITY=https://login.microsoftonline.com/common/
6+
OAUTH_ID_METADATA=v2.0/.well-known/openid-configuration
7+
OAUTH_AUTHORIZE_ENDPOINT=oauth2/v2.0/authorize
8+
OAUTH_TOKEN_ENDPOINT=oauth2/v2.0/token

demo/graph-tutorial/app.js

Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,101 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
14
var createError = require('http-errors');
25
var express = require('express');
36
var path = require('path');
47
var cookieParser = require('cookie-parser');
58
var logger = require('morgan');
69
var session = require('express-session');
710
var flash = require('connect-flash');
11+
require('dotenv').config();
12+
13+
var passport = require('passport');
14+
var OIDCStrategy = require('passport-azure-ad').OIDCStrategy;
15+
16+
// Configure passport
17+
18+
// In-memory storage of logged-in users
19+
// For demo purposes only, production apps should store
20+
// this in a reliable storage
21+
var users = {};
22+
23+
// Passport calls serializeUser and deserializeUser to
24+
// manage users
25+
passport.serializeUser(function(user, done) {
26+
// Use the OID property of the user as a key
27+
users[user.profile.oid] = user;
28+
done (null, user.profile.oid);
29+
});
30+
31+
passport.deserializeUser(function(id, done) {
32+
done(null, users[id]);
33+
});
34+
35+
// <ConfigureOAuth2Snippet>
36+
// Configure simple-oauth2
37+
const oauth2 = require('simple-oauth2').create({
38+
client: {
39+
id: process.env.OAUTH_APP_ID,
40+
secret: process.env.OAUTH_APP_PASSWORD
41+
},
42+
auth: {
43+
tokenHost: process.env.OAUTH_AUTHORITY,
44+
authorizePath: process.env.OAUTH_AUTHORIZE_ENDPOINT,
45+
tokenPath: process.env.OAUTH_TOKEN_ENDPOINT
46+
}
47+
});
48+
// </ConfigureOAuth2Snippet>
49+
50+
// Callback function called once the sign-in is complete
51+
// and an access token has been obtained
52+
// <SignInCompleteSnippet>
53+
async function signInComplete(iss, sub, profile, accessToken, refreshToken, params, done) {
54+
if (!profile.oid) {
55+
return done(new Error("No OID found in user profile."));
56+
}
57+
58+
try{
59+
const user = await graph.getUserDetails(accessToken);
60+
61+
if (user) {
62+
// Add properties to profile
63+
profile['email'] = user.mail ? user.mail : user.userPrincipalName;
64+
}
65+
} catch (err) {
66+
return done(err);
67+
}
68+
69+
// Create a simple-oauth2 token from raw tokens
70+
let oauthToken = oauth2.accessToken.create(params);
71+
72+
// Save the profile and tokens in user storage
73+
users[profile.oid] = { profile, accessToken };
74+
return done(null, users[profile.oid]);
75+
}
76+
// </SignInCompleteSnippet>
77+
78+
// Configure OIDC strategy
79+
passport.use(new OIDCStrategy(
80+
{
81+
identityMetadata: `${process.env.OAUTH_AUTHORITY}${process.env.OAUTH_ID_METADATA}`,
82+
clientID: process.env.OAUTH_APP_ID,
83+
responseType: 'code id_token',
84+
responseMode: 'form_post',
85+
redirectUrl: process.env.OAUTH_REDIRECT_URI,
86+
allowHttpForRedirectUrl: true,
87+
clientSecret: process.env.OAUTH_APP_PASSWORD,
88+
validateIssuer: false,
89+
passReqToCallback: false,
90+
scope: process.env.OAUTH_SCOPES.split(' ')
91+
},
92+
signInComplete
93+
));
894

995
var indexRouter = require('./routes/index');
1096
var usersRouter = require('./routes/users');
97+
var authRouter = require('./routes/auth');
98+
var graph = require('./graph');
1199

12100
var app = express();
13101

@@ -52,7 +140,23 @@ app.use(express.urlencoded({ extended: false }));
52140
app.use(cookieParser());
53141
app.use(express.static(path.join(__dirname, 'public')));
54142

143+
// Initialize passport
144+
app.use(passport.initialize());
145+
app.use(passport.session());
146+
147+
// <AddProfileSnippet>
148+
app.use(function(req, res, next) {
149+
// Set the authenticated user in the
150+
// template locals
151+
if (req.user) {
152+
res.locals.user = req.user.profile;
153+
}
154+
next();
155+
});
156+
// </AddProfileSnippet>
157+
55158
app.use('/', indexRouter);
159+
app.use('/auth', authRouter);
56160
app.use('/users', usersRouter);
57161

58162
// catch 404 and forward to error handler

demo/graph-tutorial/graph.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
var graph = require('@microsoft/microsoft-graph-client');
5+
require('isomorphic-fetch');
6+
7+
module.exports = {
8+
getUserDetails: async function(accessToken) {
9+
const client = getAuthenticatedClient(accessToken);
10+
11+
const user = await client.api('/me').get();
12+
return user;
13+
}
14+
};
15+
16+
function getAuthenticatedClient(accessToken) {
17+
// Initialize Graph client
18+
const client = graph.Client.init({
19+
// Use the provided access token to authenticate
20+
// requests
21+
authProvider: (done) => {
22+
done(null, accessToken);
23+
}
24+
});
25+
26+
return client;
27+
}

demo/graph-tutorial/routes/auth.js

Lines changed: 47 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,47 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
var express = require('express');
5+
var passport = require('passport');
6+
var router = express.Router();
7+
8+
/* GET auth callback. */
9+
router.get('/signin',
10+
function (req, res, next) {
11+
passport.authenticate('azuread-openidconnect',
12+
{
13+
response: res,
14+
prompt: 'login',
15+
failureRedirect: '/',
16+
failureFlash: true,
17+
successRedirect: '/'
18+
}
19+
)(req,res,next);
20+
}
21+
);
22+
23+
// <CallbackRouteSnippet>
24+
router.post('/callback',
25+
function(req, res, next) {
26+
passport.authenticate('azuread-openidconnect',
27+
{
28+
response: res,
29+
failureRedirect: '/',
30+
failureFlash: true,
31+
successRedirect: '/'
32+
}
33+
)(req,res,next);
34+
}
35+
);
36+
// </CallbackRouteSnippet>
37+
38+
router.get('/signout',
39+
function(req, res) {
40+
req.session.destroy(function(err) {
41+
req.logout();
42+
res.redirect('/');
43+
});
44+
}
45+
);
46+
47+
module.exports = router;

demo/graph-tutorial/tokens.js

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
// <TokensSnippet>
5+
module.exports = {
6+
getAccessToken: async function(req) {
7+
if (req.user) {
8+
// Get the stored token
9+
var storedToken = req.user.oauthToken;
10+
11+
if (storedToken) {
12+
if (storedToken.expired()) {
13+
// refresh token
14+
var newToken = await storedToken.refresh();
15+
16+
// Update stored token
17+
req.user.oauthToken = newToken;
18+
return newToken.token.access_token;
19+
}
20+
21+
// Token still valid, just return it
22+
return storedToken.token.access_token;
23+
}
24+
}
25+
}
26+
};
27+
// </TokensSnippet>

0 commit comments

Comments
 (0)