UNPKG

passport-azure-ad

Version:

OIDC and Bearer Passport strategies for Azure Active Directory

416 lines (345 loc) 23.1 kB
/** * Copyright (c) Microsoft Corporation * All Rights Reserved * MIT License * * Permission is hereby granted, free of charge, to any person obtaining a copy of this * software and associated documentation files (the 'Software'), to deal in the Software * without restriction, including without limitation the rights to use, copy, modify, * merge, publish, distribute, sublicense, and/or sell copies of the Software, and to * permit persons to whom the Software is furnished to do so, subject to the following * conditions: * * The above copyright notice and this permission notice shall be * included in all copies or substantial portions of the Software. * * THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND, * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS * OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, * WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT * OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. */ 'use strict'; /* eslint no-underscore-dangle: 0 */ var chai = require('chai'); var url = require('url'); chai.use(require('chai-passport-strategy')); const TEST_TIMEOUT = 1000000; // 1000 seconds var Metadata = require('../../lib/metadata').Metadata; var OIDCStrategy = require('../../lib/index').OIDCStrategy; var OAuth2 = require('../../node_modules/oauth/index').OAuth2; var nonce = 'S2e+BeZvlK5mM2sQ'; var code = 'AQABAAAAAADRNYRQ3dhRSrm-4K-adpCJP_Q38QU-rrL8zGsv9WPZWE5W1tKo261T4pqLRklNO0VPKfDkkMVzgfR6vth8MvQUl4Ie89eg9mMlxQtuWAzJ6Ig6rJu-0cQyRYzV3F1qvmg6GUfnUK2zWkyHlqPuuC7XPgZxsVmOBuMpJSxrOdxeNiFlmYp3iYzuJ7HPKEG2gMxM4CTupcGU6idWDuQYJkBml4tvUtG-HAFNrNVTFlpCMyUVrwYJQGwp_kYtHLSF6nn9dO4hRVqV5t2n5ZiZtpiltEUlQsAnrFUKHD0VB7s8UgL7bgi74bCcHOHaFQCz4ofO6aX4JVKukQUYRjhVU_cOpqP_3f_DvqVbaZo-LDO5l-3A16OJI3JU4A1aoljgh9Q7r0wWcfmYX5rWR20OPkGmaVmbg-jdAqY7aQS-CkFotuZDrV4MZtv9pBCNMzLfaOsbhES_SKhtBVT8Ui2vNA4uNrYGnsCBTHxrXNBH7x2VP3bUceQcS60k-7rTfcvrkXR__SG7jeFjLeHNbVes7hfGHFjXZ2WjfYwNSXwp_RYdEXe03_iN7Phvj-F3JDbIgO5JyT6p8rHFg-RDHRsB1nIqf5-Z6s54YfYVy3J0NWOcJGoyk5xP91gfnXGqmg8wMNw7aXetaC4vN4DA63oMHYPn-Ckt8IO7rZnlzVVSYmHWLcd2Pj82KoiCrFmMMJ66X4IlpP6UtZkEejaqGbYfAseV8wc1ub342xp5LgjESvLTViAA'; var id_token_in_auth_resp = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IlliUkFRUlljRV9tb3RXVkpLSHJ3TEJiZF85cyIsImtpZCI6IlliUkFRUlljRV9tb3RXVkpLSHJ3TEJiZF85cyJ9.eyJhdWQiOiIyYWJmM2E1Mi03ZDg2LTQ2MGItYTFlZi03N2RjNDNkZThhYWQiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC8yNjhkYTFhMS05ZGI0LTQ4YjktYjFmZS02ODMyNTBiYTkwY2MvIiwiaWF0IjoxNDcxNDY4MTY2LCJuYmYiOjE0NzE0NjgxNjYsImV4cCI6MTQ3MTQ3MjA2NiwiYW1yIjpbInB3ZCJdLCJjX2hhc2giOiJpb2FNU1h6MXFhOTdOMlZoUmVmSzVRIiwiZmFtaWx5X25hbWUiOiJvbmUiLCJnaXZlbl9uYW1lIjoicm9ib3QiLCJpcGFkZHIiOiIxNjcuMjIwLjEuMTYwIiwibmFtZSI6InJvYm90IDEiLCJub25jZSI6IlMyZStCZVp2bEs1bU0yc1EiLCJvaWQiOiI3OTEyZmU3Yi1iNWFiLTQyNWItYmIxZi0wZTgzYjk5ZmNhN2YiLCJwd2RfZXhwIjoiNDI5OTIzIiwicHdkX3VybCI6Imh0dHBzOi8vcG9ydGFsLm1pY3Jvc29mdG9ubGluZS5jb20vQ2hhbmdlUGFzc3dvcmQuYXNweCIsInN1YiI6IjFKTWR6T3hKeVdlQ29jNVFzWXZEUGNvWnRxaHVXSVp4Z21IbkN6UVRYYVEiLCJ0aWQiOiIyNjhkYTFhMS05ZGI0LTQ4YjktYjFmZS02ODMyNTBiYTkwY2MiLCJ1bmlxdWVfbmFtZSI6InJvYm90QHNpanVuLm9ubWljcm9zb2Z0LmNvbSIsInVwbiI6InJvYm90QHNpanVuLm9ubWljcm9zb2Z0LmNvbSIsInZlciI6IjEuMCJ9.NnYMS-lTTgSCFb69IwnoTpCap-4DCUVindUvrvHHOUlS7iHqDC-gzOvAvxwj5MzhE4TOCwkx0smhl1X7ZouiX0gJAmPbge4nlyTRMY7AaaY17xtHcLW8NWzixJX7hDBlV7FJp-GFD03rxTcK4uUzSbFJSnKtotsDpnVfnGP5Tmr4R9pgI8Xs-j2INOD52REAgQxAILqABUojRuD4vt77JzJdasmcZqf0OkZIWUWGK0_1u_pZVS6DwzscpA-E-om8im3S6VXkeygDVvGawxELasM04zOuhw6P3_GM1tCFfq3P-4zBYYA0HSn3g_tkjzZCAuQEsaF4vvjhLNDw72A7dw'; var id_token_in_token_resp = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IlliUkFRUlljRV9tb3RXVkpLSHJ3TEJiZF85cyIsImtpZCI6IlliUkFRUlljRV9tb3RXVkpLSHJ3TEJiZF85cyJ9.eyJhdWQiOiIyYWJmM2E1Mi03ZDg2LTQ2MGItYTFlZi03N2RjNDNkZThhYWQiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC8yNjhkYTFhMS05ZGI0LTQ4YjktYjFmZS02ODMyNTBiYTkwY2MvIiwiaWF0IjoxNDcxNDY4MjI4LCJuYmYiOjE0NzE0NjgyMjgsImV4cCI6MTQ3MTQ3MjEyOCwiYW1yIjpbInB3ZCJdLCJmYW1pbHlfbmFtZSI6Im9uZSIsImdpdmVuX25hbWUiOiJyb2JvdCIsImlwYWRkciI6IjE2Ny4yMjAuMC4xNjAiLCJuYW1lIjoicm9ib3QgMSIsIm5vbmNlIjoiUzJlK0JlWnZsSzVtTTJzUSIsIm9pZCI6Ijc5MTJmZTdiLWI1YWItNDI1Yi1iYjFmLTBlODNiOTlmY2E3ZiIsInB3ZF9leHAiOiI0Mjk4NjEiLCJwd2RfdXJsIjoiaHR0cHM6Ly9wb3J0YWwubWljcm9zb2Z0b25saW5lLmNvbS9DaGFuZ2VQYXNzd29yZC5hc3B4Iiwic3ViIjoiMUpNZHpPeEp5V2VDb2M1UXNZdkRQY29adHFodVdJWnhnbUhuQ3pRVFhhUSIsInRpZCI6IjI2OGRhMWExLTlkYjQtNDhiOS1iMWZlLTY4MzI1MGJhOTBjYyIsInVuaXF1ZV9uYW1lIjoicm9ib3RAc2lqdW4ub25taWNyb3NvZnQuY29tIiwidXBuIjoicm9ib3RAc2lqdW4ub25taWNyb3NvZnQuY29tIiwidmVyIjoiMS4wIn0.HI7-g9-vRcpOXxkVOHXe42-NzIoHEwJohIRFaOScEj4uKYetnflIELbWrIxzVWw1U6p5n-Py6PcduR6xJ5r5R5YNbDvc-cc5XN3xfD3FiZjDRxgcYuLVIkSMK8y1BqG3suHurs09ijS7OKI4WHBDKbDKaCmOC4SILgplaHBD0O0Dmg5nc2WaZVi2A1kFDHoscKBNRKEcq4LOnRAgF85R3lrW_fCSIgIPgi-tQ0xDaSmXO3TO4E1Jw5rfgbySebyaJrb1GPZSacA4W4rrKFYJ_1XFJxcOGvGhjLzvV1yInxQgw5XPecVg4cT2wPTPwOVWcvuE496nv6i1YDetSr0ndA'; var id_token_in_auth_resp_wrong_signature = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IlliUkFRUlljRV9tb3RXVkpLSHJ3TEJiZF85cyIsImtpZCI6IlliUkFRUlljRV9tb3RXVkpLSHJ3TEJiZF85cyJ9.eyJhdWQiOiIyYWJmM2E1Mi03ZDg2LTQ2MGItYTFlZi03N2RjNDNkZThhYWQiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC8yNjhkYTFhMS05ZGI0LTQ4YjktYjFmZS02ODMyNTBiYTkwY2MvIiwiaWF0IjoxNDcxNDY4MTY2LCJuYmYiOjE0NzE0NjgxNjYsImV4cCI6MTQ3MTQ3MjA2NiwiYW1yIjpbInB3ZCJdLCJjX2hhc2giOiJpb2FNU1h6MXFhOTdOMlZoUmVmSzVRIiwiZmFtaWx5X25hbWUiOiJvbmUiLCJnaXZlbl9uYW1lIjoicm9ib3QiLCJpcGFkZHIiOiIxNjcuMjIwLjEuMTYwIiwibmFtZSI6InJvYm90IDEiLCJub25jZSI6IlMyZStCZVp2bEs1bU0yc1EiLCJvaWQiOiI3OTEyZmU3Yi1iNWFiLTQyNWItYmIxZi0wZTgzYjk5ZmNhN2YiLCJwd2RfZXhwIjoiNDI5OTIzIiwicHdkX3VybCI6Imh0dHBzOi8vcG9ydGFsLm1pY3Jvc29mdG9ubGluZS5jb20vQ2hhbmdlUGFzc3dvcmQuYXNweCIsInN1YiI6IjFKTWR6T3hKeVdlQ29jNVFzWXZEUGNvWnRxaHVXSVp4Z21IbkN6UVRYYVEiLCJ0aWQiOiIyNjhkYTFhMS05ZGI0LTQ4YjktYjFmZS02ODMyNTBiYTkwY2MiLCJ1bmlxdWVfbmFtZSI6InJvYm90QHNpanVuLm9ubWljcm9zb2Z0LmNvbSIsInVwbiI6InJvYm90QHNpanVuLm9ubWljcm9zb2Z0LmNvbSIsInZlciI6IjEuMCJ9.NnYMS-lTTgSCFb69IwnoTpCap-4DCUVindUvrvHHOUlS7iHqDC-gzOvAvxwj5MzhE4TOCwkx0smhl1X7ZouiX0gJAmPbge4nlyTRMY7AaaY17xtHcLW8NWzixJX7hDBlV7FJp-GFD03rxTcK4uUzSbFJSnKtotsDpnVfnGP5Tmr4R9pgI8Xs-j2INOD52REAgQxAILqABUojRuD4vt77JzJdasmcZqf0OkZIWUWGK0_1u_pZVS6DwzscpA-E-om8im3S6VXkeygDVvGawxELasM04zOuhw6P3_GM1tCFfq3P-4zBYYA0HSn3g_tkjzZCAuQEsaF4vvjhLNDw72A7dW'; var id_token_in_token_resp_wrong_sub = 'eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsIng1dCI6IlliUkFRUlljRV9tb3RXVkpLSHJ3TEJiZF85cyIsImtpZCI6IlliUkFRUlljRV9tb3RXVkpLSHJ3TEJiZF85cyJ9.eyJhdWQiOiIyYWJmM2E1Mi03ZDg2LTQ2MGItYTFlZi03N2RjNDNkZThhYWQiLCJpc3MiOiJodHRwczovL3N0cy53aW5kb3dzLm5ldC8yNjhkYTFhMS05ZGI0LTQ4YjktYjFmZS02ODMyNTBiYTkwY2MvIiwiaWF0IjoxNDcxNDg2MDY3LCJuYmYiOjE0NzE0ODYwNjcsImV4cCI6MTQ3MTQ4OTk2NywiYW1yIjpbInB3ZCJdLCJnaXZlbl9uYW1lIjoicm9ib3QzIiwiaXBhZGRyIjoiMTY3LjIyMC4xLjE2MCIsIm5hbWUiOiJyb2JvdDMiLCJub25jZSI6IlMyZStCZVp2bEs1bU0yc1EiLCJvaWQiOiI0ZTRjMjFjZi0zNTU5LTQ5MDEtYjRiYi03OWYzMDQyMWYyMzgiLCJzdWIiOiJQZG45TlNGZkQ5ZHdnXzdVeXQyY01PQ21kaWYyWG55RW1IUFFONVE0SjNBIiwidGlkIjoiMjY4ZGExYTEtOWRiNC00OGI5LWIxZmUtNjgzMjUwYmE5MGNjIiwidW5pcXVlX25hbWUiOiJyb2JvdDNAc2lqdW4ub25taWNyb3NvZnQuY29tIiwidXBuIjoicm9ib3QzQHNpanVuLm9ubWljcm9zb2Z0LmNvbSIsInZlciI6IjEuMCJ9.tbYOLL_X77wpEermfk7U-VYmcHpnColCtUoRjaIUOUSG3o3x2GrjWqBwrj6u69Vjxgv7dAcFGebNhVFSD3MlXzf0n5kd7s5zs9-g3MTxcABlHvPn2vwNEXK8vkVQkrEeMsKAXc1xm-3w6Uh2bCnlMzOMJXqrxpzsmAOUgzJT6xAPzZZHKmMdlt8Q-kVm2lKQbcg8TuUre6e9CYIUtGi1mKsdgD8W6hVllkgyNxzhHKZp_LgRpn6y0wiz5JDPhuhv7iRsUVk5_R6lpH2cuuDcZqYvGBGkwnbLR9Kin-bh4t9ses03uPn-o8gZjxgPE0yQXoPlgw06Yu6o94L3Uo4BIQ'; var access_token = 'AQABAAAAAADRNYRQ3dhRSrm-4K-adpCJ6KNePaAkgUzyI8wn5NTUSkfVlY2ZiKFn1vF-kA7G1cguxn_guaoE4Oj7sCCXBRPB-q0ROQEsAfAJIelDFcbOryCJyIm1Rgk9XDJMcol3hio2J5Q0_ja2D2xY3o24Ny9jhdUe5L-zR6MChyew9K_l90i7IRhFJXHS8pySbRk8HUEitiYklC7emsudlHWnLZTBYRwqvW7CI7HUKL89BECM23291FZ98pEc12MmN6rRTCfsFkh7H37H7uBJNrviBO7MOnS5J1KWmQrH1Amn0wmuL1KYpOFB6Drl730jguN4VZjNKGN14GRdqJvmnvN6zTsnCtxs9xpqd0fBO-v9fIdpJfx8i75D0ule_jIIe8BffL2V0n_aJHw74aAjFcHpoS_WSVzOL121UiSygS4XrBtttSgqUEeaVoCp3k0JkXdZicSezu7sMqH27jxdyBJeDsGiIqXtRGMTObLu_lCsqC-WB_tchEUeaketerawPudhvXQKrjBs4RlzrizCEBKdl1M7IAA'; var refresh_token = 'AQABAAAAAADRNYRQ3dhRSrm-4K-adpCJx5jvcZSRjvpKPiMc2qvkrT6brLnmLUHEgB9AQyp1doHbd1cyQQduFg6lSpO4nIrIANpnzo1E0-hRV99jhOxAm82QnUnlYrQ1nLLUFrQ9QR5KY0YRSTMltM3gy8H84qGJHX75SJjhYWRTIH9ha1XvfMp2oWxfQjdhrwqfYgfa2u180e3xbSJM0wnkETiEpeIV20hNelt6vI_-BbHceXmD29SElixZl1QLohpjIUBhlz2FO0bYDqvJ9XOM_30zrCWCMXVD1hd4onAboNR6px6W2w8Olc0TNJ-0Dz8yQf1UixnJx6bqKZeWoZhq6A4UqqlwY3ExTVeX9qiS6k4UQO-q9NJyA0MagGBlFAwgxXEWnc1rQu6it-ZCtKfKD480kyOaK_M0XYFhgc5jTWzqOIx4Vc7L32yfh5ZcHSlL5eZccxbBDSN3TNsHDKohrtd-GYst98MXUWc-bHBGfDNaRKRjpRe2pQ7sWSdcOq7qovbdkjrmhArQsnIupSxRgTbXuu5TeU21zB25rosnhgCga-JAsrFJTm6gOKmlcPJF7nuLV3NE2DcliMh4QgTStDJ7tSuP_94voiCQ0zYUhnejRRpTrza5kUVqQs0s7hZwKgYs6TM-4nrEaGi9RgObvgJAIrljYzN8s2JYu1l27OWrSzEbtYGx4uroeuJhlCn_dobsgmwgAA'; var userInfoURL = 'https://login.microsoftonline.com/268da1a1-9db4-48b9-b1fe-683250ba90cc/openid/userinfo?schema=openid'; // Mock the process of getting PEMkey var PEMkey = "-----BEGIN RSA PUBLIC KEY-----\n\ MIIBCgKCAQEAvbcFrj193Gm6zeo5e2/y54Jx49sIgScv+2JO+n6NxNqQaKVnMkHc\n\ z+S1j2FfpFngotwGMzZIKVCY1SK8SKZMFfRTU3wvToZITwf3W1Qq6n+h+abqpyJT\n\ aqIcfhA0d6kEAM5NsQAKhfvw7fre1QicmU9LWVWUYAayLmiRX6o3tktJq6H58pUz\n\ Ttx/D0Dprnx6z5sW+uiMipLXbrgYmOez7htokJVgDg8w+yDFCxZNo7KVueUkLkxh\n\ NjYGkGfnt18s7ZW036WoTmdaQmW4CChf/o4TLE5VyGpYWm7I/+nV95BBvwlzokVV\n\ KzveKf3l5UU3c6PkGy+BB3E/ChqFm6sPWwIDAQAB\n\ -----END RSA PUBLIC KEY-----\n\ "; /* * test strategy (for response_type = 'id_token') which checks the expiration of id_token */ var options = { redirectUrl: 'https://localhost:3000/auth/openid/return', clientID: '2abf3a52-7d86-460b-a1ef-77dc43de8aad', clientSecret: 'secret', identityMetadata: 'https://login.microsoftonline.com/sijun.onmicrosoft.com/.well-known/openid-configuration', responseType: 'id_token code', responseMode: 'form_post', validateIssuer: true, passReqToCallback: false, sessionKey: 'my_key', oidcIssuer: 'https://sts.windows.net/268da1a1-9db4-48b9-b1fe-683250ba90cc/', ignoreExpiration: true, audience: null, algorithms: ['RS256'] }; // functions used to change the fields in options var setIgnoreExpirationFalse = function(options) { options.ignoreExpiration = false; }; var setWrongIssuer = function(options) { options.issuer = 'wrong_issuer'; }; var rmValidateIssuer = function(options) { options.validateIssuer = undefined; }; var setWrongAudience = function(options) { options.audience = 'wrong_audience'; }; var testStrategy = new OIDCStrategy(options, function(profile, done) { done(null, profile.upn); }); // mock the userinfo endpoint response var setUserInfoResponse = function(sub_choice) { if (sub_choice === 'use_valid_sub') { return () => { OAuth2.prototype.get = function(url, access_token, callback) { var userInfoBody = '{"sub":"1JMdzOxJyWeCoc5QsYvDPcoZtqhuWIZxgmHnCzQTXaQ", "upn":"robot@sijun.onmicrosoft.com"}'; callback(null, userInfoBody); }; }; } else { return () => { OAuth2.prototype.get = function(url, access_token, callback) { var userInfoBody = '{"sub":"invalid sub", "upn":"robot@sijun.onmicrosoft.com"}'; callback(null, userInfoBody); }; }; } }; // mock the token response we want when we consume the code var setTokenResponse = function(id_token_in_token_resp, access_token_in_token_resp) { return () => { testStrategy._getAccessTokenBySecretOrAssertion = function(code, oauthConfig, next, callback) { var params = { 'id_token': id_token_in_token_resp, 'token_type': 'Bearer', 'access_token': access_token_in_token_resp, 'refresh_token': refresh_token }; callback(null, params); } }; }; // used to remember the error message in self.fail var challenge; // used to remember the 'user' when we successfully log in var user; // mock the request (resulting request from the 302 redirection response) by putting // id_token and code into the body, and state and nonce into the session var setReqFromAuthRespRedirect = function(id_token_in_auth_resp, code_in_auth_resp, nonce_to_use, action) { return function(done) { // Mock `setOptions` testStrategy.setOptions = function(params, oauthConfig, optionsToValidate, done) { params.metadata.generateOidcPEM = () => { return PEMkey; }; optionsToValidate.validateIssuer = true; optionsToValidate.issuer = 'https://sts.windows.net/268da1a1-9db4-48b9-b1fe-683250ba90cc/'; optionsToValidate.audience = '2abf3a52-7d86-460b-a1ef-77dc43de8aad'; optionsToValidate.allowMultiAudiencesInToken = false; optionsToValidate.ignoreExpiration = true; optionsToValidate.algorithms = ['RS256']; optionsToValidate.nonce = nonce_to_use; optionsToValidate.clockSkew = testStrategy._options.clockSkew; oauthConfig.authorization_endpoint = "https://login.microsoftonline.com/268da1a1-9db4-48b9-b1fe-683250ba90cc/oauth2/authorize"; oauthConfig.redirectUrl = "http://localhost:3000/auth/openid/return"; oauthConfig.clientID = "2abf3a52-7d86-460b-a1ef-77dc43de8aad"; oauthConfig.token_endpoint = "https://login.microsoftonline.com/268da1a1-9db4-48b9-b1fe-683250ba90cc/oauth2/token"; oauthConfig.userinfo_endpoint = "https://login.microsoftonline.com/268da1a1-9db4-48b9-b1fe-683250ba90cc/openid/userinfo"; if (action) { for (let i = 0; i < action.length; i++) action[i](optionsToValidate); } return done(); }; chai.passport .use(testStrategy) .fail(function(c) { challenge = c; done(); }) .error(function(e) { done(); }) .success(function(u) { user = u; done(); }) .req(function(req) { // reset the value of challenge and user challenge = user = undefined; var time = Date.now(); // add state and nonce to session req.session = {'my_key': {'content': [{'state': 'my_state', 'nonce': nonce_to_use, 'policy': undefined, 'timeStamp': time}]}}; // add id_token and state to body req.body = {'id_token': id_token_in_auth_resp , 'code' : code_in_auth_resp, 'state' : 'my_state'}; // empty query req.query = {}; }) .authenticate({}); }; }; /* * Begin the testing * */ describe('OIDCStrategy hybrid flow test', function() { this.timeout(TEST_TIMEOUT); /* * success if we ignore the expiration */ describe('success', function() { before(setReqFromAuthRespRedirect(id_token_in_auth_resp, code, nonce, [setTokenResponse(id_token_in_token_resp, access_token), setUserInfoResponse('use_valid_sub')])); it('should succeed with expected user', function() { chai.expect(user).to.equal('robot@sijun.onmicrosoft.com'); }); }); /* * test the incoming request with id_token and code, make sure test fail if * (1) wrong nonce * (2) invalid c_hash * (3) id_token expired * (4) id_token signature is invalid * (5) wrong issuer * (6) wrong audience */ describe('fail: wrong nonce', function() { before(setReqFromAuthRespRedirect(id_token_in_auth_resp, code, 'wrong_nonce')); it('should fail with wrong nonce', function() { chai.expect(challenge).to.equal('In _validateResponse: invalid nonce'); }); }); describe('fail: invalid c_hash', function() { before(setReqFromAuthRespRedirect(id_token_in_auth_resp, 'wrong_code', nonce)); it('should fail with invalid c_hash', function() { chai.expect(challenge).to.equal('In _validateResponse: invalid c_hash'); }); }); describe('fail: expired id_token', function() { before(setReqFromAuthRespRedirect(id_token_in_auth_resp, code, nonce, [setIgnoreExpirationFalse])); it('should fail with expired id_token', function() { chai.expect(challenge).to.equal('In _validateResponse: jwt is expired'); }); }); describe('fail: invalid signature in id_token', function() { before(setReqFromAuthRespRedirect(id_token_in_auth_resp_wrong_signature, code, nonce)); it('should fail with invalid signature in id_token', function() { chai.expect(challenge).to.equal('In _validateResponse: invalid signature'); }); }); describe('fail: invalid issuer', function() { before(setReqFromAuthRespRedirect(id_token_in_auth_resp, code, nonce, [setWrongIssuer])); it('should fail with invalid issuer', function() { chai.expect(challenge).to.equal('In _validateResponse: jwt issuer is invalid. expected: wrong_issuer'); }); }); describe('fail: invalid audience', function() { before(setReqFromAuthRespRedirect(id_token_in_auth_resp, code, nonce, [setWrongAudience])); it('should fail with invalid audience', function() { chai.expect(challenge).to.equal('In _validateResponse: jwt audience is invalid. expected: wrong_audience,spn:wrong_audience'); }); }); /* * test the access_token, id_token received from code consumpution, make sure test fail if we get * (1) invalid at_hash // @TODO cannot get an id_token that contains at_hash from AAD using * // the response_type we supported in passport-azure-ad * (2) wrong issuer/sub */ describe('fail: invalid sub in id_token in token response', function() { before(setReqFromAuthRespRedirect(id_token_in_auth_resp, code, nonce, [setTokenResponse(id_token_in_token_resp_wrong_sub, access_token)])); it('should fail with invalid sub in id_token', function() { chai.expect(challenge).to.equal('In _authCodeFlowHandler: After redeeming the code, sub in id_token from authorize_endpoint does not match sub in id_token from token_endpoint'); }); }); /* * userInfo test, make sure test fail if * (1) no access_token * (2) useInfo has a different sub from the one in id_token */ describe('success', function() { before(setReqFromAuthRespRedirect(id_token_in_auth_resp, code, nonce, [setTokenResponse(id_token_in_token_resp, access_token)])); it('should succeed with expected user', function() { chai.expect(user).to.equal('robot@sijun.onmicrosoft.com'); }); }); describe('fail: missing access_token', function() { before(setReqFromAuthRespRedirect(id_token_in_auth_resp, code, nonce, [setTokenResponse(id_token_in_token_resp, null)])); it('should fail with access_token missing', function() { chai.expect(challenge).to.equal('In _authCodeFlowHandler: we want to access userinfo endpoint, but access_token is not received'); }); }); describe('fail: invalid sub in userinfo', function() { before(setReqFromAuthRespRedirect(id_token_in_auth_resp, code, nonce, [setTokenResponse(id_token_in_token_resp, access_token), setUserInfoResponse('use_invalid_sub')])); it('should fail with invalid sub in userInfo', function() { chai.expect(challenge).to.equal('In _authCodeFlowHandler: sub received in userinfo and id_token do not match'); }); }); }); describe('OIDCStrategy authorization code flow test', function() { this.timeout(TEST_TIMEOUT); /* * success if we ignore the expiration */ describe('success', function() { before(setReqFromAuthRespRedirect(null, code, nonce, [setTokenResponse(id_token_in_token_resp, access_token), setUserInfoResponse('use_valid_sub')])); it('should succeed with expected user', function() { chai.expect(user).to.equal('robot@sijun.onmicrosoft.com'); }); }); /* * test the incoming request with id_token and code, make sure test fail if * (1) wrong nonce * (2) id_token expired * (3) id_token signature is invalid * (4) wrong issuer * (5) wrong audience */ describe('fail: wrong nonce', function() { before(setReqFromAuthRespRedirect(null, code, 'wrong_nonce')); it('should fail with wrong nonce', function() { chai.expect(challenge).to.equal('In _validateResponse: invalid nonce'); }); }); describe('fail: expired id_token', function() { before(setReqFromAuthRespRedirect(null, code, nonce, [setIgnoreExpirationFalse])); it('should fail with expired id_token', function() { chai.expect(challenge).to.equal('In _validateResponse: jwt is expired'); }); }); describe('fail: invalid issuer', function() { before(setReqFromAuthRespRedirect(null, code, nonce, [setWrongIssuer])); it('should fail with invalid issuer', function() { chai.expect(challenge).to.equal('In _validateResponse: jwt issuer is invalid. expected: wrong_issuer'); }); }); describe('fail: invalid audience', function() { before(setReqFromAuthRespRedirect(null, code, nonce, [setWrongAudience])); it('should fail with invalid audience', function() { chai.expect(challenge).to.equal('In _validateResponse: jwt audience is invalid. expected: wrong_audience,spn:wrong_audience'); }); }); describe('fail: invalid signature in id_token', function() { before(setReqFromAuthRespRedirect(null, code, nonce, [setTokenResponse(id_token_in_auth_resp_wrong_signature, access_token)])); it('should fail with invalid signature in id_token', function() { chai.expect(challenge).to.equal('In _validateResponse: invalid signature'); }); }); /* * userinfo test, make sure test fail if we have no access_token at hand */ describe('success', function() { before(setReqFromAuthRespRedirect(null, code, nonce, [setTokenResponse(id_token_in_token_resp, access_token)])); it('should succeed with expected user', function() { chai.expect(user).to.equal('robot@sijun.onmicrosoft.com'); }); }); describe('fail: access_token is not received', function() { before(setReqFromAuthRespRedirect(null, code, nonce, [setTokenResponse(id_token_in_token_resp, null)])); it('should fail with access_token missing', function() { chai.expect(challenge).to.equal('In _authCodeFlowHandler: we want to access userinfo endpoint, but access_token is not received'); }); }); describe('fail: invalid sub in userinfo', function() { before(setReqFromAuthRespRedirect(null, code, nonce, [setTokenResponse(id_token_in_token_resp, access_token), setUserInfoResponse('use_invalid_sub')])); it('should fail with invalid sub in userInfo', function() { chai.expect(challenge).to.equal('In _authCodeFlowHandler: sub received in userinfo and id_token do not match'); }); }); });