import 'u2f-api-polyfill' ; import api from 'teleport/services/api' ; import cfg from 'teleport/config' ; import makePasswordToken from './makePasswordToken' ; import { makeRecoveryCodes } from './makeRecoveryCodes' ; import { makeMfaAuthenticateChallenge , makeMfaRegistrationChallenge , makeWebauthnAssertionResponse , makeWebauthnCreationResponse , } from './makeMfa' ; import { DeviceType } from './types' ; const auth = { u2fBrowserSupported ( ) { if ( window [ 'u2f' ] ) { return null ; } return new Error ( 'this browser does not support U2F required for hardware tokens, please try Chrome or Firefox instead' ) ; } , checkWebauthnSupport ( ) { if ( window . PublicKeyCredential ) { return Promise . resolve ( ) ; } return Promise . reject ( new Error ( 'this browser does not support Webauthn required for hardware tokens, please try the latest version of Chrome, Firefox or Safari' ) ) ; } , createMfaRegistrationChallenge ( tokenId , deviceType ) { return api . post ( cfg . getMfaCreateRegistrationChallengeUrl ( tokenId ) , { deviceType , } ) . then ( makeMfaRegistrationChallenge ) ; } , createMfaAuthnChallengeWithToken ( tokenId ) { return api . post ( cfg . getAuthnChallengeWithTokenUrl ( tokenId ) ) . then ( makeMfaAuthenticateChallenge ) ; } , mfaLoginBegin ( user , pass ) { return api . post ( cfg . api . mfaLoginBegin , { user , pass } ) . then ( makeMfaAuthenticateChallenge ) ; } , mfaChangePasswordBegin ( oldPass ) { return api . post ( cfg . api . mfaChangePasswordBegin , { pass : oldPass } ) . then ( makeMfaAuthenticateChallenge ) ; } , login ( userId , password , token ) { const data = { user : userId , pass : password , second_factor_token : token , } ; return api . post ( cfg . api . sessionPath , data ) ; } , loginWithU2f ( name , password ) { const err = this . u2fBrowserSupported ( ) ; if ( err ) { return Promise . reject ( err ) ; } return auth . mfaLoginBegin ( name , password ) . then ( data => { const promise = new Promise ( ( resolve , reject ) => { const { appId , challenge , registeredKeys } = data . u2f ; window [ 'u2f' ] . sign ( appId , challenge , registeredKeys , function ( res ) { if ( res . errorCode ) { const err = auth . _getU2fErr ( res . errorCode ) ; reject ( err ) ; return ; } const response = { user : name , u2f_sign_response : res , } ; api . post ( cfg . api . mfaLoginFinish , response ) . then ( data => { resolve ( data ) ; } ) . catch ( data => { reject ( data ) ; } ) ; } ) ; } ) ; return promise ; } ) ; } , loginWithWebauthn ( user , pass ) { return auth . checkWebauthnSupport ( ) . then ( ( ) => auth . mfaLoginBegin ( user , pass ) ) . then ( res => navigator . credentials . get ( { publicKey : res . webauthnPublicKey , } ) ) . then ( res => { const request = { user , webauthnAssertionResponse : makeWebauthnAssertionResponse ( res ) , } ; return api . post ( cfg . api . mfaLoginFinish , request ) ; } ) ; } , fetchPasswordToken ( tokenId ) { const path = cfg . getPasswordTokenUrl ( tokenId ) ; return api . get ( path ) . then ( makePasswordToken ) ; } , resetPasswordWithWebauthn ( tokenId , password ) { return auth . checkWebauthnSupport ( ) . then ( ( ) => auth . createMfaRegistrationChallenge ( tokenId , 'webauthn' ) ) . then ( res => navigator . credentials . create ( { publicKey : res . webauthnPublicKey , } ) ) . then ( res => { const request = { token : tokenId , password : base64EncodeUnicode ( password ) , webauthnCreationResponse : makeWebauthnCreationResponse ( res ) , } ; return api . put ( cfg . getPasswordTokenUrl ( ) , request ) ; } ) . then ( makeRecoveryCodes ) ; } , resetPasswordWithU2f ( tokenId , password ) { const err = this . u2fBrowserSupported ( ) ; if ( err ) { return Promise . reject ( err ) ; } return auth . _getU2FRegisterRes ( tokenId ) . then ( u2fRes => { return auth . _resetPassword ( tokenId , password , null , u2fRes ) ; } ) ; } , resetPassword ( tokenId , password , hotpToken ) { return this . _resetPassword ( tokenId , password , hotpToken ) ; } , changePassword ( oldPass , newPass , token ) { const data = { old_password : base64EncodeUnicode ( oldPass ) , new_password : base64EncodeUnicode ( newPass ) , second_factor_token : token , } ; return api . put ( cfg . api . changeUserPasswordPath , data ) ; } , changePasswordWithU2f ( oldPass , newPass ) { const err = this . u2fBrowserSupported ( ) ; if ( err ) { return Promise . reject ( err ) ; } return auth . mfaChangePasswordBegin ( oldPass ) . then ( data => { return new Promise ( ( resolve , reject ) => { const { appId , challenge , registeredKeys } = data . u2f ; window [ 'u2f' ] . sign ( appId , challenge , registeredKeys , function ( res ) { if ( res . errorCode ) { const err = auth . _getU2fErr ( res . errorCode ) ; reject ( err ) ; return ; } const data = { new_password : base64EncodeUnicode ( newPass ) , u2f_sign_response : res , } ; api . put ( cfg . api . changeUserPasswordPath , data ) . then ( data => { resolve ( data ) ; } ) . catch ( data => { reject ( data ) ; } ) ; } ) ; } ) ; } ) ; } , changePasswordWithWebauthn ( oldPass , newPass ) { return auth . checkWebauthnSupport ( ) . then ( ( ) => api . post ( cfg . api . mfaChangePasswordBegin , { pass : oldPass } ) ) . then ( res => navigator . credentials . get ( { publicKey : makeMfaAuthenticateChallenge ( res ) . webauthnPublicKey , } ) ) . then ( res => { const request = { old_password : base64EncodeUnicode ( oldPass ) , new_password : base64EncodeUnicode ( newPass ) , webauthnAssertionResponse : makeWebauthnAssertionResponse ( res ) , } ; return api . put ( cfg . api . changeUserPasswordPath , request ) ; } ) ; } , createPrivilegeTokenWithTotp ( secondFactorToken ) { return api . post ( cfg . api . createPrivilegeTokenPath , { secondFactorToken } ) ; } , createPrivilegeTokenWithWebauthn ( ) { return auth . checkWebauthnSupport ( ) . then ( ( ) => api . post ( cfg . api . mfaAuthnChallengePath ) . then ( makeMfaAuthenticateChallenge ) ) . then ( res => navigator . credentials . get ( { publicKey : res . webauthnPublicKey , } ) ) . then ( res => api . post ( cfg . api . createPrivilegeTokenPath , { webauthnAssertionResponse : makeWebauthnAssertionResponse ( res ) , } ) ) ; } , createPrivilegeTokenWithU2f ( ) { const err = auth . u2fBrowserSupported ( ) ; if ( err ) { return Promise . reject ( err ) ; } return api . post ( cfg . api . mfaAuthnChallengePath ) . then ( data => { return new Promise ( ( resolve , reject ) => { let devices = [ data ] ; if ( data . u2f_challenges ) { devices = data . u2f_challenges ; } window [ 'u2f' ] . sign ( data . appId , data . challenge , devices , res => { if ( res . errorCode ) { const err = auth . _getU2fErr ( res . errorCode ) ; reject ( err ) ; return ; } api . post ( cfg . api . createPrivilegeTokenPath , { u2fSignResponse : res , } ) . then ( resolve ) . catch ( err => { reject ( err ) ; } ) ; } ) ; } ) ; } ) ; } , createRestrictedPrivilegeToken ( ) { return api . post ( cfg . api . createPrivilegeTokenPath , { } ) ; } , _resetPassword ( tokenId , psw , hotpToken , u2fResponse ) { const request = { password : base64EncodeUnicode ( psw ) , second_factor_token : hotpToken , token : tokenId , u2f_register_response : u2fResponse , } ; return api . put ( cfg . getPasswordTokenUrl ( ) , request ) . then ( makeRecoveryCodes ) ; } , _getU2FRegisterRes ( tokenId ) { return auth . createMfaRegistrationChallenge ( tokenId , 'u2f' ) . then ( data => { const challenge = data . u2fRegisterRequest ; return new Promise ( ( resolve , reject ) => { window [ 'u2f' ] . register ( challenge . appId , [ challenge ] , [ ] , function ( res ) { if ( res . errorCode ) { const err = auth . _getU2fErr ( res . errorCode ) ; reject ( err ) ; return ; } resolve ( res ) ; } ) ; } ) ; } ) ; } , _getU2fErr ( errorCode ) { let errorMsg = `error code ${ errorCode }` ; for ( var msg in window [ 'u2f' ] . ErrorCodes ) { if ( window [ 'u2f' ] . ErrorCodes [ msg ] == errorCode ) { errorMsg = msg ; } } let message = `Please check your U2F settings, make sure it is plugged in and you are using the supported browser.\nU2F error: ${ errorMsg }` ; return new Error ( message ) ; } , } ; function base64EncodeUnicode ( str ) { return window . btoa ( encodeURIComponent ( str ) . replace ( /%([0-9A-F]{2})/g , function ( match , p1 ) { const hexadecimalStr = '0x' + p1 ; return String . fromCharCode ( Number ( hexadecimalStr ) ) ; } ) ) ; } export default auth ;