├── .github └── workflows │ └── semgrep.yml ├── .gitignore ├── LICENSE ├── README.md ├── lib └── passport-azure-ad-oauth2 │ ├── index.js │ └── strategy.js ├── opslevel.yml ├── package.json └── test ├── module.test.js └── strategy.test.js /.github/workflows/semgrep.yml: -------------------------------------------------------------------------------- 1 | name: Semgrep 2 | on: 3 | pull_request_target: {} 4 | push: 5 | branches: ["master"] 6 | jobs: 7 | semgrep: 8 | name: Scan 9 | runs-on: ubuntu-latest 10 | if: (github.actor != 'dependabot[bot]' && github.actor != 'snyk-bot') 11 | steps: 12 | - uses: actions/checkout@v2 13 | - uses: returntocorp/semgrep-action@v1 14 | with: 15 | publishToken: ${{ secrets.SEMGREP_APP_TOKEN }} 16 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | node_modules 3 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2015 Auth0, Inc. (http://auth0.com) 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 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Passport-azure-ad-oauth2 2 | 3 | [Passport](http://passportjs.org/) strategy for authenticating with [Azure AD](http://msdn.microsoft.com/en-us/library/azure/dn645545.aspx) 4 | using the OAuth 2.0 protocol. 5 | 6 | ## Install 7 | 8 | $ npm install passport-azure-ad-oauth2 9 | 10 | ## Usage 11 | 12 | #### Configure Strategy 13 | 14 | The Azure AD OAuth 2.0 authentication strategy authenticates requests by delegating to Azure AD using the OAuth 2.0 protocol. 15 | 16 | Applications must supply a `verify` callback which accepts an `accessToken`, `refresh_token`, `params` and service-specific `profile`, and then calls the `done` callback supplying a `user`, which should be set to `false` if the credentials are not valid. If an exception occured, `err` should be set. 17 | 18 | ##### Options 19 | 20 | * `clientID`: specifies the client id of the application that is registered in Azure Active Directory. 21 | * `clientSecret`: secret used to establish ownership of the client Id. 22 | * `callbackURL`: URL to which Azure AD will redirect the user after obtaining authorization. 23 | * `resource`: [optional] the App ID URI of the web API (secured resource). 24 | * `tenant`: [optional] tenant domain (e.g.: contoso.onmicrosoft.com). 25 | * `useCommonEndpoint`: [optional] use "https://login.microsoftonline.com/common" instead of default endpoint (https://login.microsoftonline.com/{tenant}). This is typically enabled if you're using this for a Multi-tenant application in Azure AD (Default: `false`). 26 | 27 | ```javascript 28 | passport.use(new AzureAdOAuth2Strategy({ 29 | clientID: '{YOUR_CLIENT_ID}', 30 | clientSecret: '{YOUR_CLIENT_SECRET}', 31 | callbackURL: 'https://www.example.net/auth/azureadoauth2/callback', 32 | resource: '00000002-0000-0000-c000-000000000000', 33 | tenant: 'contoso.onmicrosoft.com' 34 | }, 35 | function (accessToken, refresh_token, params, profile, done) { 36 | // currently we can't find a way to exchange access token by user info (see userProfile implementation), so 37 | // you will need a jwt-package like https://github.com/auth0/node-jsonwebtoken to decode id_token and get waad profile 38 | var waadProfile = profile || jwt.decode(params.id_token); 39 | 40 | // this is just an example: here you would provide a model *User* with the function *findOrCreate* 41 | User.findOrCreate({ id: waadProfile.upn }, function (err, user) { 42 | done(err, user); 43 | }); 44 | })); 45 | ``` 46 | 47 | #### Authenticate Requests 48 | 49 | Use `passport.authenticate()`, specifying the `'azure_ad_oauth2'` strategy, to authenticate requests. 50 | 51 | For example, as route middleware in an [Express](http://expressjs.com/) application: 52 | 53 | ```javascript 54 | app.get('/auth/azureadoauth2', 55 | passport.authenticate('azure_ad_oauth2')); 56 | 57 | app.get('/auth/azureadoauth2/callback', 58 | passport.authenticate('azure_ad_oauth2', { failureRedirect: '/login' }), 59 | function (req, res) { 60 | // Successful authentication, redirect home. 61 | res.redirect('/'); 62 | }); 63 | ``` 64 | 65 | ## Tests 66 | 67 | $ npm install 68 | $ npm test 69 | 70 | ## Issue Reporting 71 | 72 | If you have found a bug or if you have a feature request, please report them at this repository issues section. Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://auth0.com/whitehat) details the procedure for disclosing security issues. 73 | 74 | ## Credits 75 | 76 | - [Jared Hanson](http://github.com/jaredhanson) 77 | - [Azure AD Team](https://github.com/AzureAD/azure-activedirectory-library-for-nodejs) 78 | 79 | ## Issue Reporting 80 | 81 | If you have found a bug or if you have a feature request, please report them at this repository issues section. Please do not report security vulnerabilities on the public GitHub issue tracker. The [Responsible Disclosure Program](https://auth0.com/whitehat) details the procedure for disclosing security issues. 82 | 83 | ## Author 84 | 85 | [Auth0](auth0.com) 86 | 87 | ## License 88 | 89 | This project is licensed under the MIT license. See the [LICENSE](LICENSE) file for more info. 90 | -------------------------------------------------------------------------------- /lib/passport-azure-ad-oauth2/index.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | var Strategy = require('./strategy'); 5 | 6 | 7 | /** 8 | * Expose `Strategy` directly from package. 9 | */ 10 | exports = module.exports = Strategy; 11 | 12 | /** 13 | * Export constructors. 14 | */ 15 | exports.Strategy = Strategy; 16 | -------------------------------------------------------------------------------- /lib/passport-azure-ad-oauth2/strategy.js: -------------------------------------------------------------------------------- 1 | /** 2 | * Module dependencies. 3 | */ 4 | var util = require('util') 5 | , OAuth2Strategy = require('passport-oauth').OAuth2Strategy 6 | , InternalOAuthError = require('passport-oauth').InternalOAuthError; 7 | 8 | 9 | /** 10 | * `Strategy` constructor. 11 | * 12 | * The Azure AD OAuth 2.0 authentication strategy authenticates requests by delegating to 13 | * Azure AD using the OAuth 2.0 protocol. 14 | * 15 | * Applications must supply a `verify` callback which accepts an `accessToken`, 16 | * `refresh_token`, `params` and service-specific `profile`, and then calls the `done` 17 | * callback supplying a `user`, which should be set to `false` if the 18 | * credentials are not valid. If an exception occured, `err` should be set. 19 | * 20 | * Options: 21 | * - `clientID` specifies the client id of the application that is registered in Azure Active Directory 22 | * - `clientSecret` secret used to establish ownership of the client Id 23 | * - `callbackURL` URL to which Azure AD will redirect the user after obtaining authorization 24 | * - `resource` [optional] the App ID URI of the web API (secured resource) 25 | * - `tenant` [optional] tenant domain (e.g.: contoso.onmicrosoft.com) 26 | * - `useCommonEndpoint` [optional] Use "https://login.microsoftonline.com/common" instead of default endpoint (https://login.microsoftonline.com/{your_domain}) 27 | * 28 | * Examples: 29 | * 30 | * passport.use(new AzureAdOAuth2Strategy({ 31 | * clientID: 'yourClientId', 32 | * clientSecret: 'youAADIssuedClientSecret', 33 | * callbackURL: 'https://www.example.net/auth/azureadoauth2/callback', 34 | * resource: '00000002-0000-0000-c000-000000000000', 35 | * tenant: 'contoso.onmicrosoft.com' 36 | * }, 37 | * function (accessToken, refresh_token, params, profile, done) { 38 | * var waadProfile = profile || jwt.decode(params.id_token, '', true); 39 | * 40 | * User.findOrCreate({ id: waadProfile.upn }, function (err, user) { 41 | * done(err, user); 42 | * }); 43 | * } 44 | * )); 45 | * 46 | * @param {Object} options 47 | * @param {Function} verify 48 | * @api public 49 | */ 50 | function Strategy (options, verify) { 51 | // more details: http://msdn.microsoft.com/en-us/library/azure/dn645542.aspx 52 | options = options || {}; 53 | 54 | var base_url = 'https://login.microsoftonline.com/' + ((!options.useCommonEndpoint && options.tenant) || 'common'); 55 | options.authorizationURL = options.authorizationURL || base_url + '/oauth2/authorize'; 56 | options.tokenURL = options.tokenURL || base_url + '/oauth2/token'; 57 | 58 | OAuth2Strategy.call(this, options, verify); 59 | 60 | this.name = 'azure_ad_oauth2'; 61 | this.resource = options.resource; 62 | } 63 | 64 | /** 65 | * Inherit from `OAuth2Strategy`. 66 | */ 67 | util.inherits(Strategy, OAuth2Strategy); 68 | 69 | /** 70 | * Authenticate request by delegating to Azure AD using OAuth. 71 | * 72 | * @param {Object} req 73 | * @api protected 74 | */ 75 | Strategy.prototype.authenticate = function (req, options) { 76 | if (!options.resource && this.resource) { // include default resource as authorization parameter 77 | options.resource = this.resource; 78 | } 79 | 80 | // Call the base class for standard OAuth authentication. 81 | OAuth2Strategy.prototype.authenticate.call(this, req, options); 82 | }; 83 | 84 | /** 85 | * Retrieve user profile from Azure AD. 86 | * 87 | * This function constructs a normalized profile, with the following properties: 88 | * 89 | * - `provider` always set to `azure_ad_oauth2` 90 | * - `id` 91 | * - `username` 92 | * - `displayName` 93 | * 94 | * @param {String} accessToken 95 | * @param {Function} done 96 | * @api protected 97 | */ 98 | Strategy.prototype.userProfile = function (accessToken, done) { 99 | // waad provides user profile information in the id_token response (params.id_token). 100 | done(null, { provider: 'azure_ad_oauth2' }); 101 | 102 | /*this._oauth2.get('https://login.microsoftonline.com/{XYZ}/openid/userinfo', accessToken, function (err, body, res) { 103 | if (err) { return done(new InternalOAuthError('failed to fetch user profile', err)); } 104 | });*/ 105 | }; 106 | 107 | /** 108 | * Return extra Azure AD-specific parameters to be included in the authorization 109 | * request. 110 | * 111 | * @param {Object} options 112 | * @return {Object} 113 | * @api protected 114 | */ 115 | Strategy.prototype.authorizationParams = function (options) { 116 | return options; 117 | }; 118 | 119 | /** 120 | * Expose `Strategy`. 121 | */ 122 | module.exports = Strategy; 123 | -------------------------------------------------------------------------------- /opslevel.yml: -------------------------------------------------------------------------------- 1 | --- 2 | version: 1 3 | repository: 4 | owner: iam_federations 5 | tags: 6 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "passport-azure-ad-oauth2", 3 | "version": "0.1.0", 4 | "description": "Passport strategy for authenticating with Azure AD using the OAuth 2.0 protocol.", 5 | "keywords": [ 6 | "passport", 7 | "waad", 8 | "oauth", 9 | "azure active directory", 10 | "azure ad", 11 | "auth", 12 | "authn", 13 | "authentication", 14 | "identity" 15 | ], 16 | "repository": { 17 | "type": "git", 18 | "url": "git://github.com/auth0/passport-azure-ad-oauth2.git" 19 | }, 20 | "bugs": { 21 | "url": "http://github.com/auth0/passport-azure-ad-oauth2/issues" 22 | }, 23 | "author": { 24 | "name": "Auth0", 25 | "email": "support@auth0.com", 26 | "url": "http://www.auth0.com/" 27 | }, 28 | "licenses": [ 29 | { 30 | "type": "MIT", 31 | "url": "http://www.opensource.org/licenses/MIT" 32 | } 33 | ], 34 | "main": "./lib/passport-azure-ad-oauth2", 35 | "dependencies": { 36 | "passport-oauth": "1.0.x" 37 | }, 38 | "devDependencies": { 39 | "mocha": "1.x.x", 40 | "chai": "1.x.x" 41 | }, 42 | "scripts": { 43 | "test": "node_modules/.bin/mocha --reporter spec test/*.test.js" 44 | }, 45 | "homepage": "https://github.com/auth0/passport-azure-ad-oauth2" 46 | } 47 | -------------------------------------------------------------------------------- /test/module.test.js: -------------------------------------------------------------------------------- 1 | var strategy = require('..'); 2 | var expect = require('chai').expect; 3 | 4 | describe('passport-azure-ad-oauth2', function () { 5 | 6 | it('should export Strategy constructor directly from package', function () { 7 | expect(strategy).to.be.a('function'); 8 | expect(strategy).to.equal(strategy.Strategy); 9 | }); 10 | 11 | it('should export Strategy constructor', function () { 12 | expect(strategy.Strategy).to.be.a('function'); 13 | }); 14 | }); 15 | -------------------------------------------------------------------------------- /test/strategy.test.js: -------------------------------------------------------------------------------- 1 | var expect = require('chai').expect; 2 | var AzureAdOAuth2Strategy = require('..'); 3 | 4 | describe('Strategy', function() { 5 | 6 | var strategy = new AzureAdOAuth2Strategy({ 7 | clientID: 'abc123', 8 | clientSecret: 'shhh', 9 | callbackURL: 'https://www.example.net/auth/azureadoauth2/callback', 10 | resource: '00000002-0000-0000-c000-000000000000', 11 | tenant: 'contoso.onmicrosoft.com' 12 | }, 13 | function () {}); 14 | 15 | it('should be named azure_ad_oauth2', function () { 16 | expect(strategy.name).to.equal('azure_ad_oauth2'); 17 | }); 18 | }); 19 | --------------------------------------------------------------------------------