Skip to content
This repository was archived by the owner on Feb 26, 2020. It is now read-only.

Commit c5a54ca

Browse files
committed
Added tests
1 parent 86702fa commit c5a54ca

7 files changed

Lines changed: 471 additions & 171 deletions

File tree

node-server/config.js

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
// Don't commit this file to your public repos
22
exports.creds = {
33
mongoose_auth_local: 'mongodb://localhost/tasklist', // Your mongo auth uri goes here
4-
openid-configuration: 'https://login.microsoftonline.com/common/.well-known/openid-configuration', // For using Microsoft you should never need to change this.
4+
openid_configuration: 'https://login.microsoftonline.com/common/.well-known/openid-configuration', // For using Microsoft you should never need to change this.
5+
openid_keys: 'https://login.microsoftonline.com/common/discovery/keys', // For using Microsoft you should never need to change this. If asbsent will attempt to get from openid_configuration
56
}

node-server/lib/aadutils.js

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -45,7 +45,9 @@ exports.getElement = function (parentElement, elementName) {
4545
return parentElement['samlp:'+elementName];
4646
} else if (parentElement['wsa:' + elementName]) {
4747
return parentElement['wsa:' + elementName];
48-
}
48+
} else if (parentElement['oidc:' + elementName]) {
49+
return parentElement['oidc:' + elementName];
50+
}
4951
return parentElement[elementName];
5052
};
5153

@@ -59,6 +61,8 @@ exports.getFirstElement = function (parentElement, elementName) {
5961
element = parentElement['samlp:'+elementName];
6062
} else if (parentElement['wsa:' + elementName]) {
6163
element = parentElement['wsa:' + elementName];
64+
} else if (parentElement['oidc:' + elementName]) {
65+
element = parentElement['oidc:' + elementName];
6266
} else {
6367
element = parentElement[elementName];
6468
}

node-server/lib/metadata.js

Lines changed: 127 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,25 @@ var request = require('request');
2222
var aadutils = require('./aadutils');
2323
var async = require('async');
2424

