mirror of
https://github.com/0rangebananaspy/authelia.git
synced 2024-09-14 22:47:21 +07:00
8f05846e21
This implements Webauthn. Old devices can be used to authenticate via the appid compatibility layer which should be automatic. New devices will be registered via Webauthn, and devices which do not support FIDO2 will no longer be able to be registered. At this time it does not fully support multiple devices (backend does, frontend doesn't allow registration of additional devices). Does not support passwordless.
168 lines
4.4 KiB
Go
168 lines
4.4 KiB
Go
package models
|
|
|
|
import (
|
|
"encoding/hex"
|
|
"strings"
|
|
"time"
|
|
|
|
"github.com/duo-labs/webauthn/protocol"
|
|
"github.com/duo-labs/webauthn/webauthn"
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
const (
|
|
attestationTypeFIDOU2F = "fido-u2f"
|
|
)
|
|
|
|
// WebauthnUser is an object to represent a user for the Webauthn lib.
|
|
type WebauthnUser struct {
|
|
Username string
|
|
DisplayName string
|
|
Devices []WebauthnDevice
|
|
}
|
|
|
|
// HasFIDOU2F returns true if the user has any attestation type `fido-u2f` devices.
|
|
func (w WebauthnUser) HasFIDOU2F() bool {
|
|
for _, c := range w.Devices {
|
|
if c.AttestationType == attestationTypeFIDOU2F {
|
|
return true
|
|
}
|
|
}
|
|
|
|
return false
|
|
}
|
|
|
|
// WebAuthnID implements the webauthn.User interface.
|
|
func (w WebauthnUser) WebAuthnID() []byte {
|
|
return []byte(w.Username)
|
|
}
|
|
|
|
// WebAuthnName implements the webauthn.User interface.
|
|
func (w WebauthnUser) WebAuthnName() string {
|
|
return w.Username
|
|
}
|
|
|
|
// WebAuthnDisplayName implements the webauthn.User interface.
|
|
func (w WebauthnUser) WebAuthnDisplayName() string {
|
|
return w.DisplayName
|
|
}
|
|
|
|
// WebAuthnIcon implements the webauthn.User interface.
|
|
func (w WebauthnUser) WebAuthnIcon() string {
|
|
return ""
|
|
}
|
|
|
|
// WebAuthnCredentials implements the webauthn.User interface.
|
|
func (w WebauthnUser) WebAuthnCredentials() (credentials []webauthn.Credential) {
|
|
credentials = make([]webauthn.Credential, len(w.Devices))
|
|
|
|
var credential webauthn.Credential
|
|
|
|
for i, device := range w.Devices {
|
|
aaguid, err := device.AAGUID.MarshalBinary()
|
|
if err != nil {
|
|
continue
|
|
}
|
|
|
|
credential = webauthn.Credential{
|
|
ID: device.KID.Bytes(),
|
|
PublicKey: device.PublicKey,
|
|
AttestationType: device.AttestationType,
|
|
Authenticator: webauthn.Authenticator{
|
|
AAGUID: aaguid,
|
|
SignCount: device.SignCount,
|
|
CloneWarning: device.CloneWarning,
|
|
},
|
|
}
|
|
|
|
transports := strings.Split(device.Transport, ",")
|
|
credential.Transport = []protocol.AuthenticatorTransport{}
|
|
|
|
for _, t := range transports {
|
|
if t == "" {
|
|
continue
|
|
}
|
|
|
|
credential.Transport = append(credential.Transport, protocol.AuthenticatorTransport(t))
|
|
}
|
|
|
|
credentials[i] = credential
|
|
}
|
|
|
|
return credentials
|
|
}
|
|
|
|
// WebAuthnCredentialDescriptors decodes the users credentials into protocol.CredentialDescriptor's.
|
|
func (w WebauthnUser) WebAuthnCredentialDescriptors() (descriptors []protocol.CredentialDescriptor) {
|
|
credentials := w.WebAuthnCredentials()
|
|
|
|
descriptors = make([]protocol.CredentialDescriptor, len(credentials))
|
|
|
|
for i, credential := range credentials {
|
|
descriptors[i] = credential.Descriptor()
|
|
}
|
|
|
|
return descriptors
|
|
}
|
|
|
|
// NewWebauthnDeviceFromCredential creates a WebauthnDevice from a webauthn.Credential.
|
|
func NewWebauthnDeviceFromCredential(rpid, username, description string, credential *webauthn.Credential) (device WebauthnDevice) {
|
|
transport := make([]string, len(credential.Transport))
|
|
|
|
for i, t := range credential.Transport {
|
|
transport[i] = string(t)
|
|
}
|
|
|
|
device = WebauthnDevice{
|
|
RPID: rpid,
|
|
Username: username,
|
|
CreatedAt: time.Now(),
|
|
Description: description,
|
|
KID: NewBase64(credential.ID),
|
|
PublicKey: credential.PublicKey,
|
|
AttestationType: credential.AttestationType,
|
|
SignCount: credential.Authenticator.SignCount,
|
|
CloneWarning: credential.Authenticator.CloneWarning,
|
|
Transport: strings.Join(transport, ","),
|
|
}
|
|
|
|
device.AAGUID, _ = uuid.Parse(hex.EncodeToString(credential.Authenticator.AAGUID))
|
|
|
|
return device
|
|
}
|
|
|
|
// WebauthnDevice represents a Webauthn Device in the database storage.
|
|
type WebauthnDevice struct {
|
|
ID int `db:"id"`
|
|
CreatedAt time.Time `db:"created_at"`
|
|
LastUsedAt *time.Time `db:"last_used_at"`
|
|
RPID string `db:"rpid"`
|
|
Username string `db:"username"`
|
|
Description string `db:"description"`
|
|
KID Base64 `db:"kid"`
|
|
PublicKey []byte `db:"public_key"`
|
|
AttestationType string `db:"attestation_type"`
|
|
Transport string `db:"transport"`
|
|
AAGUID uuid.UUID `db:"aaguid"`
|
|
SignCount uint32 `db:"sign_count"`
|
|
CloneWarning bool `db:"clone_warning"`
|
|
}
|
|
|
|
// UpdateSignInInfo adjusts the values of the WebauthnDevice after a sign in.
|
|
func (w *WebauthnDevice) UpdateSignInInfo(config *webauthn.Config, now time.Time, signCount uint32) {
|
|
w.LastUsedAt = &now
|
|
|
|
w.SignCount = signCount
|
|
|
|
if w.RPID != "" {
|
|
return
|
|
}
|
|
|
|
switch w.AttestationType {
|
|
case attestationTypeFIDOU2F:
|
|
w.RPID = config.RPOrigin
|
|
default:
|
|
w.RPID = config.RPID
|
|
}
|
|
}
|