Skip to content

Commit c8596dd

Browse files
committed
Completed add auth step
1 parent 2c897f6 commit c8596dd

8 files changed

Lines changed: 285 additions & 157 deletions

File tree

demo/graph-tutorial/.env.example

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,5 @@
1+
OAUTH_APP_ID=YOUR_APP_ID_HERE
2+
OAUTH_APP_SECRET=YOUR_APP_SECRET_HERE
3+
OAUTH_REDIRECT_URI=http://localhost:3000/auth/callback
4+
OAUTH_SCOPES='user.read,calendars.read'
5+
OAUTH_AUTHORITY=https://login.microsoftonline.com/common/

demo/graph-tutorial/app.js

Lines changed: 43 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,16 +1,50 @@
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');
6-
var session = require('express-session');
7-
var flash = require('connect-flash');
9+
const session = require('express-session');
10+
const flash = require('connect-flash');
11+
const msal = require('@azure/msal-node');
12+
require('dotenv').config();
813

914
var indexRouter = require('./routes/index');
1015
var usersRouter = require('./routes/users');
16+
var authRouter = require('./routes/auth');
1117

1218
var app = express();
1319

20+
// <MsalInitSnippet>
21+
// In-memory storage of logged-in users
22+
// For demo purposes only, production apps should store
23+
// this in a reliable storage
24+
app.locals.users = {};
25+
26+
// MSAL config
27+
const msalConfig = {
28+
auth: {
29+
clientId: process.env.OAUTH_APP_ID,
30+
authority: process.env.OAUTH_AUTHORITY,
31+
clientSecret: process.env.OAUTH_APP_SECRET
32+
},
33+
system: {
34+
loggerOptions: {
35+
loggerCallback(loglevel, message, containsPii) {
36+
console.log(message);
37+
},
38+
piiLoggingEnabled: false,
39+
logLevel: msal.LogLevel.Verbose,
40+
}
41+
}
42+
};
43+
44+
// Create msal application object
45+
app.locals.msalClient = new msal.ConfidentialClientApplication(msalConfig);
46+
// </MsalInitSnippet>
47+
1448
// <SessionSnippet>
1549
// Session middleware
1650
// NOTE: Uses default in-memory session store, which is not
@@ -38,6 +72,12 @@ app.use(function(req, res, next) {
3872
res.locals.error.push({message: 'An error occurred', debug: errs[i]});
3973
}
4074

75+
// Check for an authenticated user and load
76+
// into response locals
77+
if (req.session.userId) {
78+
res.locals.user = app.locals.users[req.session.userId];
79+
}
80+
4181
next();
4282
});
4383
// </SessionSnippet>
@@ -53,6 +93,7 @@ app.use(cookieParser());
5393
app.use(express.static(path.join(__dirname, 'public')));
5494

5595
app.use('/', indexRouter);
96+
app.use('/auth', authRouter);
5697
app.use('/users', usersRouter);
5798

5899
// catch 404 and forward to error handler

demo/graph-tutorial/graph.js

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
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
12+
.api('/me')
13+
.select('displayName,mail,userPrincipalName')
14+
.get();
15+
return user;
16+
}
17+
};
18+
19+
function getAuthenticatedClient(accessToken) {
20+
// Initialize Graph client
21+
const client = graph.Client.init({
22+
// Use the provided access token to authenticate
23+
// requests
24+
authProvider: (done) => {
25+
done(null, accessToken);
26+
}
27+
});
28+
29+
return client;
30+
}

demo/graph-tutorial/package-lock.json

Lines changed: 25 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

demo/graph-tutorial/package.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,13 @@
1313
"debug": "^4.1.1",
1414
"dotenv": "^8.2.0",
1515
"express": "^4.17.1",
16+
"express-promise-router": "^4.0.1",
1617
"express-session": "^1.17.1",
1718
"hbs": "^4.1.1",
1819
"http-errors": "^1.8.0",
1920
"isomorphic-fetch": "^2.2.1",
2021
"moment": "^2.28.0",
21-
"morgan": "^1.10.0"
22+
"morgan": "^1.10.0",
23+
"uuid": "^8.3.0"
2224
}
2325
}

demo/graph-tutorial/routes/auth.js