25-
var Metadata = function (url) {
25+
// Logging
26+
27+
var bunyan = require('bunyan');
28+
var log = bunyan.createLogger({name: 'Microsoft OpenID Connect Passport Strategy'});
29+
30+
var Metadata = function (url, authtype) {
31+
32+
2633
if(!url) {
2734
throw new Error("Metadata: url is a required argument");
2835
}
36+
if(!authtype) {
37+
throw new Error('OIDCBearerStrategy requires an authentication type specified to metadata parser. Valid types are saml, wsfed, or odic"');
38+
}
39+
2940
this.url = url;
3041
this.metadata = null;
42+
this.authtype = authtype;
43+
log.info(authtype, 'Metadata requested for authentication type');
3144
};
3245

3346
Object.defineProperty(Metadata, 'url', {
@@ -36,66 +49,162 @@ Object.defineProperty(Metadata, 'url', {
3649
}
3750
});
3851

39-
Object.defineProperty(Metadata, 'oidc', {
52+
Object.defineProperty(Metadata, 'saml', {
4053
get: function () {
4154
return this.saml;
4255
}
4356
});
4457

58+
Object.defineProperty(Metadata, 'wsfed', {
59+
get: function () {
60+
return this.wsfed;
61+
}
62+
});
63+
64+
Object.defineProperty(Metadata, 'oidc', {
65+
get: function () {
66+
return this.oidc;
67+
}
68+
});
69+
4570

46-
Metadata.prototype.updateOIDCMetadata = function(doc, next) {
71+
Object.defineProperty(Metadata, 'metadata', {
72+
get: function () {
73+
return this.metadata;
74+
}
75+
});
76+
77+
Metadata.prototype.updateSamlMetadata = function(doc, next) {
78+
log.info('Request to update the SAML Metadata');
4779
try {
48-
this.odic = {};
4980

50-
var issuer = aadutils.getElement(doc, 'EntityDescriptor');
51-
var token_endpoint = aadutils.getElement(entity, 'IDPSSODescriptor');
52-
var token_endpoint_auth_methods_supported = aadutils.getElement(entity, 'AuthMethodsSupported');
81+
this.saml = {};
82+
83+
var entity = aadutils.getElement(doc, 'EntityDescriptor');
84+
var idp = aadutils.getElement(entity, 'IDPSSODescriptor');
5385
var signOn = aadutils.getElement(idp[0], 'SingleSignOnService');
5486
var signOff = aadutils.getElement(idp[0], 'SingleLogoutService');
5587
var keyDescriptor = aadutils.getElement(idp[0], 'KeyDescriptor');
56-
this.odic.loginEndpoint = signOn[0].$.Location;
57-
this.oidc.logoutEndpoint = signOff[0].$.Location;
88+
this.saml.loginEndpoint = signOn[0].$.Location;
89+
this.saml.logoutEndpoint = signOff[0].$.Location;
5890

5991
// copy the x509 certs from the metadata
60-
this.odic.certs = []; // where we put certs
92+
this.saml.certs = [];
6193
for (var j=0;j<keyDescriptor.length;j++) {
62-
this.odic.certs.push(keyDescriptor[j].KeyInfo[0].X509Data[0].X509Certificate[0]);
94+
this.saml.certs.push(keyDescriptor[j].KeyInfo[0].X509Data[0].X509Certificate[0]);
6395
}
6496
next(null);
6597
} catch (e) {
66-
next(new Error('Invalid Open ID Connect Metadata' + e.message));
98+
next(new Error('Invalid SAMLP Federation Metadata ' + e.message));
6799
}
68100
};
69101

102+
Metadata.prototype.updateOidcMetadata = function(doc, next) {
103+
log.info('Request to update the Open ID Connect Metadata');
104+
try {
105+
this.oidc = {};
106+
107+
var issuer = doc['issuer'];
108+
var keyDescriptor = aadutils.getElement(idp[0], 'keys');
109+
110+
// copy the x509 certs from the metadata
111+
this.oidc.certs = [];
112+
for (var j=0;j<keyDescriptor.length;j++) {
113+
this.oidc.certs.push(keyDescriptor[j].KeyInfo[0].X509Data[0].X509Certificate[0]);
114+
}
115+
next(null);
116+
} catch (e) {
117+
next(new Error('Invalid Open ID Connect Federation Metadata ' + e.message));
118+
}
119+
};
120+
121+
122+
Metadata.prototype.updateWsfedMetadata = function(doc, next) {
123+
log.info('Request to update the WS Federation Metadata');
124+
try {
125+
this.wsfed = {};
126+
var entity = aadutils.getElement(doc, 'EntityDescriptor');
127+
var roles = aadutils.getElement(entity, 'RoleDescriptor');
128+
for(var i = 0; i < roles.length; i++) {
129+
var role = roles[i];
130+
if(role['fed:SecurityTokenServiceEndpoint']) {
131+
var endpoint = role['fed:SecurityTokenServiceEndpoint'];
132+
var endPointReference = aadutils.getFirstElement(endpoint[0],'EndpointReference');
133+
this.wsfed.loginEndpoint = aadutils.getFirstElement(endPointReference,'Address');
134+
135+
var keyDescriptor = aadutils.getElement(role, 'KeyDescriptor');
136+
// copy the x509 certs from the metadata
137+
this.wsfed.certs = [];
138+
for (var j=0;j<keyDescriptor.length;j++) {
139+
this.wsfed.certs.push(keyDescriptor[j].KeyInfo[0].X509Data[0].X509Certificate[0]);
140+
}
141+
break;
142+
}
143+
}
144+
145+
return next(null);
146+
} catch (e) {
147+
next(new Error('Invalid WSFED Federation Metadata ' + e.message));
148+
}
149+
};
70150

71151
Metadata.prototype.fetch = function(callback) {
72152
var self = this;
73-
153+
log.info("Fetching metadata from the provided metadata URL: " + self.url);
74154
async.waterfall([
75-
// fetch the metadata for the AAD tenant
155+
// fetch the Federation metadata for the AAD tenant
76156
function(next){
77157
request(self.url, function (err, response, body) {
78158
if(err) {
79159
next(err);
80160
} else if(response.statusCode !== 200) {
81161
next(new Error("Error:" + response.statusCode + " Cannot get AAD Federation metadata from " + self.url));
82162
} else {
163+
log.info(body, "retreived");
83164
next(null, body);
84165
}
85166
});
86167
},
87168
function(body, next){
169+
// parse the AAD Federation metadata xml
170+
171+
if(self.authtype == "saml" || self.authtype == "wsfed") {
172+
log.info(body, "Parsing XML retreived from the endpoint");
173+
var parser = new xml2js.Parser({explicitRoot:true});
88174
// Note: xml responses from Azure AAD have a leading \ufeff which breaks xml2js parser!
89-
JSON.parse(body, function (err, data) {
175+
parser.parseString(body.replace("\ufeff", ""), function (err, data) {
90176
self.metatdata = data;
91177
next(err);
92178

93179
});
180+
} else if(self.authtype == "oidc") {
181+
log.info(body, "Parsing JSON retreived from the endpoint");
182+
JSON.parse(body, function (err, data) {
183+
self.metatdata = data;
184+
next(err);
185+
});
186+
187+
} else {
188+
189+
next(new Error("Error: No Authentication type specified to metadata parser. Valid types are saml, wsfed, or odic"));
190+
}
191+
94192
},
193+
95194
function(next){
96-
// update the ODIC SSO endpoints and certs from the metadata
97-
self.updateODICMetadata(self.metatdata, next);
98-
},
195+
if(self.authtype = "saml") {
196+
// update the SAML SSO endpoints and certs from the metadata
197+
self.updateSamlMetadata(self.metatdata, next);
198+
}},
199+
function(next){
200+
if(self.authtype = "wsfed") {
201+
// update the SAML SSO endpoints and certs from the metadata
202+
self.updateWsfedMetadata(self.metatdata, next);
203+
}},
204+
function(next){
205+
if(self.authtype = "oidc") {
206+
self.updateOidcMetadata(self.metadata, next);
207+
}},
99208
], function (err) {
100209
// return err or success (err === null) to callback
101210
callback(err);

0 commit comments

Comments
 (0)