diff --git a/CHANGELOG.md b/CHANGELOG.md
index 540b4648..882f1e0e 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,4 +1,22 @@
-
+
+# 3.0.9
+
+## OIDCStrategy
+
+### Bug fixes
+
+* [#338](https://github.com/AzureAD/passport-azure-ad/issues/338) Allow query parameters in the identityMetadata config key
+
+* [#346](https://github.com/AzureAD/passport-azure-ad/pull/346) Fix: Cannot set cookie on the response
+
+## BearerStrategy
+
+### Bug fixes
+
+* [#333](https://github.com/AzureAD/passport-azure-ad/issues/333) jwt should not check sub for access token
+
+* [#338](https://github.com/AzureAD/passport-azure-ad/issues/338) Allow query parameters in the identityMetadata config key
+
# 3.0.8
## OIDCStrategy
diff --git a/README.md b/README.md
index 1c90ea96..f4d766dd 100644
--- a/README.md
+++ b/README.md
@@ -16,7 +16,7 @@ and with [Microsoft Active Directory Federation Services](http://en.wikipedia.or
_passport-azure-ad_ has a known security vulnerability affecting versions <1.4.6 and 2.0.0. Please update to >=1.4.6 or >=2.0.1 immediately. For more details, see the [security notice](https://github.com/AzureAD/passport-azure-ad/blob/master/SECURITY-NOTICE.MD).
## 2. Versions
-Current version - 3.0.8
+Current version - 3.0.9
Minimum recommended version - 1.4.6
You can find the changes for each version in the [change log](https://github.com/AzureAD/passport-azure-ad/blob/master/CHANGELOG.md).
@@ -371,7 +371,7 @@ the strategy.
* `domain_hint`: if you want to specify the domain that the user should use to sign in. This option is not supported for B2C tenant.
-* `login_hint`: if you want to prefill the username with a given value in the login page. The value should be the `upn` of an user, not the email (most times they are the same though).
+* `login_hint`: if you want to prefill the username with a given value in the login page. The value should be the `upn` of a user, not the email (most times they are the same though).
* `prompt`: v1 and v2 endpoint support `login`, `consent` and `admin_consent`; B2C endpoint only supports `login`.
@@ -402,10 +402,7 @@ Passport framework uses session to keep a persistent login session. As a plug in
...
});
- // must pass the response object to passport.authenticate, since we will use response object to set cookie
- app.get('/login', function(req, res, next) => {
- passport.authenticate('azuread-openidconnect', { session: false, response: res })(req, res, next);
- });
+ app.get('/login', passport.authenticate('azuread-openidconnect', { session: false }));
```
@@ -583,8 +580,7 @@ First you need to register one application in v1 tenant, one in v2 tenant and on
For the v2 application, you should register it at https://apps.dev.microsoft.com/ instead of Azure Portal.
-For the B2C application, create four policies named 'B2C_1_signin', 'B2C_1_signup', 'B2C_1_updateprofile',
-'B2C_1_resetpassword'. For each policy, select 'Local Account' as the identity provider, and select the
+For the B2C application, create policies named 'B2C_1_signin', 'B2C_1_signup'. For each policy, select 'Local Account' as the identity provider, and select the
following:
* 'B2C_1_signup':
@@ -593,26 +589,10 @@ following:
* Application claims: 'Display Name', Email Addresses', 'Given Name', 'Identity Provider', 'Surname', 'Users Object ID'
-* 'B2C_1_updateprofile':
-
- * Profile attributes: 'Display Name', 'Given Name', 'Surname'
-
- * Application claims: 'Display Name', Email Addresses', 'Given Name', 'Identity Provider', 'Surname', 'Users Object ID'
-
* 'B2C_1_signin':
* Application claims: 'Display Name', Email Addresses', 'Given Name', 'Identity Provider', 'Surname', 'Users Object ID'
-* 'B2C_1_signin_acr':
-
- * Application claims: 'Display Name', Email Addresses', 'Given Name', 'Identity Provider', 'Surname', 'Users Object ID'
-
- * After creating this policy, go the blade of this policy, click 'Edit' and then 'Token, session & SSO config'. Now switch the 'Claim representing policy ID' from 'tfp' to 'acr' and save the change.
-
-* 'B2C_1_resetpassword':
-
- * Application claims: 'Email Addresses', 'Given Name', 'Users Object ID'
-
You will also need to click the 'Run now' button in the 'B2C_1_signup' blade to create an user.
For B2C application, you will also need to create at least one scope and provide it to test parameters. See [how to create scope for B2C access token](https://azure.microsoft.com/en-us/blog/azure-ad-b2c-access-tokens-now-in-public-preview/). In the bearer_b2c_test, We will use OIDCStrategy to get a B2C
@@ -622,7 +602,7 @@ access token for the scope, and use BearerStrategy to validate the scope. Note f
#### 6.2.2. Fill the test parameters
-Open `test/End_to_end_test/script.js`, set `is_test_parameters_completed` parameter to true. For `test_parameters` variable, fill in the tenant id/client id/client secret of your applications, and the username/password of your application user. The 'oid' value is the object id of your application user. To find the 'oid' value, go to your tenant, click 'Users and groups', find your user and click it. The Object ID value will show up in the new blade.
+Open `test/End_to_end_test/script.js`, set `is_test_parameters_completed` parameter to true. For `test_parameters` variable, fill in the tenant id/client id/client secret of your applications, and the username/password of your application user.
For `thumbprint` and `privatePEMKey` parameters, you need to specify a certificate for your app and register the public key in Azure Active Directory. `thumbprint` is the base64url format of the thumbprint of the public key, and `privatePEMKey` is the private pem key string. For a v1 tenant, you can follow [this post](http://www.andrewconnell.com/blog/user-app-app-only-permissions-client-credentials-grant-flow-in-azure-ad-office-365-apis) to generate a certificate and register the public key. For a v2 tenant, you can go to your application page in the [v2 portal](https://apps.dev.microsoft.com) and click `Generate New Key Pair`. A certificate will be generated for you to download. The corresponding public key is automatically registered in this case.
diff --git a/lib/aadutils.js b/lib/aadutils.js
index 648f5a1a..70f09c4d 100644
--- a/lib/aadutils.js
+++ b/lib/aadutils.js
@@ -253,3 +253,16 @@ exports.getErrorMessage = (err) => {
return str;
};
+exports.concatUrl = (url, rest) => {
+ if (typeof rest === 'string' || rest instanceof String) {
+ rest = [rest];
+ }
+
+ if (!url) {
+ return `?${rest.join('&')}`;
+ }
+
+ var hasParam = url.indexOf('?') !== -1;
+ return rest ? url.concat(hasParam ? '&' : '?').concat(rest.join('&')) : url;
+};
+
diff --git a/lib/bearerstrategy.js b/lib/bearerstrategy.js
index 72f473ed..a617740b 100644
--- a/lib/bearerstrategy.js
+++ b/lib/bearerstrategy.js
@@ -386,12 +386,15 @@ Strategy.prototype.authenticate = function authenticateStrategy(req) {
// compute metadataUrl
(next) => {
- params.metadataURL = self._options.identityMetadata
- .concat(`?${aadutils.getLibraryProductParameterName()}=${aadutils.getLibraryProduct()}`)
- .concat(`&${aadutils.getLibraryVersionParameterName()}=${aadutils.getLibraryVersion()}`); ;
+ params.metadataURL = aadutils.concatUrl(self._options.identityMetadata,
+ [
+ `${aadutils.getLibraryProductParameterName()}=${aadutils.getLibraryProduct()}`,
+ `${aadutils.getLibraryVersionParameterName()}=${aadutils.getLibraryVersion()}`
+ ]
+ );
if (self._options.isB2C)
- params.metadataURL = params.metadataURL.concat(`&p=${self._options.policyName}`)
+ params.metadataURL = aadutils.concatUrl(params.metadataURL, `p=${self._options.policyName}`);
params.cacheKey = params.metadataURL;
@@ -432,6 +435,9 @@ Strategy.prototype.authenticate = function authenticateStrategy(req) {
if (self._options.scope)
optionsToValidate.scope = self._options.scope;
+ // Beaer token is considered as an access_token.
+ optionsToValidate.isAccessToken = true;
+
log.info(`In Strategy.prototype.authenticate: we will validate the following options: ${optionsToValidate}`);
return next();
diff --git a/lib/constants.js b/lib/constants.js
index bd7fa542..874cd594 100644
--- a/lib/constants.js
+++ b/lib/constants.js
@@ -28,8 +28,6 @@
var CONSTANTS = {};
CONSTANTS.POLICY_REGEX = /^b2c_1a?_[0-9a-z._-]+$/i; // policy is case insensitive
-CONSTANTS.TENANTNAME_REGEX = /^[0-9a-zA-Z]+.onmicrosoft.com$/;
-CONSTANTS.TENANTID_REGEX = /^[0-9a-zA-Z-]+$/;
CONSTANTS.CLOCK_SKEW = 300; // 5 minutes
diff --git a/lib/jsonWebToken.js b/lib/jsonWebToken.js
index 316fc61f..8a44c44e 100644
--- a/lib/jsonWebToken.js
+++ b/lib/jsonWebToken.js
@@ -48,6 +48,7 @@ var hasCommonElem = (array1, array2) => {
* - issuer (required if validateIssuer is true)
* - subject (optional, validate if provided)
* - ignoreExpiration (optional, if not set true we will validate expiration)
+ * - isAccessToken (optional. Specify if the token is id_token or access_token. The default value is false.)
* @callback
*/
exports.verify = function(jwtString, PEMKey, options, callback) {
@@ -164,13 +165,15 @@ exports.verify = function(jwtString, PEMKey, options, callback) {
return done(new Error('jwt issuer is invalid. expected: ' + options.issuer));
}
- // (2) subject
+ // (2) subject (id_token only. We don't check subject for access_token)
// - check the existence and the format of payload.sub
// - validate if options.subject is set
- if (typeof payload.sub !== 'string' || payload.sub === '')
- return done(new Error('invalid sub value in payload'));
- if (options.subject && options.subject !== payload.sub)
- return done(new Error('jwt subject is invalid. expected: ' + options.subject));
+ if (options.isAccessToken !== true) {
+ if (typeof payload.sub !== 'string' || payload.sub === '')
+ return done(new Error('invalid sub value in payload'));
+ if (options.subject && options.subject !== payload.sub)
+ return done(new Error('jwt subject is invalid. expected: ' + options.subject));
+ }
// (3) audience
// - always validate
diff --git a/lib/oidcstrategy.js b/lib/oidcstrategy.js
index 85dcd0f2..85af7c99 100644
--- a/lib/oidcstrategy.js
+++ b/lib/oidcstrategy.js
@@ -418,8 +418,13 @@ function Strategy(options, verify) {
options.isB2C = false;
// add telemetry
- options.identityMetadata = options.identityMetadata.concat(`?${aadutils.getLibraryProductParameterName()}=${aadutils.getLibraryProduct()}`)
- .concat(`&${aadutils.getLibraryVersionParameterName()}=${aadutils.getLibraryVersion()}`);
+ options.identityMetadata = aadutils.concatUrl(
+ options.identityMetadata,
+ [
+ `${aadutils.getLibraryProductParameterName()}=${aadutils.getLibraryProduct()}`,
+ `${aadutils.getLibraryVersionParameterName()}=${aadutils.getLibraryVersion()}`
+ ]
+ );
/****************************************************************************************
* Take care of issuer and audience
@@ -573,11 +578,7 @@ Strategy.prototype.authenticate = function authenticateStrategy(req, options) {
var prompt = options && options.prompt;
var extraAuthReqQueryParams = options && options.extraAuthReqQueryParams;
var extraTokenReqQueryParams = options && options.extraTokenReqQueryParams;
- var response = options && options.response;
-
- // validate tenantIdOrName if it is provided
- if (tenantIdOrName && !CONSTANTS.TENANTNAME_REGEX.test(tenantIdOrName) && !CONSTANTS.TENANTID_REGEX.test(tenantIdOrName))
- return self.failWithLog(`In passport.authenticate: invalid tenantIdOrName ${tenantIdOrName}`);
+ var response = options && options.response || req.res;
// 'params': items we get from the request or metadata, such as id_token, code, policy, metadata, cacheKey, etc
var params = { 'tenantIdOrName': tenantIdOrName, 'extraAuthReqQueryParams': extraAuthReqQueryParams, 'extraTokenReqQueryParams': extraTokenReqQueryParams };
@@ -1331,13 +1332,8 @@ Strategy.prototype._flowInitializationHandler = function flowInitializationHandl
// add telemetry
params[aadutils.getLibraryProductParameterName()] = aadutils.getLibraryProduct();
params[aadutils.getLibraryVersionParameterName()] = aadutils.getLibraryVersion();
- let location;
-
- // Implement support for standard OpenID Connect params (display, prompt, etc.)
- if (self._options.isB2C)
- location = `${oauthConfig.authorization_endpoint}&${querystring.stringify(params)}`;
- else
- location = `${oauthConfig.authorization_endpoint}?${querystring.stringify(params)}`;
+
+ const location = aadutils.concatUrl(oauthConfig.authorization_endpoint, querystring.stringify(params));
return self.redirect(location);
};
diff --git a/package.json b/package.json
index bc681cf0..106bfe4a 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "passport-azure-ad",
- "version": "3.0.8",
+ "version": "3.0.9",
"license": "MIT",
"keywords": [
"azure active directory",
diff --git a/test/Chai-passport_test/aadutils_test.js b/test/Chai-passport_test/aadutils_test.js
index facf51fd..dfb02440 100644
--- a/test/Chai-passport_test/aadutils_test.js
+++ b/test/Chai-passport_test/aadutils_test.js
@@ -54,3 +54,33 @@ describe('uid test', function() {
done();
});
});
+
+describe('concatUrl test', function() {
+ it('should generate a valid url if a query parameter is already in place', function(done) {
+ const currentUrl = 'http://example.com?foo=bar';
+ const newUrl = aadutils.concatUrl(currentUrl, ['bar=foo']);
+ expect(newUrl).to.equal(`${currentUrl}&bar=foo`);
+ done();
+ });
+
+ it('should generate a valid url if no query parameter is present', function(done) {
+ const currentUrl = 'http://example.com';
+ const newUrl = aadutils.concatUrl(currentUrl, 'bar=foo');
+ expect(newUrl).to.equal(`${currentUrl}?bar=foo`);
+ done();
+ });
+
+ it('should return the bare url if no additional arguments are present', function(done) {
+ const currentUrl = 'http://example.com';
+ const newUrl = aadutils.concatUrl(currentUrl);
+ expect(newUrl).to.equal(currentUrl);
+ done();
+ });
+
+ it('should return parseable query parameters if no url is present', function(done) {
+ const parameters = ['bar=foo&foo=bar'];
+ const newUrl = aadutils.concatUrl(undefined, parameters)
+ expect(newUrl).to.equal(`?${parameters}`);
+ done();
+ });
+})
diff --git a/test/Chai-passport_test/constants_test.js b/test/Chai-passport_test/constants_test.js
index 3761dbd6..51b2cfbf 100644
--- a/test/Chai-passport_test/constants_test.js
+++ b/test/Chai-passport_test/constants_test.js
@@ -29,9 +29,6 @@ var CONSTANTS = require('../../lib/constants');
const TEST_TIMEOUT = 1000000; // 1000 seconds
-CONSTANTS.TENANTNAME_REGEX = /^[0-9a-zA-Z]+.onmicrosoft.com$/;
-CONSTANTS.TENANTID_REGEX = /^[0-9a-zA-Z-]+$/;
-
describe('policy checking', function() {
this.timeout(TEST_TIMEOUT);
@@ -61,36 +58,3 @@ describe('policy checking', function() {
done();
});
});
-
-describe('tenant name checking', function() {
- this.timeout(TEST_TIMEOUT);
-
- it('should pass with good tenant name', function(done) {
- expect(CONSTANTS.TENANTNAME_REGEX.test('contoso123COMPANY.onmicrosoft.com')).to.equal(true);
- done();
- });
-
- it('should fail with bad tenant name', function(done) {
- expect(CONSTANTS.TENANTNAME_REGEX.test('contoso.onmicrosoft.comm')).to.equal(false);
- expect(CONSTANTS.TENANTNAME_REGEX.test('contoso123COMPANY')).to.equal(false);
- expect(CONSTANTS.TENANTNAME_REGEX.test('.onmicrosoft.com')).to.equal(false);
- expect(CONSTANTS.TENANTNAME_REGEX.test('contoso123COMPANY.ONMICROSOFT.com')).to.equal(false);
- expect(CONSTANTS.TENANTNAME_REGEX.test('contoso_company.onmicrosoft.com')).to.equal(false);
- done();
- });
-});
-
-describe('tenant id checking', function() {
- this.timeout(TEST_TIMEOUT);
-
- it('should pass with good tenant id', function(done) {
- expect(CONSTANTS.TENANTID_REGEX.test('683eAd13-3193-43f0-9677-d727c25a588f')).to.equal(true);
- done();
- });
-
- it('should fail with bad tenant id', function(done) {
- expect(CONSTANTS.TENANTID_REGEX.test('23_12')).to.equal(false);
- done();
- });
-});
-
diff --git a/test/Chai-passport_test/json_web_token_test.js b/test/Chai-passport_test/json_web_token_test.js
new file mode 100644
index 00000000..2fa7ce29
--- /dev/null
+++ b/test/Chai-passport_test/json_web_token_test.js
@@ -0,0 +1,55 @@
+/**
+ * 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';
+
+var chai = require('chai');
+var jws = require('jws');
+var jwt = require('../../lib/jsonWebToken');
+
+const TEST_TIMEOUT = 1000000; // 1000 seconds
+
+const secret = "12345678901234567890123456789012"; // 512 bit symmetric key
+
+describe('json web token test', function() {
+ this.timeout(TEST_TIMEOUT);
+
+ var signStream = jws.createSign(
+ {
+ 'header': { 'alg': 'HS256', 'typ': 'JWT'},
+ 'payload': { 'nbf': Date.now() / 1000, 'exp': Date.now() / 1000 + 300, 'iat': Date.now() / 1000, 'iss': 'https://example.com', 'aud': 'audience' },
+ 'secret': secret
+ });
+
+ var jwtString = signStream.sign(); // create the corresponding json web token
+ var options = { audience: 'audience', algorithms: ['HS256'], issuer: 'https://example.com' }; // validation options
+
+ it('should fail with missing sub error', function(done) {
+ jwt.verify(jwtString, secret, options, (err, token) => { chai.expect(err.message).to.equal('invalid sub value in payload'); done(); });
+ });
+
+ it('should succeed if testing access token', function(done) {
+ options.isAccessToken = true;
+ jwt.verify(jwtString, secret, options, (err, token) => { chai.expect(err).to.equal(null); done(); });
+ });
+});
diff --git a/test/End_to_end_test/bearer_test.js b/test/End_to_end_test/bearer_test.js
index dcd8d333..dafdfeb1 100644
--- a/test/End_to_end_test/bearer_test.js
+++ b/test/End_to_end_test/bearer_test.js
@@ -29,6 +29,7 @@
var chromedriver = require('./driver');
var service = chromedriver.get_service();
+var error_handler = chromedriver.error_handler;
var webdriver = chromedriver.webdriver;
var By = webdriver.By;
var until = webdriver.until;
@@ -37,7 +38,7 @@ var chai = require('chai');
var expect = chai.expect;
const TEST_TIMEOUT = 1000000; // 1000 seconds
-const LOGIN_WAITING_TIME = 3000; // 3 second
+const LOGIN_WAITING_TIME = 1000; // 1 second
/******************************************************************************
* configurations needed
@@ -163,28 +164,36 @@ var get_token_for_resource = (resource, done) => {
// we only need to enter the user name and password if we haven't logged in yet
if (!client_already_logged_in) {
driver.wait(until.titleIs('Sign in to your account'), 10000);
- var usernamebox = driver.findElement(By.name('login'));
+ var usernamebox = driver.findElement(By.name('loginfmt'));
usernamebox.sendKeys(test_parameters.username);
+ usernamebox.sendKeys(webdriver.Key.ENTER);
var passwordbox = driver.findElement(By.name('passwd'));
passwordbox.sendKeys(test_parameters.password);
driver.sleep(LOGIN_WAITING_TIME);
+ passwordbox = driver.findElement(By.name('passwd'));
passwordbox.sendKeys(webdriver.Key.ENTER);
client_already_logged_in = true;
}
}).then(() => {
- done();
+ driver.wait(until.titleIs('Sign in to your account'), 5000).then(
+ ()=>{ driver.findElement(By.id('idBtn_Back')).click().then(()=>{done();}); },
+ ()=>{ done();}
+ );
});
};
var checkResult = (test_app_config, result, done) => {
var server = require('./app/api')(test_app_config);
-
driver.get('http://localhost:3000/callApi')
.then(() => {
- driver.wait(until.titleIs('result'), 10000);
+ driver.wait(until.titleIs('result'), 10000).catch((ex) => {
+ error_handler(ex, server, done);
+ });
driver.findElement(By.id('status')).getText().then((text) => {
expect(text).to.equal(result);
server.shutdown(done);
+ }).catch((ex) => {
+ error_handler(ex, server, done);
});
});
};
diff --git a/test/End_to_end_test/driver.js b/test/End_to_end_test/driver.js
index 4c728961..5e844e3b 100644
--- a/test/End_to_end_test/driver.js
+++ b/test/End_to_end_test/driver.js
@@ -24,8 +24,10 @@
'use strict';
var webdriver = require('selenium-webdriver');
+var chai = require('chai');
var chrome = require('selenium-webdriver/chrome');
var path = require('chromedriver').path;
+var expect = chai.expect;
var chromeCapabilities = webdriver.Capabilities.chrome();
var chromeOptions = {
@@ -34,6 +36,12 @@ var chromeOptions = {
chromeCapabilities.set('chromeOptions', chromeOptions);
exports = module.exports = {
+ error_handler: (ex, server, done) => {
+ server.shutdown(() => {
+ expect(ex).to.equal(null);
+ done();
+ });
+ },
get_service: () => {
var service = new chrome.ServiceBuilder(path).build();
chrome.setDefaultService(service);
diff --git a/test/End_to_end_test/oidc_b2c_test.js b/test/End_to_end_test/oidc_b2c_test.js
index 97e378d0..076888a9 100644
--- a/test/End_to_end_test/oidc_b2c_test.js
+++ b/test/End_to_end_test/oidc_b2c_test.js
@@ -176,12 +176,6 @@ var resultPageValidation = (test_app_config, driver) => {
driver.findElement(By.id('status')).getText().then((text) => {
expect(text).to.equal('succeeded');
});
- driver.findElement(By.id('oid')).getText().then((text) => {
- expect(text).to.equal(test_parameters.oid);
- });
- driver.findElement(By.id('emails')).getText().then((text) => {
- expect(text).to.equal(test_parameters.username);
- });
driver.findElement(By.id('access_token')).getText().then((text) => {
if (test_app_config.scope.length > 6)
expect(text).to.equal('exists');
@@ -208,15 +202,7 @@ var checkResult = (test_app_config, done) => {
else
server = require('./app/app')(test_app_config, {}, 8);
- driver.get('http://localhost:3000/login?p=b2c_1_signup')
- .then(() => {
- if (first_time) {
- driver.wait(until.titleIs('User details'), 10000);
- }
- })
- .then(() => {
- driver.get('http://localhost:3000/login?p=b2c_1_signin');
- })
+ driver.get('http://localhost:3000/login?p=b2c_1_signin')
.then(() => {
if (first_time) {
driver.wait(until.titleIs('Sign in to your account'), 10000);
@@ -232,17 +218,6 @@ var checkResult = (test_app_config, done) => {
.then(() => {
resultPageValidation(test_app_config, driver);
})
- .then(() => {
- driver.get('http://localhost:3000/login?p=b2c_1_signin_acr');
- })
- .then(() => {
- driver.get('http://localhost:3000/login?p=b2c_1_resetpassword');
- driver.wait(until.titleIs('User details'), 10000);
- })
- .then(() => {
- driver.get('http://localhost:3000/login?p=b2c_1_updateprofile');
- driver.wait(until.titleIs('Update profile'), 10000);
- })
.then(() => {
server.shutdown(done);
});
@@ -264,43 +239,6 @@ var checkInvalidResult = (test_app_config, tenantIdOrName, done) => {
});
};
-var checkResultForPromptAndHint = (test_app_config, authenticate_opt, done) => {
- var server = create_app(test_app_config, authenticate_opt, 8);
-
- if (!driver)
- driver = chromedriver.get_driver();
-
- driver.get('http://localhost:3000/login?p=b2c_1_signin')
- .then(() => {
- if (authenticate_opt.domain_hint === 'live.com') {
- // we should have come to the login page for live.com
- driver.wait(until.titleIs('Sign in to your Microsoft account'), 10000);
- } else if (authenticate_opt.prompt === 'login') {
- // without domain_hint, we will come to the generic login page
- driver.wait(until.titleIs('Sign in to your account'), 10000);
- if (!authenticate_opt.login_hint) {
- // if there is no login_hint, then we have to fill the username portion
- var usernamebox = driver.findElement(By.name('login'));
- usernamebox.sendKeys(test_parameters.username);
- }
- var passwordbox = driver.findElement(By.name('passwd'));
- passwordbox.sendKeys(test_parameters.password);
- driver.sleep(LOGIN_WAITING_TIME);
- passwordbox.sendKeys(webdriver.Key.ENTER);
- }
- }).then(() => {
- if (authenticate_opt.domain_hint === 'live.com') {
- server.shutdown(done);
- } else {
- driver.wait(until.titleIs('result'), 10000);
- driver.findElement(By.id('status')).getText().then((text) => {
- expect(text).to.equal('succeeded');
- server.shutdown(done);
- });
- }
- });
-};
-
/******************************************************************************
* The test cases
*****************************************************************************/
@@ -396,18 +334,6 @@ describe('oidc b2c positive other test', function() {
});
});
-describe('oidc b2c login_hint and prompt test', function() {
- this.timeout(TEST_TIMEOUT);
-
- it('should succeed with login page showing up and username prefilled', function(done) {
- checkResultForPromptAndHint(hybrid_config, { login_hint: test_parameters.username, prompt: 'login' }, done);
- });
-
- it('should succeed without login page showing up', function(done) {
- checkResultForPromptAndHint(hybrid_config, { login_hint: test_parameters.username }, done);
- });
-});
-
describe('oidc b2c negative test', function() {
this.timeout(TEST_TIMEOUT);
@@ -416,11 +342,6 @@ describe('oidc b2c negative test', function() {
checkInvalidResult(hybrid_config_common_endpoint_wrong_secret, test_parameters.tenantID, done);
});
- // invalid tenant id or name
- it('should fail with invalid identityMetadata', function(done) {
- checkInvalidResult(config_template_common_endpoint, 'invalid_tenant', done);
- });
-
it('close service', function(done) {
expect('1').to.equal('1');
driver.quit();
diff --git a/test/End_to_end_test/oidc_v1_test.js b/test/End_to_end_test/oidc_v1_test.js
index fe4f0962..01d9c6fb 100644
--- a/test/End_to_end_test/oidc_v1_test.js
+++ b/test/End_to_end_test/oidc_v1_test.js
@@ -40,7 +40,7 @@ var expect = chai.expect;
var fs = require('fs');
const TEST_TIMEOUT = 1000000; // 1000 seconds
-const LOGIN_WAITING_TIME = 3000; // 3 second
+const LOGIN_WAITING_TIME = 1000; // 1 second
/******************************************************************************
* Configurations needed
@@ -232,33 +232,23 @@ var checkResult = (test_app_config, arity, done) => {
.then(() => {
if (first_time) {
driver.wait(until.titleIs('Sign in to your account'), 10000);
- var usernamebox = driver.findElement(By.name('login'));
+ var usernamebox = driver.findElement(By.name('loginfmt'));
usernamebox.sendKeys(test_parameters.username);
+ usernamebox.sendKeys(webdriver.Key.ENTER);
var passwordbox = driver.findElement(By.name('passwd'));
passwordbox.sendKeys(test_parameters.password);
driver.sleep(LOGIN_WAITING_TIME);
+ passwordbox = driver.findElement(By.name('passwd'));
passwordbox.sendKeys(webdriver.Key.ENTER);
first_time = false;
}
+ }).then(() => {
+ driver.findElement(By.id('idBtn_Back')).then((element)=>{element.click();}, () => {});
}).then(() => {
driver.wait(until.titleIs('result'), 10000);
driver.findElement(By.id('status')).getText().then((text) => {
expect(text).to.equal('succeeded');
});
- driver.findElement(By.id('oid')).getText().then((text) => {
- // arity 3 means we are using function(iss, sub, done), so there is no profile.oid
- if (arity !== 3)
- expect(text).to.equal(test_parameters.oid);
- else
- expect(text).to.equal('none');
- });
- driver.findElement(By.id('upn')).getText().then((text) => {
- // arity 3 means we are using function(iss, sub, done), so there is no profile.displayName
- if (arity !== 3)
- expect(text).to.equal(test_parameters.username);
- else
- expect(text).to.equal('none');
- });
driver.findElement(By.id('access_token')).getText().then((text) => {
if (arity >= 6)
expect(text).to.equal('exists');
@@ -275,83 +265,6 @@ var checkResult = (test_app_config, arity, done) => {
});
};
-var checkResultTwoTabs = (test_app_config, arity, done) => {
- var server = create_app(test_app_config, {}, arity);
-
- if (!driver1)
- driver1 = chromedriver.get_driver();
- if (!driver2)
- driver2 = chromedriver.get_driver();
-
- driver1.get('http://localhost:3000/login')
- .then(() => {
- // go to login page at tab1
- driver1.wait(until.titleIs('Sign in to your account'), 10000);
- driver2.get('http://localhost:3000/login');
- })
- .then(() => {
- // go to login page at tab2
- driver2.wait(until.titleIs('Sign in to your account'), 10000);
- var usernamebox = driver1.findElement(By.name('login'));
- usernamebox.sendKeys(test_parameters.username);
- var passwordbox = driver1.findElement(By.name('passwd'));
- passwordbox.sendKeys(test_parameters.password);
- driver1.sleep(LOGIN_WAITING_TIME);
- passwordbox.sendKeys(webdriver.Key.ENTER);
- })
- .then(() => {
- // check the result on tab1
- driver1.wait(until.titleIs('result'), 10000);
- driver1.findElement(By.id('status')).getText().then((text) => {
- expect(text).to.equal('succeeded');
- });
- driver1.findElement(By.id('oid')).getText().then((text) => {
- expect(text).to.equal(test_parameters.oid);
- });
- driver1.findElement(By.id('upn')).getText().then((text) => {
- expect(text).to.equal(test_parameters.username);
- });
- driver1.findElement(By.id('access_token')).getText().then((text) => {
- expect(text).to.equal('exists');
- });
- driver1.findElement(By.id('refresh_token')).getText().then((text) => {
- expect(text).to.equal('exists');
- })
- })
- .then(() => {
- // switch to tab2
- var usernamebox = driver2.findElement(By.name('login'));
- usernamebox.sendKeys(test_parameters.username);
- var passwordbox = driver2.findElement(By.name('passwd'));
- passwordbox.sendKeys(test_parameters.password);
- driver2.sleep(LOGIN_WAITING_TIME);
- passwordbox.sendKeys(webdriver.Key.ENTER);
- })
- .then(() => {
- // check result on tab2
- driver2.wait(until.titleIs('result'), 10000);
- driver2.findElement(By.id('status')).getText().then((text) => {
- expect(text).to.equal('succeeded');
- });
- driver2.findElement(By.id('oid')).getText().then((text) => {
- expect(text).to.equal(test_parameters.oid);
- });
- driver2.findElement(By.id('upn')).getText().then((text) => {
- expect(text).to.equal(test_parameters.username);
- });
- driver2.findElement(By.id('access_token')).getText().then((text) => {
- expect(text).to.equal('exists');
- });
- driver2.findElement(By.id('refresh_token')).getText().then((text) => {
- expect(text).to.equal('exists');
- driver1.manage().deleteAllCookies();
- driver2.manage().deleteAllCookies();
- driver1.quit(); driver2.quit();
- server.shutdown(done);
- });
- });
-};
-
var checkInvalidResult = (test_app_config, done) => {
var server = create_app(test_app_config, {}, 8);
@@ -362,11 +275,13 @@ var checkInvalidResult = (test_app_config, done) => {
.then(() => {
if (first_time) {
driver.wait(until.titleIs('Sign in to your account'), 10000);
- var usernamebox = driver.findElement(By.name('login'));
+ var usernamebox = driver.findElement(By.name('loginfmt'));
usernamebox.sendKeys(test_parameters.username);
+ usernamebox.sendKeys(webdriver.Key.ENTER);
var passwordbox = driver.findElement(By.name('passwd'));
passwordbox.sendKeys(test_parameters.password);
driver.sleep(LOGIN_WAITING_TIME);
+ passwordbox = driver.findElement(By.name('passwd'));
passwordbox.sendKeys(webdriver.Key.ENTER);
first_time = false;
}
@@ -380,48 +295,6 @@ var checkInvalidResult = (test_app_config, done) => {
});
};
-var checkResultForPromptAndHint = (test_app_config, authenticate_opt, done) => {
- var server = create_app(test_app_config, authenticate_opt, 8);
-
- if (!driver)
- driver = chromedriver.get_driver();
-
- driver.get('http://localhost:3000/login')
- .then(() => {
- if (authenticate_opt.domain_hint === 'live.com') {
- // we should have come to the login page for live.com
- driver.wait(until.titleIs('Sign in to your Microsoft account'), 10000);
- } else if (authenticate_opt.prompt) {
- // without domain_hint, we will come to the generic login page
- driver.wait(until.titleIs('Sign in to your account'), 10000);
- if (!authenticate_opt.login_hint) {
- // if there is no login_hint, then we have to fill the username portion
- var usernamebox = driver.findElement(By.name('login'));
- usernamebox.sendKeys(test_parameters.username);
- }
-
- var passwordbox = driver.findElement(By.name('passwd'));
- passwordbox.sendKeys(test_parameters.password);
- driver.sleep(LOGIN_WAITING_TIME);
- passwordbox.sendKeys(webdriver.Key.ENTER);
- }
- }).then(() => {
- if (authenticate_opt.domain_hint === 'live.com') {
- server.shutdown(done);
- } else {
- if (authenticate_opt.prompt === 'consent') {
- // consent
- driver.findElement(By.id('cred_accept_button')).click();
- }
- driver.wait(until.titleIs('result'), 10000);
- driver.findElement(By.id('status')).getText().then((text) => {
- expect(text).to.equal('succeeded');
- server.shutdown(done);
- });
- }
- });
-};
-
/******************************************************************************
* The test cases
*****************************************************************************/
@@ -540,9 +413,9 @@ describe('oidc v1 positive other test', function() {
checkResult(implicit_config_common_endpoint, 2, done);
});
- /***************************************************************************
+ /**************************************************************************
* Test issuer and validateIssuers for both tenant specific and common endpoint
- **************************************************************************/
+ *************************************************************************/
// tenant specific endpoint
it('should succeed', function(done) {
@@ -581,34 +454,6 @@ describe('oidc v1 positive other test', function() {
it('should succeed', function(done) {
checkResult(code_config_common_endpoint_query, 2, done);
});
-
- /****************************************************************************
- * Test login from two tabs
- ***************************************************************************/
-
- it('should succeed with arity 8 for verify function', function(done) {
- checkResultTwoTabs(hybrid_config, 8, done);
- });
-});
-
-describe('oidc v1 login/domain hint and prompt test', function() {
- this.timeout(TEST_TIMEOUT);
-
- it('should succeed with login page showing up and username prefilled', function(done) {
- checkResultForPromptAndHint(hybrid_config, { login_hint: test_parameters.username, prompt: 'login' }, done);
- });
-
- it('should succeed with login page showing up and username prefilled and consent page showing up later', function(done) {
- checkResultForPromptAndHint(hybrid_config, { login_hint: test_parameters.username, prompt: 'consent' }, done);
- });
-
- it('should succeed without login page showing up', function(done) {
- checkResultForPromptAndHint(hybrid_config, { login_hint: test_parameters.username }, done);
- });
-
- it('should succeed with live.com login page showing up', function(done) {
- checkResultForPromptAndHint(hybrid_config, { domain_hint: 'live.com' }, done);
- });
});
describe('oidc v1 negative test', function() {
diff --git a/test/End_to_end_test/oidc_v2_test.js b/test/End_to_end_test/oidc_v2_test.js
index b106f4b6..9a4c2b13 100644
--- a/test/End_to_end_test/oidc_v2_test.js
+++ b/test/End_to_end_test/oidc_v2_test.js
@@ -40,7 +40,7 @@ var expect = chai.expect;
var fs = require('fs');
const TEST_TIMEOUT = 1000000; // 1000 seconds
-const LOGIN_WAITING_TIME = 3000; // 3 second
+const LOGIN_WAITING_TIME = 1000; // 1 second
/******************************************************************************
* Configurations needed
@@ -219,6 +219,8 @@ var apply_test_parameters = (done) => {
done();
};
+var client_already_logged_in = false;
+
var checkResult = (test_app_config, done) => {
var server = create_app(test_app_config, {}, 8);
@@ -227,28 +229,33 @@ var checkResult = (test_app_config, done) => {
driver.get('http://localhost:3000/login')
.then(() => {
- driver.getTitle().then((title) => {
- if (title === 'Sign in to your account') {
- driver.findElement(By.xpath('//*[@id="' + username_id_on_page + '"]/table/tbody/tr/td[2]/div[1]')).then((element) => {
- element.click();
- }, (err) => {
- var usernamebox = driver.findElement(By.name('login'));
- usernamebox.sendKeys(test_parameters.username);
- var passwordbox = driver.findElement(By.name('passwd'));
- passwordbox.sendKeys(test_parameters.password);
- driver.sleep(LOGIN_WAITING_TIME);
- passwordbox.sendKeys(webdriver.Key.ENTER);
- });
- }
- });
+ if (!client_already_logged_in) {
+ driver.wait(until.titleIs('Sign in to your account'), 10000);
+ var usernamebox = driver.findElement(By.name('loginfmt'));
+ usernamebox.sendKeys(test_parameters.username);
+ usernamebox.sendKeys(webdriver.Key.ENTER);
+ var passwordbox = driver.findElement(By.name('passwd'));
+ passwordbox.sendKeys(test_parameters.password);
+ driver.sleep(LOGIN_WAITING_TIME);
+ passwordbox = driver.findElement(By.name('passwd'));
+ passwordbox.sendKeys(webdriver.Key.ENTER);
+ client_already_logged_in = true;
+ driver.findElement(By.id('idBtn_Back')).then((element)=>{element.click();}, () => {});
+ }
+ }).then(() => {
+ if (test_app_config.identityMetadata && test_app_config.identityMetadata.indexOf('common')!= -1) {
+ driver.getTitle().then((title) => {
+ if (title == 'Sign in to your account') {
+ var selectAccoutButton = driver.wait(until.elementLocated(By.xpath('//*[@id="i0281"]/div[1]/div/div[1]/div[2]/div/div/div[2]/div[1]/div/div[2]')), 5000);
+ driver.wait(until.elementIsVisible(selectAccoutButton), 5000).click();
+ }
+ });
+ }
}).then(() => {
driver.wait(until.titleIs('result'), 10000);
driver.findElement(By.id('status')).getText().then((text) => {
expect(text).to.equal('succeeded');
});
- driver.findElement(By.id('oid')).getText().then((text) => {
- expect(text).to.equal(test_parameters.oid);
- });
driver.findElement(By.id('access_token')).getText().then((text) => {
if (test_app_config.responseType !== 'id_token' && test_app_config.scope.length > 6) // if we have scope besides 'openid'
expect(text).to.equal('exists');
@@ -274,17 +281,21 @@ var checkInvalidResult = (test_app_config, done) => {
driver.get('http://localhost:3000/login')
.then(() => {
driver.getTitle().then((title) => {
- if (title === 'Sign in to your account') {
- driver.findElement(By.xpath('//*[@id="' + username_id_on_page + '"]/table/tbody/tr/td[2]/div[1]')).then((element) => {
+ if (!client_already_logged_in) {
+ driver.wait(until.titleIs('Sign in to your account'), 10000);
+ var usernamebox = driver.findElement(By.name('loginfmt'));
+ usernamebox.sendKeys(test_parameters.username);
+ usernamebox.sendKeys(webdriver.Key.ENTER);
+ var passwordbox = driver.findElement(By.name('passwd'));
+ passwordbox.sendKeys(test_parameters.password);
+ driver.sleep(LOGIN_WAITING_TIME);
+ passwordbox = driver.findElement(By.name('passwd'));
+ passwordbox.sendKeys(webdriver.Key.ENTER);
+ client_already_logged_in = true;
+ } else {
+ driver.findElement(By.xpath('//*[@id="i0281"]/div[1]/div/div[1]/div[2]/div/div/div[2]/div[1]/div/div[2]')).then((element) => {
element.click();
- }, (err) => {
- var usernamebox = driver.findElement(By.name('login'));
- usernamebox.sendKeys(test_parameters.username);
- var passwordbox = driver.findElement(By.name('passwd'));
- passwordbox.sendKeys(test_parameters.password);
- driver.sleep(LOGIN_WAITING_TIME);
- passwordbox.sendKeys(webdriver.Key.ENTER);
- });
+ }, (err) => {});
}
});
})
@@ -297,47 +308,6 @@ var checkInvalidResult = (test_app_config, done) => {
});
};
-var checkResultForPromptAndHint = (test_app_config, authenticate_opt, done) => {
- var server = create_app(test_app_config, authenticate_opt, 8);
-
- if (!driver)
- driver = chromedriver.get_driver();
-
- driver.get('http://localhost:3000/login')
- .then(() => {
- if (authenticate_opt.domain_hint === 'live.com') {
- // we should have come to the login page for live.com
- driver.wait(until.titleIs('Sign in to your Microsoft account'), 10000);
- } else if (authenticate_opt.prompt) {
- // without domain_hint, we will come to the generic login page
- driver.wait(until.titleIs('Sign in to your account'), 10000);
- if (!authenticate_opt.login_hint) {
- // if there is no login_hint, then we have to fill the username portion
- var usernamebox = driver.findElement(By.name('login'));
- usernamebox.sendKeys(test_parameters.username);
- }
- var passwordbox = driver.findElement(By.name('passwd'));
- passwordbox.sendKeys(test_parameters.password);
- driver.sleep(LOGIN_WAITING_TIME);
- passwordbox.sendKeys(webdriver.Key.ENTER);
- }
- }).then(() => {
- if (authenticate_opt.domain_hint === 'live.com') {
- server.shutdown(done);
- } else {
- if (authenticate_opt.prompt === 'consent') {
- // consent
- driver.findElement(By.id('cred_accept_button')).click();
- }
- driver.wait(until.titleIs('result'), 10000);
- driver.findElement(By.id('status')).getText().then((text) => {
- expect(text).to.equal('succeeded');
- server.shutdown(done);
- });
- }
- });
-};
-
/******************************************************************************
* The test cases
*****************************************************************************/
@@ -401,9 +371,9 @@ describe('oidc v2 positive test', function() {
checkResult(implicit_config_common_endpoint, done);
});
- /***************************************************************************
+ /**************************************************************************
* Test issuer and validateIssuers for both tenant specific and common endpoint
- **************************************************************************/
+ *************************************************************************/
// tenant specific endpoint
it('should succeed', function(done) {
@@ -444,26 +414,6 @@ describe('oidc v2 positive test', function() {
});
});
-describe('oidc v2 login/domain hint and prompt test', function() {
- this.timeout(TEST_TIMEOUT);
-
- it('should succeed with login page showing up and username prefilled', function(done) {
- checkResultForPromptAndHint(hybrid_config, { login_hint: test_parameters.username, prompt: 'login' }, done);
- });
-
- it('should succeed with login page showing up and username prefilled and consent page showing up later', function(done) {
- checkResultForPromptAndHint(hybrid_config, { login_hint: test_parameters.username, prompt: 'consent' }, done);
- });
-
- it('should succeed without login page showing up', function(done) {
- checkResultForPromptAndHint(hybrid_config, { login_hint: test_parameters.username }, done);
- });
-
- it('should succeed with live.com login page showing up', function(done) {
- checkResultForPromptAndHint(hybrid_config, { domain_hint: 'live.com' }, done);
- });
-});
-
describe('oidc v2 negative test', function() {
this.timeout(TEST_TIMEOUT);
diff --git a/test/End_to_end_test/script.js b/test/End_to_end_test/script.js
index 13b1caa0..03c72768 100644
--- a/test/End_to_end_test/script.js
+++ b/test/End_to_end_test/script.js
@@ -135,8 +135,7 @@ exports.set_test_parameters = (callback) => {
thumbprint: 'Z2mGlF+IHL49Q9a66mDQLWG/lfs=',
privatePEMKey: v1_private_pem_key,
username: 'manNonMFA1@msidlab5.onmicrosoft.com',
- password: v1_v2_password,
- oid: 'a9590750-7562-45f5-af5b-41f504843766'
+ password: v1_v2_password
};
test_parameters.v2_params = {
@@ -146,8 +145,7 @@ exports.set_test_parameters = (callback) => {
thumbprint: 'uW2guEHq4k/5Rr/UhhBcoIl0ERk=',
privatePEMKey: v2_private_pem_key,
username: 'manNonMFA1@msidlab5.onmicrosoft.com',
- password: v1_v2_password,
- oid: 'a9590750-7562-45f5-af5b-41f504843766'
+ password: v1_v2_password
};
test_parameters.b2c_params = {
@@ -156,7 +154,6 @@ exports.set_test_parameters = (callback) => {
clientSecret: b2c_client_secret,
username: 'lsj31415926@gmail.com',
password: b2c_password,
- oid: '7a61aaa0-6510-4e5c-b3ba-f31e5b7c7642',
scopeForBearer: ['read', 'write'],
scopeForOIDC: ['https://sijun1b2c.onmicrosoft.com/oidc-b2c/read', 'https://sijun1b2c.onmicrosoft.com/oidc-b2c/write']
};
diff --git a/test/End_to_end_test/test_parameters.js b/test/End_to_end_test/test_parameters.js
index 3528f288..dedc5b6a 100644
--- a/test/End_to_end_test/test_parameters.js
+++ b/test/End_to_end_test/test_parameters.js
@@ -31,8 +31,7 @@ var test_parameters = {
thumbprint: '',
privatePEMKey: '',
username: '',
- password: '',
- oid: '',
+ password: ''
},
v2_params: {
tenantID: '',
@@ -41,8 +40,7 @@ var test_parameters = {
thumbprint: '',
privatePEMKey: '',
username: '',
- password: '',
- oid: '',
+ password: ''
},
b2c_params: {
tenantID: '',
@@ -50,7 +48,6 @@ var test_parameters = {
clientSecret: '',
username: '',
password: '',
- oid: '',
scopeForBearer: '',
scopeForOIDC: ''
}