Lines changed: 92 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,92 @@
1+
// Copyright (c) Microsoft Corporation.
2+
// Licensed under the MIT License.
3+
4+
const router = require('express-promise-router')();
5+
const graph = require('../graph');
6+
7+
/* GET auth callback. */
8+
router.get('/signin',
9+
async function (req, res) {
10+
const urlParameters = {
11+
scopes: process.env.OAUTH_SCOPES.split(','),
12+
redirectUri: process.env.OAUTH_REDIRECT_URI
13+
};
14+
15+
try {
16+
const authUrl = await req.app.locals
17+
.msalClient.getAuthCodeUrl(urlParameters);
18+
res.redirect(authUrl);
19+
}
20+
catch (error) {
21+
console.log(`Error: ${error}`);
22+
req.flash('error_msg', {
23+
message: 'Error getting auth URL',
24+
debug: JSON.stringify(error)
25+
});
26+
res.redirect('/');
27+
}
28+
}
29+
);
30+
31+
// <CallbackSnippet>
32+
router.get('/callback',
33+
async function(req, res) {
34+
const tokenRequest = {
35+
code: req.query.code,
36+
scopes: process.env.OAUTH_SCOPES.split(','),
37+
redirectUri: process.env.OAUTH_REDIRECT_URI
38+
};
39+
40+
try {
41+
const response = await req.app.locals
42+
.msalClient.acquireTokenByCode(tokenRequest);
43+
44+
// Save the user's homeAccountId in their session
45+
req.session.userId = response.account.homeAccountId;
46+
47+
const user = await graph.getUserDetails(response.accessToken);
48+
49+
// Add the user to user storage
50+
req.app.locals.users[req.session.userId] = {
51+
displayName: user.displayName,
52+
email: user.mail || user.userPrincipalName
53+
};
54+
} catch(error) {
55+
req.flash('error_msg', {
56+
message: 'Error completing authentication',
57+
debug: JSON.stringify(error)
58+
});
59+
}
60+
61+
res.redirect('/');
62+
}
63+
);
64+
// </CallbackSnippet>
65+
66+
router.get('/signout',
67+
function(req, res) {
68+
// Sign out
69+
if (req.session.userId) {
70+
// Look up the user's account in the cache
71+
const accounts = req.app.locals.msalClient
72+
.getTokenCache()
73+
.getAllAccounts();
74+
75+
const userAccount = accounts.find(a => a.homeAccountId === req.session.userId);
76+
77+
// Remove the account
78+
if (userAccount) {
79+
req.app.locals.msalClient
80+
.getTokenCache()
81+
.removeAccount(userAccount);
82+
}
83+
}
84+
85+
// Destroy the user's session
86+
req.session.destroy(function (err) {
87+
res.redirect('/');
88+
});
89+
}
90+
);
91+
92+
module.exports = router;

tutorial/02-create-app.md

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -44,7 +44,9 @@ Before moving on, install some additional packages that you will use later:
4444
- [moment](https://github.com/moment/moment/) for formatting date/time values.
4545
- [connect-flash](https://github.com/jaredhanson/connect-flash) to flash error messages in the app.
4646
- [express-session](https://github.com/expressjs/session) to store values in an in-memory server-side session.
47+
- [express-promise-router](https://github.com/express-promise-router/express-promise-router) to allow route handlers to return a Promise.
4748
- [msal-node](https://github.com/AzureAD/microsoft-authentication-library-for-js/tree/dev/lib/msal-node) for authenticating and getting access tokens.
49+
- [uuid](https://github.com/uuidjs/uuid) used by msal-node to generate GUIDs.
4850
- [microsoft-graph-client](https://github.com/microsoftgraph/msgraph-sdk-javascript) for making calls to Microsoft Graph.
4951
- [isomorphic-fetch](https://github.com/matthew-andrews/isomorphic-fetch) to polyfill the fetch for Node. A fetch polyfill is required for the `microsoft-graph-client` library. See the [Microsoft Graph JavaScript client library wiki](https://github.com/microsoftgraph/msgraph-sdk-javascript/wiki/Migration-from-1.x.x-to-2.x.x#polyfill-only-when-required) for more information.
5052
@@ -71,8 +73,9 @@ Before moving on, install some additional packages that you will use later:
7173
1. Update the application to use the `connect-flash` and `express-session` middleware. Open the `./app.js` file and add the following `require` statement to the top of the file.
7274
7375
```javascript
74-
var session = require('express-session');
75-
var flash = require('connect-flash');
76+
const session = require('express-session');
77+
const flash = require('connect-flash');
78+
const msal = require('@azure/msal-node');
7679
```
7780

7881
1. Add the following code immediately after the `var app = express();` line.

0 commit comments

Comments
 (0)