implemented web login with U2F

This commit is contained in:
jcj83429 2016-10-27 16:37:10 -07:00
parent a237d22c15
commit 4beaa2594e
4 changed files with 91 additions and 4 deletions

View file

@ -22,7 +22,7 @@ var {actions, getters} = require('app/modules/user');
var GoogleAuthInfo = require('./googleAuthLogo');
var cfg = require('app/config');
var {TeleportLogo} = require('./icons.jsx');
var {PROVIDER_GOOGLE} = require('app/services/auth');
var {PROVIDER_GOOGLE, SECOND_FACTOR_TYPE_HOTP, SECOND_FACTOR_TYPE_OIDC, SECOND_FACTOR_TYPE_U2F} = require('app/services/auth');
var LoginInputForm = React.createClass({
@ -33,13 +33,17 @@ var LoginInputForm = React.createClass({
user: '',
password: '',
token: '',
provider: null
provider: null,
second_factor_type: SECOND_FACTOR_TYPE_HOTP
}
},
onLogin(e){
e.preventDefault();
this.state.second_factor_type = SECOND_FACTOR_TYPE_HOTP;
// token field is required for Google Authenticator
$('input[name=token]').addClass("required");
if (this.isValid()) {
this.props.onClick(this.state);
}
@ -47,10 +51,21 @@ var LoginInputForm = React.createClass({
onLoginWithGoogle: function(e) {
e.preventDefault();
this.state.second_factor_type = SECOND_FACTOR_TYPE_OIDC;
this.state.provider = PROVIDER_GOOGLE;
this.props.onClick(this.state);
},
onLoginWithU2f: function(e) {
e.preventDefault();
this.state.second_factor_type = SECOND_FACTOR_TYPE_U2F;
// token field not required for U2F
$('input[name=token]').removeClass("required");
if (this.isValid()) {
this.props.onClick(this.state);
}
},
isValid: function() {
var $form = $(this.refs.form);
return $form.length === 0 || $form.valid();
@ -75,7 +90,9 @@ var LoginInputForm = React.createClass({
<input autoComplete="off" valueLink={this.linkState('token')} className="form-control required" name="token" placeholder="Two factor token (Google Authenticator)"/>
</div>
<button onClick={this.onLogin} disabled={isProcessing} type="submit" className="btn btn-primary block full-width m-b">Login</button>
<button onClick={this.onLoginWithU2f} disabled={isProcessing} type="submit" className="btn btn-primary block full-width m-b">Login with U2F</button>
{ useGoogle ? <button onClick={this.onLoginWithGoogle} type="submit" className="btn btn-danger block full-width m-b">With Google</button> : null }
{ isProcessing && this.state.second_factor_type == SECOND_FACTOR_TYPE_U2F ? (<label className="help-block">Insert your U2F key and press the button on the key</label>) : null }
{ isFailed ? (<label className="error">{message}</label>) : null }
</div>
</form>

View file

@ -49,6 +49,10 @@ let cfg = {
sessionPath: '/v1/webapi/sessions',
invitePath: '/v1/webapi/users/invites/:inviteToken',
createUserPath: '/v1/webapi/users',
u2fCreateUserChallengePath: '/webapi/u2f/invite_register_request/:inviteToken',
u2fCreateUserPath: '/webapi/u2f/new_user',
u2fSessionChallengePath: '/webapi/u2f/sign_request',
u2fSessionPath: '/webapi/u2f/sessions',
nodesPath: '/v1/webapi/sites/-current-/nodes',
siteSessionPath: '/v1/webapi/sites/-current-/sessions',
sessionEventsPath: '/v1/webapi/sites/-current-/sessions/:sid/events',
@ -84,6 +88,10 @@ let cfg = {
return formatPattern(cfg.api.invitePath, {inviteToken});
},
getU2fCreateUserChallengeUrl(inviteToken){
return formatPattern(cfg.api.u2fCreateUserChallengePath, {inviteToken});
},
getEventStreamConnStr(){
var hostname = getWsHostName();
return `${hostname}/v1/webapi/sites/-current-`;

View file

@ -22,6 +22,7 @@ var auth = require('app/services/auth');
var session = require('app/services/session');
var cfg = require('app/config');
var api = require('app/services/api');
var {SECOND_FACTOR_TYPE_OIDC, SECOND_FACTOR_TYPE_U2F} = require('app/services/auth');
export default {
@ -70,14 +71,27 @@ export default {
});
},
login({user, password, token, provider}, redirect){
if(provider){
login({user, password, token, provider, second_factor_type}, redirect){
if(second_factor_type == SECOND_FACTOR_TYPE_OIDC){
let fullPath = cfg.getFullUrl(redirect);
window.location = cfg.api.getSsoUrl(fullPath, provider);
return;
}
restApiActions.start(TRYING_TO_LOGIN);
if(second_factor_type == SECOND_FACTOR_TYPE_U2F){
// Because the U2f API is asynchronous, we have to pass in callbacks
auth.u2fLogin(user, password, function(sessionData){
restApiActions.success(TRYING_TO_LOGIN);
reactor.dispatch(TLPT_RECEIVE_USER, sessionData.user);
session.getHistory().push({pathname: redirect});
}, function(msg){
restApiActions.fail(TRYING_TO_LOGIN, msg);
});
return
}
auth.login(user, password, token)
.done((sessionData)=>{
restApiActions.success(TRYING_TO_LOGIN);

View file

@ -18,9 +18,14 @@ var api = require('./api');
var session = require('./session');
var cfg = require('app/config');
var $ = require('jQuery');
require('u2f-api-polyfill'); // This puts it in window.u2f
const PROVIDER_GOOGLE = 'google';
const SECOND_FACTOR_TYPE_HOTP = 'hotp';
const SECOND_FACTOR_TYPE_OIDC = 'oidc';
const SECOND_FACTOR_TYPE_U2F = 'u2f';
const CHECK_TOKEN_REFRESH_RATE = 10*1000; // 10 sec
var refreshTokenTimerId = null;
@ -54,6 +59,46 @@ var auth = {
});
},
u2fLogin(name, password, successCb, errorCb){
auth._stopTokenRefresher();
session.clear();
var data = {
user: name,
pass: password
}
api.post(cfg.api.u2fSessionChallengePath, data, false).then(data=>{
window.u2f.sign(data.appId, data.challenge, [data],function(res){
if(res.errorCode){
var errorMsg;
// lookup error message...
for(var err in window.u2f.ErrorCodes){
if(window.u2f.ErrorCodes[err] == res.errorCode){
errorMsg = err;
}
}
errorCb("U2F Error: " + errorMsg);
return;
}
var response = {
user: name,
u2f_sign_response: res
}
api.post(cfg.api.u2fSessionPath, response, false).then(data=>{
session.setUserData(data);
auth._startTokenRefresher();
successCb(data);
}).fail(data=>{
errorCb(data.responseJSON);
})
});
}).fail(data=>{
errorCb(data.responseJSON);
})
},
ensureUser(){
this._stopTokenRefresher();
@ -119,3 +164,6 @@ var auth = {
module.exports = auth;
module.exports.PROVIDER_GOOGLE = PROVIDER_GOOGLE;
module.exports.SECOND_FACTOR_TYPE_HOTP = SECOND_FACTOR_TYPE_HOTP;
module.exports.SECOND_FACTOR_TYPE_OIDC = SECOND_FACTOR_TYPE_OIDC;
module.exports.SECOND_FACTOR_TYPE_U2F = SECOND_FACTOR_TYPE_U2F;