├── .gitignore ├── LICENSE ├── README.md ├── app.js ├── config.js ├── contributing.md ├── package.json ├── routes ├── index.js └── user.js └── views ├── account.ejs ├── index.ejs └── layout.ejs /.gitignore: -------------------------------------------------------------------------------- 1 | # Logs 2 | logs 3 | *.log 4 | 5 | # Runtime data 6 | pids 7 | *.pid 8 | *.seed 9 | 10 | # Directory for instrumented libs generated by jscoverage/JSCover 11 | lib-cov 12 | 13 | # Coverage directory used by tools like istanbul 14 | coverage 15 | 16 | # Grunt intermediate storage (http://gruntjs.com/creating-plugins#storing-task-files) 17 | .grunt 18 | 19 | # node-waf configuration 20 | .lock-wscript 21 | 22 | # Compiled binary addons (http://nodejs.org/api/addons.html) 23 | build/Release 24 | 25 | # Dependency directory 26 | # https://www.npmjs.org/doc/misc/npm-faq.html#should-i-check-my-node_modules-folder-into-git 27 | node_modules 28 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Microsoft Azure Active Directory Samples and Documentation 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | 23 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | #Azure Active Directory OIDC Web Sample 2 | 3 | This Node.js app will give you with a quick and easy way to set up a Web application in node.js with Express using OpenID Connect. The sample server included in the download are designed to run on any platform. 4 | 5 | We've released all of the source code for this example in GitHub under an MIT license, so feel free to clone (or even better, fork!) and provide feedback on the forums. 6 | 7 | 8 | ## Quick Start 9 | 10 | Getting started with the sample is easy. It is configured to run out of the box with minimal setup. 11 | 12 | ### Step 1: Register a web application in B2C Azure AD Tenant 13 | 14 | If you don't have an Azure AD B2C Tenant yet, please [create one](https://azure.microsoft.com/en-us/documentation/articles/active-directory-b2c-get-started/). 15 | 16 | Next let's register a web application in your tenant. 17 | 18 | * In the main page of your tenant, click `Manage B2C settings`, and you will be redirected to the settings page. 19 | 20 | * Click `Applications`, then click `Add`. Enter a name like 'my_b2c_app', and switch the `Web App / Web API` option to yes. After that, enter 'http://localhost:3000/auth/openid/return' into the `Reply URL` field. Then click `Generate key` to generate a app key, and save it somewhere. This app key is the client secret of your application. Now click `Create` button to finish registration. 21 | 22 | * Click the application you just created, copy the `Application ID` field and save it somewhere. This value is the clientID of your application. 23 | 24 | * Now let's add some policies we will use for this sample. In the setting page, add a sign-in policy, a sign-up poligy, a profile-editing policy and a password-reset policy. When you add the policies, use the names 'signin', 'signup', 'updateprofile' and 'resetpassword' respectively. For `Identity providers`, choose `Email signup`; for `Application claims`, choose `Email Addresses`, `User's Object ID` and any other claims you want; for `Sign-up attributes`, choose `Email Address` and anything else you like. 25 | 26 | * Now we have a B2C web application and policies registered. Note that Azure AD adds a 'B2C_1_' prefix automatically to all policy names, so the policy names we will use are actually 'B2C_1_signin', 'B2C_1_signup', 'B2C_1_updateprofile' and 'B2C_1_resetpassword'. 27 | 28 | ### Step 2: Download node.js for your platform 29 | To successfully use this sample, you need a working installation of Node.js. 30 | 31 | ### Step 3: Download the Sample application and modules 32 | 33 | Next, clone the sample repo and install the NPM. 34 | 35 | From your shell or command line: 36 | 37 | * `$ git clone git@github.com:AzureADQuickStarts/B2C-WebApp-OpenIDConnect-NodeJS.git` 38 | * `$ npm install` 39 | 40 | ### Step 4: Configure your server 41 | 42 | * Provide the parameters in `exports.creds` in config.js as instructed. 43 | 44 | * Update `exports.destroySessionUrl` in config.js, using your tenant name and signin policy name. If you want to redirect the users to a different url after they log out, update the `post_logout_redirect_uri` part as well. 45 | 46 | * Set `exports.useMongoDBSessionStore` in config.js to false, if you want to use the 47 | default session store for `express-session`. Note that the default session store is 48 | not suitable for production, you must use mongoDB or other [compatible session stores](https://github.com/expressjs/session#compatible-session-stores). 49 | 50 | * Update `exports.databaseUri`, if you want to use mongoDB session store and a different database uri. 51 | 52 | * Update `exports.mongoDBSessionMaxAge`. Here you can specify how long you want 53 | to keep a session in mongoDB. The unit is second(s). 54 | 55 | ### Step 5: Run the application 56 | 57 | * Start mongoDB service. 58 | 59 | If you are using mongoDB session store in this app, you have to install mongoDB and start the service first. If you are using the default session store, you can skip this step. 60 | 61 | * Run the app. 62 | 63 | Use the following command in terminal. 64 | 65 | * `$ node app.js` 66 | 67 | **Is the server output hard to understand?:** We use `bunyan` for logging in this sample. The console won't make much sense to you unless you also install bunyan and run the server like above but pipe it through the bunyan binary: 68 | 69 | * `$ npm install -g bunyan` 70 | * `$ node app.js | bunyan` 71 | 72 | ### You're done! 73 | 74 | You will have a server successfully running on `http://localhost:3000`. 75 | 76 | ### Acknowledgements 77 | 78 | We would like to acknowledge the folks who own/contribute to the following projects for their support of Azure Active Directory and their libraries that were used to build this sample. In places where we forked these libraries to add additional functionality, we ensured that the chain of forking remains intact so you can navigate back to the original package. Working with such great partners in the open source community clearly illustrates what open collaboration can accomplish. Thank you! 79 | 80 | 81 | ## About The Code 82 | 83 | Code hosted on GitHub under MIT license 84 | -------------------------------------------------------------------------------- /app.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) Microsoft Corporation 3 | * All Rights Reserved 4 | * MIT License 5 | * 6 | * Permission is hereby granted, free of charge, to any person obtaining a copy of this 7 | * software and associated documentation files (the 'Software'), to deal in the Software 8 | * without restriction, including without limitation the rights to use, copy, modify, 9 | * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to 10 | * permit persons to whom the Software is furnished to do so, subject to the following 11 | * conditions: 12 | * 13 | * The above copyright notice and this permission notice shall be 14 | * included in all copies or substantial portions of the Software. 15 | * 16 | * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, 17 | * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS 19 | * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, 20 | * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT 21 | * OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 22 | */ 23 | 24 | 'use strict'; 25 | 26 | /****************************************************************************** 27 | * Module dependencies. 28 | *****************************************************************************/ 29 | 30 | var express = require('express'); 31 | var cookieParser = require('cookie-parser'); 32 | var expressSession = require('express-session'); 33 | var bodyParser = require('body-parser'); 34 | var methodOverride = require('method-override'); 35 | var passport = require('passport'); 36 | var util = require('util'); 37 | var bunyan = require('bunyan'); 38 | var config = require('./config'); 39 | 40 | // set up database for express session 41 | var MongoStore = require('connect-mongo')(expressSession); 42 | var mongoose = require('mongoose'); 43 | 44 | // Start QuickStart here 45 | 46 | var OIDCStrategy = require('passport-azure-ad').OIDCStrategy; 47 | 48 | var log = bunyan.createLogger({ 49 | name: 'Microsoft OIDC Example Web Application' 50 | }); 51 | 52 | /****************************************************************************** 53 | * Set up passport in the app 54 | ******************************************************************************/ 55 | 56 | //----------------------------------------------------------------------------- 57 | // To support persistent login sessions, Passport needs to be able to 58 | // serialize users into and deserialize users out of the session. Typically, 59 | // this will be as simple as storing the user ID when serializing, and finding 60 | // the user by ID when deserializing. 61 | //----------------------------------------------------------------------------- 62 | passport.serializeUser(function(user, done) { 63 | done(null, user.oid); 64 | }); 65 | 66 | passport.deserializeUser(function(oid, done) { 67 | findByOid(oid, function (err, user) { 68 | done(err, user); 69 | }); 70 | }); 71 | 72 | // array to hold logged in users 73 | var users = []; 74 | 75 | var findByOid = function(oid, fn) { 76 | for (var i = 0, len = users.length; i < len; i++) { 77 | var user = users[i]; 78 | log.info('we are using user: ', user); 79 | if (user.oid === oid) { 80 | return fn(null, user); 81 | } 82 | } 83 | return fn(null, null); 84 | }; 85 | 86 | //----------------------------------------------------------------------------- 87 | // Use the OIDCStrategy within Passport. 88 | // 89 | // Strategies in passport require a `verify` function, which accepts credentials 90 | // (in this case, the `oid` claim in id_token), and invoke a callback to find 91 | // the corresponding user object. 92 | // 93 | // The following are the accepted prototypes for the `verify` function 94 | // (1) function(iss, sub, done) 95 | // (2) function(iss, sub, profile, done) 96 | // (3) function(iss, sub, profile, access_token, refresh_token, done) 97 | // (4) function(iss, sub, profile, access_token, refresh_token, params, done) 98 | // (5) function(iss, sub, profile, jwtClaims, access_token, refresh_token, params, done) 99 | // (6) prototype (1)-(5) with an additional `req` parameter as the first parameter 100 | // 101 | // To do prototype (6), passReqToCallback must be set to true in the config. 102 | //----------------------------------------------------------------------------- 103 | passport.use(new OIDCStrategy({ 104 | identityMetadata: config.creds.identityMetadata, 105 | clientID: config.creds.clientID, 106 | responseType: config.creds.responseType, 107 | responseMode: config.creds.responseMode, 108 | redirectUrl: config.creds.redirectUrl, 109 | allowHttpForRedirectUrl: config.creds.allowHttpForRedirectUrl, 110 | clientSecret: config.creds.clientSecret, 111 | validateIssuer: config.creds.validateIssuer, 112 | isB2C: config.creds.isB2C, 113 | issuer: config.creds.issuer, 114 | passReqToCallback: config.creds.passReqToCallback, 115 | scope: config.creds.scope, 116 | loggingLevel: config.creds.loggingLevel, 117 | nonceLifetime: config.creds.nonceLifetime, 118 | nonceMaxAmount: config.creds.nonceMaxAmount, 119 | useCookieInsteadOfSession: config.creds.useCookieInsteadOfSession, 120 | cookieEncryptionKeys: config.creds.cookieEncryptionKeys, 121 | clockSkew: config.creds.clockSkew, 122 | }, 123 | function(iss, sub, profile, accessToken, refreshToken, done) { 124 | if (!profile.oid) { 125 | return done(new Error("No oid found"), null); 126 | } 127 | // asynchronous verification, for effect... 128 | process.nextTick(function () { 129 | findByOid(profile.oid, function(err, user) { 130 | if (err) { 131 | return done(err); 132 | } 133 | if (!user) { 134 | // "Auto-registration" 135 | users.push(profile); 136 | return done(null, profile); 137 | } 138 | return done(null, user); 139 | }); 140 | }); 141 | } 142 | )); 143 | 144 | 145 | //----------------------------------------------------------------------------- 146 | // Config the app, include middlewares 147 | //----------------------------------------------------------------------------- 148 | var app = express(); 149 | 150 | app.set('views', __dirname + '/views'); 151 | app.set('view engine', 'ejs'); 152 | app.use(express.logger()); 153 | app.use(methodOverride()); 154 | app.use(cookieParser()); 155 | 156 | // set up session middleware 157 | if (config.useMongoDBSessionStore) { 158 | mongoose.connect(config.databaseUri); 159 | app.use(express.session({ 160 | secret: 'secret', 161 | cookie: {maxAge: config.mongoDBSessionMaxAge * 1000}, 162 | store: new MongoStore({ 163 | mongooseConnection: mongoose.connection, 164 | clear_interval: config.mongoDBSessionMaxAge 165 | }) 166 | })); 167 | } else { 168 | app.use(expressSession({ secret: 'keyboard cat', resave: true, saveUninitialized: false })); 169 | } 170 | 171 | app.use(bodyParser.urlencoded({ extended : true })); 172 | 173 | // Initialize Passport! Also use passport.session() middleware, to support 174 | // persistent login sessions (recommended). 175 | app.use(passport.initialize()); 176 | app.use(passport.session()); 177 | app.use(app.router); 178 | app.use(express.static(__dirname + '/../../public')); 179 | 180 | //----------------------------------------------------------------------------- 181 | // Set up the route controller 182 | // 183 | // 1. For 'login' route and 'returnURL' route, use `passport.authenticate`. 184 | // This way the passport middleware can redirect the user to login page, receive 185 | // id_token etc from returnURL. 186 | // 187 | // 2. For the routes you want to check if user is already logged in, use 188 | // `ensureAuthenticated`. It checks if there is an user stored in session, if not 189 | // it will call `passport.authenticate` to ask for user to log in. 190 | //----------------------------------------------------------------------------- 191 | function ensureAuthenticated(req, res, next) { 192 | if (req.isAuthenticated()) { return next(); } 193 | res.redirect('/login'); 194 | }; 195 | 196 | app.get('/', function(req, res) { 197 | res.render('index', { user: req.user }); 198 | }); 199 | 200 | // '/account' is only available to logged in user 201 | app.get('/account', ensureAuthenticated, function(req, res) { 202 | res.render('account', { user: req.user }); 203 | }); 204 | 205 | app.get('/login', 206 | function(req, res, next) { 207 | passport.authenticate('azuread-openidconnect', 208 | { 209 | response: res, // required 210 | resourceURL: config.resourceURL, // optional. Provide a value if you want to specify the resource. 211 | customState: 'my_state', // optional. Provide a value if you want to provide custom state value. 212 | failureRedirect: '/' 213 | } 214 | )(req, res, next); 215 | }, 216 | function(req, res) { 217 | log.info('Login was called in the Sample'); 218 | res.redirect('/'); 219 | }); 220 | 221 | // 'GET returnURL' 222 | // `passport.authenticate` will try to authenticate the content returned in 223 | // query (such as authorization code). If authentication fails, user will be 224 | // redirected to '/' (home page); otherwise, it passes to the next middleware. 225 | app.get('/auth/openid/return', 226 | function(req, res, next) { 227 | passport.authenticate('azuread-openidconnect', 228 | { 229 | response: res, // required 230 | failureRedirect: '/' 231 | } 232 | )(req, res, next); 233 | }, 234 | function(req, res) { 235 | log.info('We received a return from AzureAD.'); 236 | res.redirect('/'); 237 | }); 238 | 239 | // 'POST returnURL' 240 | // `passport.authenticate` will try to authenticate the content returned in 241 | // body (such as authorization code). If authentication fails, user will be 242 | // redirected to '/' (home page); otherwise, it passes to the next middleware. 243 | app.post('/auth/openid/return', 244 | function(req, res, next) { 245 | passport.authenticate('azuread-openidconnect', 246 | { 247 | response: res, // required 248 | failureRedirect: '/' 249 | } 250 | )(req, res, next); 251 | }, 252 | function(req, res) { 253 | log.info('We received a return from AzureAD.'); 254 | res.redirect('/'); 255 | }); 256 | 257 | // 'logout' route, logout from passport, and destroy the session with AAD. 258 | app.get('/logout', function(req, res){ 259 | req.session.destroy(function(err) { 260 | req.logOut(); 261 | res.redirect(config.destroySessionUrl); 262 | }); 263 | }); 264 | 265 | app.listen(3000); 266 | 267 | -------------------------------------------------------------------------------- /config.js: -------------------------------------------------------------------------------- 1 | 2 | exports.creds = { 3 | // Required. It must be tenant-specific endpoint, common endpoint is not supported to use B2C 4 | // feature. 5 | identityMetadata: 'https://login.microsoftonline.com/.onmicrosoft.com/v2.0/.well-known/openid-configuration', 6 | // or equivalently: 'https://login.microsoftonline.com//v2.0/.well-known/openid-configuration' 7 | 8 | // Required, the client ID of your app in AAD 9 | clientID: '', 10 | 11 | // Required, must be 'code', 'code id_token', 'id_token code' or 'id_token' 12 | // If you want to get access_token, you must be 'code', 'code id_token' or 'id_token code' 13 | responseType: 'code id_token', 14 | 15 | // Required 16 | responseMode: 'form_post', 17 | 18 | // Required, the reply URL registered in AAD for your app 19 | redirectUrl: 'http://localhost:3000/auth/openid/return', 20 | 21 | // Required if we use http for redirectUrl 22 | allowHttpForRedirectUrl: true, 23 | 24 | // Required if `responseType` is 'code', 'id_token code' or 'code id_token'. 25 | // If app key contains '\', replace it with '\\'. 26 | clientSecret: '', 27 | 28 | // Required, must be true for B2C 29 | isB2C: true, 30 | 31 | // Required to set to false if you don't want to validate issuer 32 | validateIssuer: true, 33 | 34 | // Required if you want to provide the issuer(s) you want to validate instead of using the issuer from metadata 35 | issuer: null, 36 | 37 | // Required to set to true if the `verify` function has 'req' as the first parameter 38 | passReqToCallback: false, 39 | 40 | // Recommended to set to true. By default we save state in express session, if this option is set to true, then 41 | // we encrypt state and save it in cookie instead. This option together with { session: false } allows your app 42 | // to be completely express session free. 43 | useCookieInsteadOfSession: true, 44 | 45 | // Required if `useCookieInsteadOfSession` is set to true. You can provide multiple set of key/iv pairs for key 46 | // rollover purpose. We always use the first set of key/iv pair to encrypt cookie, but we will try every set of 47 | // key/iv pair to decrypt cookie. Key can be any string of length 32, and iv can be any string of length 12. 48 | cookieEncryptionKeys: [ 49 | { 'key': '12345678901234567890123456789012', 'iv': '123456789012' }, 50 | { 'key': 'abcdefghijklmnopqrstuvwxyzabcdef', 'iv': 'abcdefghijkl' } 51 | ], 52 | 53 | // Optional. The additional scope you want besides 'openid' 54 | // (1) if you want refresh_token, use 'offline_access' 55 | // (2) if you want access_token, use the clientID 56 | scope: ['offline_access'], 57 | 58 | // Optional, 'error', 'warn' or 'info' 59 | loggingLevel: 'info', 60 | 61 | // Optional. The lifetime of nonce in session or cookie, the default value is 3600 (seconds). 62 | nonceLifetime: null, 63 | 64 | // Optional. The max amount of nonce saved in session or cookie, the default value is 10. 65 | nonceMaxAmount: 5, 66 | 67 | // Optional. The clock skew allowed in token validation, the default value is 300 seconds. 68 | clockSkew: null, 69 | }; 70 | 71 | // The url you need to go to destroy the session with AAD, 72 | // replace with your tenant name, and 73 | // replace with your signin policy name. 74 | exports.destroySessionUrl = 75 | 'https://login.microsoftonline.com/.onmicrosoft.com/oauth2/v2.0/logout' + 76 | '?p=' + 77 | '&post_logout_redirect_uri=http://localhost:3000'; 78 | 79 | // If you want to use the mongoDB session store for session middleware; otherwise we will use the default 80 | // session store provided by express-session. 81 | // Note that the default session store is designed for development purpose only. 82 | exports.useMongoDBSessionStore = true; 83 | 84 | // If you want to use mongoDB, provide the uri here for the database. 85 | exports.databaseUri = 'mongodb://localhost/OIDCStrategy'; 86 | 87 | // How long you want to keep session in mongoDB. 88 | exports.mongoDBSessionMaxAge = 24 * 60 * 60; // 1 day (unit is second) -------------------------------------------------------------------------------- /contributing.md: -------------------------------------------------------------------------------- 1 | # CONTRIBUTING 2 | 3 | Azure Active Directory SDK projects welcomes new contributors. This document will guide you 4 | through the process. 5 | 6 | ### CONTRIBUTOR LICENSE AGREEMENT 7 | 8 | Please visit [https://cla.microsoft.com/](https://cla.microsoft.com/) and sign the Contributor License 9 | Agreement. You only need to do that once. We can not look at your code until you've submitted this request. 10 | 11 | 12 | ### FORK 13 | 14 | Fork the project [on GitHub][] and check out 15 | your copy. 16 | 17 | Now decide if you want your feature or bug fix to go into the dev branch 18 | or the master branch. **All bug fixes and new features should go into the dev branch.** 19 | 20 | The master branch is effectively frozen; patches that change the SDKs 21 | protocols or API surface area or affect the run-time behavior of the SDK will be rejected. 22 | 23 | Some of our SDKs have bundled dependencies that are not part of the project proper. Any changes to files in those directories or its subdirectories should be sent to their respective 24 | projects. Do not send your patch to us, we cannot accept it. 25 | 26 | In case of doubt, open an issue in the [issue tracker][]. 27 | 28 | Especially do so if you plan to work on a major change in functionality. Nothing is more 29 | frustrating than seeing your hard work go to waste because your vision 30 | does not align with our goals for the SDK. 31 | 32 | 33 | ### BRANCH 34 | 35 | Okay, so you have decided on the proper branch. Create a feature branch 36 | and start hacking: 37 | 38 | ``` 39 | $ git checkout -b my-feature-branch 40 | ``` 41 | 42 | 43 | ### COMMIT 44 | 45 | Make sure git knows your name and email address: 46 | 47 | ``` 48 | $ git config --global user.name "J. Random User" 49 | $ git config --global user.email "j.random.user@example.com" 50 | ``` 51 | 52 | Writing good commit logs is important. A commit log should describe what 53 | changed and why. Follow these guidelines when writing one: 54 | 55 | 1. The first line should be 50 characters or less and contain a short 56 | description of the change prefixed with the name of the changed 57 | subsystem (e.g. "net: add localAddress and localPort to Socket"). 58 | 2. Keep the second line blank. 59 | 3. Wrap all other lines at 72 columns. 60 | 61 | A good commit log looks like this: 62 | 63 | ``` 64 | fix: explaining the commit in one line 65 | 66 | Body of commit message is a few lines of text, explaining things 67 | in more detail, possibly giving some background about the issue 68 | being fixed, etc etc. 69 | 70 | The body of the commit message can be several paragraphs, and 71 | please do proper word-wrap and keep columns shorter than about 72 | 72 characters or so. That way `git log` will show things 73 | nicely even when it is indented. 74 | ``` 75 | 76 | The header line should be meaningful; it is what other people see when they 77 | run `git shortlog` or `git log --oneline`. 78 | 79 | Check the output of `git log --oneline files_that_you_changed` to find out 80 | what directories your changes touch. 81 | 82 | 83 | ### REBASE 84 | 85 | Use `git rebase` (not `git merge`) to sync your work from time to time. 86 | 87 | ``` 88 | $ git fetch upstream 89 | $ git rebase upstream/v0.1 # or upstream/master 90 | ``` 91 | 92 | 93 | ### TEST 94 | 95 | Bug fixes and features should come with tests. Add your tests in the 96 | test directory. This varies by repository but often follows the same convention of /src/test. Look at other tests to see how they should be 97 | structured (license boilerplate, common includes, etc.). 98 | 99 | 100 | Make sure that all tests pass. 101 | 102 | 103 | ### PUSH 104 | 105 | ``` 106 | $ git push origin my-feature-branch 107 | ``` 108 | 109 | Go to https://github.com/username/azure-activedirectory-library-for-***.git and select your feature branch. Click 110 | the 'Pull Request' button and fill out the form. 111 | 112 | Pull requests are usually reviewed within a few days. If there are comments 113 | to address, apply your changes in a separate commit and push that to your 114 | feature branch. Post a comment in the pull request afterwards; GitHub does 115 | not send out notifications when you add commits. 116 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "passport-azure-ad-login-oidc", 3 | "version": "0.0.1", 4 | "dependencies": { 5 | "assert-plus": "*", 6 | "body-parser": "^1.15.2", 7 | "bunyan": "*", 8 | "connect-mongo": "^1.3.2", 9 | "cookie-parser": "^1.4.3", 10 | "ejs": ">= 0.0.0", 11 | "ejs-locals": ">= 0.0.0", 12 | "express": "3.x", 13 | "express-session": "^1.14.1", 14 | "method-override": "^2.3.6", 15 | "mongoose": "^4.6.2", 16 | "passport": "*", 17 | "passport-azure-ad": "^3.0.6" 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /routes/index.js: -------------------------------------------------------------------------------- 1 | /* 2 | * GET home page. 3 | */ 4 | 5 | exports.index = function(req, res){ 6 | res.render('index', { title: 'Express' }); 7 | }; 8 | -------------------------------------------------------------------------------- /routes/user.js: -------------------------------------------------------------------------------- 1 | 2 | /* 3 | * GET users listing. 4 | */ 5 | 6 | exports.list = function(req, res){ 7 | res.send("respond with a resource"); 8 | }; -------------------------------------------------------------------------------- /views/account.ejs: -------------------------------------------------------------------------------- 1 | <% if (!user) { %> 2 |

Welcome! Please log in.

3 | Log In 4 | <% } else { %> 5 |

UPN: <%= user._json.upn %>

6 |

Profile ID: <%= user.id %>

7 |

Full Claimes

8 | <%- JSON.stringify(user) %> 9 |

10 | Log Out 11 | <% } %> 12 | -------------------------------------------------------------------------------- /views/index.ejs: -------------------------------------------------------------------------------- 1 | <% if (!user) { %> 2 |

Welcome! Please log in.

3 | Sign In 4 | Sign Up 5 | Update Profile 6 | Reset Password 7 | <% } else { %> 8 |

Hello, <%= user.emails[0] %>.

9 | Account Info
10 | Log Out 11 | <% } %> 12 | -------------------------------------------------------------------------------- /views/layout.ejs: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Passport-OpenID Example 5 | 6 | 7 | <% if (!user) { %> 8 |

9 | Home | 10 | Log In 11 |

12 | <% } else { %> 13 |

14 | Home | 15 | Account | 16 | Log Out 17 |

18 | <% } %> 19 | <%- body %> 20 | 21 | 22 | --------------------------------------------------------------------------------