2022-03-03 11:20:43 +00:00
package handlers
import (
"net/url"
2023-01-07 03:21:27 +00:00
"strings"
2022-03-03 11:20:43 +00:00
2022-03-03 23:46:38 +00:00
"github.com/go-webauthn/webauthn/protocol"
"github.com/go-webauthn/webauthn/webauthn"
2022-03-03 11:20:43 +00:00
"github.com/authelia/authelia/v4/internal/middlewares"
2022-03-06 05:47:40 +00:00
"github.com/authelia/authelia/v4/internal/model"
2023-02-16 19:40:40 +00:00
"github.com/authelia/authelia/v4/internal/random"
2022-03-03 11:20:43 +00:00
)
2023-04-11 04:40:09 +00:00
func getWebAuthnUserByRPID ( ctx * middlewares . AutheliaCtx , username , displayname string , rpid string ) ( user * model . WebAuthnUser , err error ) {
if user , err = ctx . Providers . StorageProvider . LoadWebAuthnUser ( ctx , rpid , username ) ; err != nil {
2023-02-16 19:40:40 +00:00
return nil , err
}
if user == nil {
2023-04-11 04:40:09 +00:00
user = & model . WebAuthnUser {
2023-02-16 19:40:40 +00:00
RPID : rpid ,
Username : username ,
UserID : ctx . Providers . Random . StringCustom ( 64 , random . CharSetASCII ) ,
2023-02-19 01:40:57 +00:00
DisplayName : displayname ,
2023-02-16 19:40:40 +00:00
}
2023-02-11 15:47:03 +00:00
2023-04-11 04:40:09 +00:00
if err = ctx . Providers . StorageProvider . SaveWebAuthnUser ( ctx , * user ) ; err != nil {
2023-02-16 19:40:40 +00:00
return nil , err
}
2023-02-19 01:40:57 +00:00
} else {
user . DisplayName = displayname
2022-03-03 11:20:43 +00:00
}
if user . DisplayName == "" {
user . DisplayName = user . Username
}
2023-04-11 04:40:09 +00:00
if user . Devices , err = ctx . Providers . StorageProvider . LoadWebAuthnDevicesByUsername ( ctx , rpid , user . Username ) ; err != nil {
2022-03-03 11:20:43 +00:00
return nil , err
}
return user , nil
}
2023-04-11 04:40:09 +00:00
func newWebAuthn ( ctx * middlewares . AutheliaCtx ) ( w * webauthn . WebAuthn , err error ) {
2022-03-03 11:20:43 +00:00
var (
2023-02-11 15:47:03 +00:00
origin * url . URL
2022-03-03 11:20:43 +00:00
)
2023-02-11 15:47:03 +00:00
if origin , err = ctx . GetOrigin ( ) ; err != nil {
2022-03-03 11:20:43 +00:00
return nil , err
}
config := & webauthn . Config {
2023-02-19 00:48:35 +00:00
RPID : origin . Hostname ( ) ,
2023-04-11 04:40:09 +00:00
RPDisplayName : ctx . Configuration . WebAuthn . DisplayName ,
2023-02-19 00:48:35 +00:00
RPOrigins : [ ] string { origin . String ( ) } ,
2023-04-11 04:40:09 +00:00
AttestationPreference : ctx . Configuration . WebAuthn . ConveyancePreference ,
2022-03-03 11:20:43 +00:00
AuthenticatorSelection : protocol . AuthenticatorSelection {
AuthenticatorAttachment : protocol . CrossPlatform ,
2022-03-03 23:46:38 +00:00
RequireResidentKey : protocol . ResidentKeyNotRequired ( ) ,
2023-02-19 00:48:35 +00:00
ResidentKey : protocol . ResidentKeyRequirementDiscouraged ,
2023-04-11 04:40:09 +00:00
UserVerification : ctx . Configuration . WebAuthn . UserVerification ,
2023-02-19 00:48:35 +00:00
} ,
Debug : false ,
EncodeUserIDAsString : true ,
Timeouts : webauthn . TimeoutsConfig {
Login : webauthn . TimeoutConfig {
Enforce : true ,
2023-04-11 04:40:09 +00:00
Timeout : ctx . Configuration . WebAuthn . Timeout ,
TimeoutUVD : ctx . Configuration . WebAuthn . Timeout ,
2023-02-19 00:48:35 +00:00
} ,
Registration : webauthn . TimeoutConfig {
Enforce : true ,
2023-04-11 04:40:09 +00:00
Timeout : ctx . Configuration . WebAuthn . Timeout ,
TimeoutUVD : ctx . Configuration . WebAuthn . Timeout ,
2023-02-19 00:48:35 +00:00
} ,
2022-03-03 11:20:43 +00:00
} ,
}
2023-04-11 04:40:09 +00:00
ctx . Logger . Tracef ( "Creating new WebAuthn RP instance with ID %s and Origins %s" , config . RPID , strings . Join ( config . RPOrigins , ", " ) )
2022-03-03 11:20:43 +00:00
return webauthn . New ( config )
}
2023-02-18 04:36:58 +00:00
func webauthnCredentialCreationIsDiscoverable ( ctx * middlewares . AutheliaCtx , response * protocol . ParsedCredentialCreationData ) ( discoverable bool ) {
if value , ok := response . ClientExtensionResults [ "credProps" ] ; ok {
switch credentialProperties := value . ( type ) {
case map [ string ] any :
var v any
if v , ok = credentialProperties [ "rk" ] ; ok {
if discoverable , ok = v . ( bool ) ; ok {
ctx . Logger . WithFields ( map [ string ] any { "discoverable" : discoverable } ) . Trace ( "Determined Credential Discoverability via Client Extension Results" )
return discoverable
} else {
ctx . Logger . WithFields ( map [ string ] any { "discoverable" : false } ) . Trace ( "Assuming Credential Discoverability is false as the 'rk' field for the 'credProps' extension in the Client Extension Results was not a boolean" )
}
} else {
ctx . Logger . WithFields ( map [ string ] any { "discoverable" : false } ) . Trace ( "Assuming Credential Discoverability is false as the 'rk' field for the 'credProps' extension was missing from the Client Extension Results" )
}
return false
default :
ctx . Logger . WithFields ( map [ string ] any { "discoverable" : false } ) . Trace ( "Assuming Credential Discoverability is false as the 'credProps' extension in the Client Extension Results does not appear to be a dictionary" )
return false
}
}
ctx . Logger . WithFields ( map [ string ] any { "discoverable" : false } ) . Trace ( "Assuming Credential Discoverability is false as the 'credProps' extension is missing from the Client Extension Results" )
return false
}