Started work on OAuth support.

This commit is contained in:
Ylian Saint-Hilaire 2020-05-13 20:49:58 -07:00
parent 0d54e39ace
commit 906d43d367
42 changed files with 107 additions and 4 deletions

View File

@ -502,6 +502,7 @@
<Folder Include="typings\globals\node-forge\" />
<Folder Include="typings\globals\nodemailer\" />
<Folder Include="typings\globals\node\" />
<Folder Include="typings\globals\passport\" />
<Folder Include="views\" />
</ItemGroup>
<ItemGroup>
@ -512,6 +513,7 @@
<TypeScriptCompile Include="typings\globals\node-forge\index.d.ts" />
<TypeScriptCompile Include="typings\globals\nodemailer\index.d.ts" />
<TypeScriptCompile Include="typings\globals\node\index.d.ts" />
<TypeScriptCompile Include="typings\globals\passport\index.d.ts" />
<TypeScriptCompile Include="typings\index.d.ts" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.Common.targets" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

Binary file not shown.

View File

@ -2537,9 +2537,10 @@ function mainStart() {
// Lowercase the auth value if present
for (var i in config.domains) { if (typeof config.domains[i].auth == 'string') { config.domains[i].auth = config.domains[i].auth.toLowerCase(); } }
// Check if Windows SSPI and YubiKey OTP will be used
// Check if Windows SSPI, LDAP, Passport and YubiKey OTP will be used
var sspi = false;
var ldap = false;
var passport = null;
var allsspi = true;
var yubikey = false;
var recordingIndex = false;
@ -2549,6 +2550,12 @@ function mainStart() {
for (var i in config.domains) {
if (config.domains[i].yubikey != null) { yubikey = true; }
if (config.domains[i].auth == 'ldap') { ldap = true; }
if ((typeof config.domains[i].authstrategies == 'object')) {
if (passport == null) { passport = ['passport']; }
if ((typeof config.domains[i].authstrategies.twitter == 'object') && (typeof config.domains[i].authstrategies.twitter.apikey == 'string') && (typeof config.domains[i].authstrategies.twitter.apisecret == 'string') && (passport.indexOf('passport-twitter') == -1)) { passport.push('passport-twitter'); }
if ((typeof config.domains[i].authstrategies.google == 'object') && (typeof config.domains[i].authstrategies.google.clientid == 'string') && (typeof config.domains[i].authstrategies.google.clientsecret == 'string') && (passport.indexOf('passport-google-oauth20') == -1)) { passport.push('passport-google-oauth20'); }
if ((typeof config.domains[i].authstrategies.github == 'object') && (typeof config.domains[i].authstrategies.github.clientid == 'string') && (typeof config.domains[i].authstrategies.github.clientsecret == 'string') && (passport.indexOf('passport-github2') == -1)) { passport.push('passport-github2'); }
}
if ((config.domains[i].sessionrecording != null) && (config.domains[i].sessionrecording.index == true)) { recordingIndex = true; }
}
@ -2559,6 +2566,7 @@ function mainStart() {
var modules = ['ws', 'cbor', 'nedb', 'https', 'yauzl', 'xmldom', 'ipcheck', 'express', 'archiver', 'multiparty', 'node-forge', 'express-ws', 'compression', 'body-parser', 'connect-redis', 'cookie-session', 'express-handlebars'];
if (require('os').platform() == 'win32') { modules.push('node-windows'); if (sspi == true) { modules.push('node-sspi'); } } // Add Windows modules
if (ldap == true) { modules.push('ldapauth-fork'); }
if (passport != null) { modules.push(...passport); }
if (recordingIndex == true) { modules.push('image-size'); } // Need to get the remote desktop JPEG sizes to index the recodring file.
if (config.letsencrypt != null) { if (nodeVersion < 8) { addServerWarning("Let's Encrypt support requires Node v8.x or higher.", !args.launch); } else { modules.push('acme-client'); } } // Add acme-client module
if (config.settings.mqtt != null) { modules.push('aedes'); } // Add MQTT Modules

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.1 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.2 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 741 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 844 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.9 KiB

View File

@ -70,6 +70,13 @@
Don&#39;t have an account? <a onclick="return xgo(2,event);" href="#" style=cursor:pointer>Create one</a>.
</div>
<input id=loginformargs name="urlargs" type="hidden" value="" />
<div id="authStrategies" style="display:none">
<hr />
<div style="margin-bottom:8px">Log in using an existing account</div>
<a id="auth-twitter" href="auth-twitter" style="display:none"><img src="images/login/twitter32.png" loading="lazy" width="32" height="32" style="margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer" title="Sign-in using Twitter" /></a>
<a id="auth-google" href="auth-google" style="display:none"><img src="images/login/google32.png" loading="lazy" width="32" height="32" style="margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer" title="Sign-in using Google" /></a>
<a id="auth-github" href="auth-github" style="display:none"><img src="images/login/github32.png" loading="lazy" width="32" height="32" style="margin-left:3px;margin-right:3px;border-radius:3px;box-shadow:2px 2px 5px black;cursor:pointer" title="Sign-in using GitHub" /></a>
</div>
</form>
</div>
<div id=createpanel style="display:none;position:relative">
@ -302,6 +309,7 @@
var otpemail = (decodeURIComponent('{{{otpemail}}}') === 'true');
var otpsms = (decodeURIComponent('{{{otpsms}}}') === 'true');
var twoFactorCookieDays = parseInt('{{{twoFactorCookieDays}}}');
var authStrategies = '{{{authStrategies}}}'.split(',');
function startup() {
// Display the right server message
@ -368,6 +376,14 @@
QV('createPanelHint', passRequirements.hint === true);
QV('resetpasswordpanelHint', passRequirements.hint === true);
// Setup authentication strategies
if (authStrategies != '') {
QV('authStrategies', true);
if (authStrategies.indexOf('twitter') >= 0) { QV('auth-twitter', true); }
if (authStrategies.indexOf('google') >= 0) { QV('auth-google', true); }
if (authStrategies.indexOf('github') >= 0) { QV('auth-github', true); }
}
// Display the welcome text
if (welcomeText) {
QH('welcomeText', welcomeText);

View File

@ -1982,8 +1982,16 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
var twoFactorCookieDays = 30;
if (typeof domain.twofactorcookiedurationdays == 'number') { twoFactorCookieDays = domain.twofactorcookiedurationdays; }
// See what authentication strategies we have
var authStrategies = [];
if (typeof domain.authstrategies == 'object') {
if ((typeof domain.authstrategies.twitter == 'object') && (typeof domain.authstrategies.twitter.apikey == 'string') && (typeof domain.authstrategies.twitter.apisecret == 'string')) { authStrategies.push('twitter'); }
if ((typeof domain.authstrategies.google == 'object') && (typeof domain.authstrategies.google.clientid == 'string') && (typeof domain.authstrategies.google.clientsecret == 'string')) { authStrategies.push('google'); }
if ((typeof domain.authstrategies.github == 'object') && (typeof domain.authstrategies.github.clientid == 'string') && (typeof domain.authstrategies.github.clientsecret == 'string')) { authStrategies.push('github'); }
}
// Render the login page
render(req, res, getRenderPage('login', req, domain), getRenderArgs({ loginmode: loginmode, rootCertLink: getRootCertLink(), newAccount: newAccountsAllowed, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: encodeURIComponent(hardwareKeyChallenge), messageid: msgid, passhint: passhint, welcometext: domain.welcometext ? encodeURIComponent(domain.welcometext).split('\'').join('\\\'') : null, hwstate: hwstate, otpemail: otpemail, otpsms: otpsms, twoFactorCookieDays: twoFactorCookieDays }, req, domain));
render(req, res, getRenderPage('login', req, domain), getRenderArgs({ loginmode: loginmode, rootCertLink: getRootCertLink(), newAccount: newAccountsAllowed, newAccountPass: (((domain.newaccountspass == null) || (domain.newaccountspass == '')) ? 0 : 1), serverDnsName: obj.getWebServerName(domain), serverPublicPort: httpsPort, emailcheck: emailcheck, features: features, sessiontime: args.sessiontime, passRequirements: passRequirements, footer: (domain.footer == null) ? '' : domain.footer, hkey: encodeURIComponent(hardwareKeyChallenge), messageid: msgid, passhint: passhint, welcometext: domain.welcometext ? encodeURIComponent(domain.welcometext).split('\'').join('\\\'') : null, hwstate: hwstate, otpemail: otpemail, otpsms: otpsms, twoFactorCookieDays: twoFactorCookieDays, authStrategies: authStrategies.join(',') }, req, domain));
}
// Handle a post request on the root
@ -3917,7 +3925,8 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
if (parent.multiServer != null) { obj.app.ws('/meshserver.ashx', function (ws, req) { parent.multiServer.CreatePeerInServer(parent.multiServer, ws, req); }); }
for (var i in parent.config.domains) {
if (parent.config.domains[i].dns != null) { continue; } // This is a subdomain with a DNS name, no added HTTP bindings needed.
var url = parent.config.domains[i].url;
var domain = parent.config.domains[i];
var url = domain.url;
obj.app.get(url, handleRootRequest);
obj.app.post(url, handleRootPostRequest);
obj.app.get(url + 'backup.zip', handleBackupRequest);
@ -3969,7 +3978,7 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
}
});
});
if (parent.config.domains[i].agentinvitecodes == true) {
if (domain.agentinvitecodes == true) {
obj.app.get(url + 'invite', handleInviteRequest);
obj.app.post(url + 'invite', handleInviteRequest);
}
@ -3979,6 +3988,74 @@ module.exports.CreateWebServer = function (parent, db, args, certificates) {
obj.app.get(url + 'pluginHandler.js', obj.handlePluginJS);
}
// Setup passport if needed
if (typeof domain.authstrategies == 'object') {
const passport = domain.passport = require('passport');
if ((typeof domain.authstrategies.twitter == 'object') && (typeof domain.authstrategies.twitter.apikey == 'string') && (typeof domain.authstrategies.twitter.apisecret == 'string')) {
const TwitterStrategy = require('passport-twitter');
passport.use(new TwitterStrategy({
consumerKey: domain.authstrategies.twitter.apikey,
consumerSecret: domain.authstrategies.twitter.apisecret,
callbackURL: url + 'auth-twitter-callback'
},
function (token, tokenSecret, profile, cb) {
console.log('Twitter1', token, tokenSecret, profile);
//User.findOrCreate({ twitterId: profile.id }, function (err, user) { return cb(err, user); });
}
));
obj.app.get(url + 'auth-twitter', domain.passport.authenticate('twitter'));
obj.app.get(url + 'https://alt.meshcentral.com',
domain.passport.authenticate('twitter', { failureRedirect: '/' }),
function (req, res) {
// Successful authentication, redirect home.
console.log('Twitter2');
res.redirect('/');
});
}
if ((typeof domain.authstrategies.google == 'object') && (typeof domain.authstrategies.google.clientid == 'string') && (typeof domain.authstrategies.google.clientsecret == 'string')) {
const GoogleStrategy = require('passport-google-oauth20');
passport.use(new GoogleStrategy({
clientID: domain.authstrategies.google.clientid,
clientSecret: domain.authstrategies.google.clientsecret,
callbackURL: url + 'auth-google-callback'
},
function (token, tokenSecret, profile, cb) {
console.log('Google1', token, tokenSecret, profile);
//User.findOrCreate({ googleId: profile.id }, function (err, user) { return cb(err, user); });
}
));
obj.app.get(url + 'auth-google', domain.passport.authenticate('google'));
obj.app.get(url + 'auth-google-callback',
domain.passport.authenticate('google', { failureRedirect: '/' }),
function (req, res) {
// Successful authentication, redirect home.
console.log('Google2');
res.redirect('/');
});
}
if ((typeof domain.authstrategies.github == 'object') && (typeof domain.authstrategies.github.clientid == 'string') && (typeof domain.authstrategies.github.clientsecret == 'string')) {
const GitHubStrategy = require('passport-github2');
passport.use(new GitHubStrategy({
clientID: domain.authstrategies.github.clientid,
clientSecret: domain.authstrategies.github.clientsecret,
callbackURL: url + 'auth-github-callback'
},
function (token, tokenSecret, profile, cb) {
console.log('GitHub1', token, tokenSecret, profile);
//User.findOrCreate({ githubId: profile.id }, function (err, user) { return cb(err, user); });
}
));
obj.app.get(url + 'auth-github', domain.passport.authenticate('github', { scope: ['user:email'] }));
obj.app.get(url + 'auth-github-callback',
domain.passport.authenticate('google', { failureRedirect: '/' }),
function (req, res) {
// Successful authentication, redirect home.
console.log('GitHub2');
res.redirect('/');
});
}
}
// Server redirects
if (parent.config.domains[i].redirects) { for (var j in parent.config.domains[i].redirects) { if (j[0] != '_') { obj.app.get(url + j, obj.handleDomainRedirect); } } }