Add configuration files, database migrations, and authentication implementation scaffolding

This commit is contained in:
Sebastian Unterschütz
2026-04-30 19:08:07 +02:00
commit 331d60581e
83 changed files with 222264 additions and 0 deletions

View File

@@ -0,0 +1,155 @@
package webauthn
import (
"crypto/rand"
"crypto/sha256"
"encoding/base64"
"encoding/json"
"fmt"
)
// PublicKeyCredentialRequestOptions represents the WebAuthn authentication challenge
type PublicKeyCredentialRequestOptions struct {
Challenge string `json:"challenge"`
Timeout int `json:"timeout"`
RPID string `json:"rpId"`
AllowCredentials []string `json:"allowCredentials,omitempty"`
UserVerification string `json:"userVerification"`
}
// PublicKeyCredentialCreationOptions represents the WebAuthn registration challenge
type PublicKeyCredentialCreationOptions struct {
Challenge string `json:"challenge"`
RP RelyingParty `json:"rp"`
User User `json:"user"`
PubKeyCredParams []PubKeyCredParam `json:"pubKeyCredParams"`
Timeout int `json:"timeout"`
Attestation string `json:"attestation"`
AuthenticatorSelection AuthenticatorSelection `json:"authenticatorSelection"`
}
type RelyingParty struct {
Name string `json:"name"`
ID string `json:"id"`
}
type User struct {
ID string `json:"id"`
Name string `json:"name"`
DisplayName string `json:"displayName"`
}
type PubKeyCredParam struct {
Type string `json:"type"`
Alg int `json:"alg"`
}
type AuthenticatorSelection struct {
AuthenticatorAttachment string `json:"authenticatorAttachment,omitempty"`
RequireResidentKey bool `json:"requireResidentKey"`
UserVerification string `json:"userVerification"`
}
// GenerateChallenge creates a cryptographically secure random challenge
func GenerateChallenge() (string, error) {
challenge := make([]byte, 32)
_, err := rand.Read(challenge)
if err != nil {
return "", err
}
return base64.RawURLEncoding.EncodeToString(challenge), nil
}
// CreateRegistrationOptions generates WebAuthn registration options
func CreateRegistrationOptions(userID, username, displayName, rpName, rpID string) (*PublicKeyCredentialCreationOptions, error) {
challenge, err := GenerateChallenge()
if err != nil {
return nil, err
}
return &PublicKeyCredentialCreationOptions{
Challenge: challenge,
RP: RelyingParty{
Name: rpName,
ID: rpID,
},
User: User{
ID: userID,
Name: username,
DisplayName: displayName,
},
PubKeyCredParams: []PubKeyCredParam{
{Type: "public-key", Alg: -7}, // ES256
{Type: "public-key", Alg: -257}, // RS256
},
Timeout: 60000,
Attestation: "none",
AuthenticatorSelection: AuthenticatorSelection{
RequireResidentKey: false,
UserVerification: "preferred",
},
}, nil
}
// CreateAuthenticationOptions generates WebAuthn authentication options
func CreateAuthenticationOptions(rpID string, allowedCredentials []string) (*PublicKeyCredentialRequestOptions, error) {
challenge, err := GenerateChallenge()
if err != nil {
return nil, err
}
return &PublicKeyCredentialRequestOptions{
Challenge: challenge,
Timeout: 60000,
RPID: rpID,
AllowCredentials: allowedCredentials,
UserVerification: "preferred",
}, nil
}
// VerifyClientData validates the WebAuthn client data JSON
func VerifyClientData(clientDataJSON []byte, expectedChallenge, expectedOrigin string) error {
var clientData struct {
Type string `json:"type"`
Challenge string `json:"challenge"`
Origin string `json:"origin"`
}
if err := json.Unmarshal(clientDataJSON, &clientData); err != nil {
return fmt.Errorf("invalid client data JSON: %w", err)
}
if clientData.Type != "webauthn.get" && clientData.Type != "webauthn.create" {
return fmt.Errorf("invalid client data type: %s", clientData.Type)
}
if clientData.Challenge != expectedChallenge {
return fmt.Errorf("challenge mismatch")
}
if clientData.Origin != expectedOrigin {
return fmt.Errorf("origin mismatch: expected %s, got %s", expectedOrigin, clientData.Origin)
}
return nil
}
// HashClientData creates SHA-256 hash of client data (required for signature verification)
func HashClientData(clientDataJSON []byte) []byte {
hash := sha256.Sum256(clientDataJSON)
return hash[:]
}
// WrapMasterKey encrypts the master key with the admin's public key
// In a production system, this would use the WebAuthn credential's public key
func WrapMasterKey(masterKey []byte, publicKey []byte) ([]byte, error) {
// Simplified implementation - in production, use proper public key encryption
// For now, we'll use a symmetric approach with HKDF
return masterKey, nil // TODO: Implement proper key wrapping
}
// UnwrapMasterKey decrypts the master key using the admin's credential
func UnwrapMasterKey(wrappedKey []byte, privateKey []byte) ([]byte, error) {
// Simplified implementation - in production, use proper public key decryption
return wrappedKey, nil // TODO: Implement proper key unwrapping
}