TASK-SECADV-001: WebAuthn/Passkeys (F022) - WebAuthn credential model, service, handler - Registration/authentication ceremony endpoints - CRUD operations (list, rename, delete passkeys) - Routes: GET/POST/PUT/DELETE /auth/passkeys/* TASK-SECADV-002: Configurable password policy (F015) - PasswordPolicyConfig with MinLength, MaxLength, RequireUpper/Lower/Number/Special - NewPasswordValidatorWithPolicy constructor - PasswordPolicyFromEnv() reads env vars (PASSWORD_MIN_LENGTH, etc.) - All character class checks now respect policy configuration TASK-SECADV-003: Géolocalisation connexions (F025) - GeoIPResolver interface + GeoIPService implementation - Country/city columns added to login_history table - LoginHistoryService.Record() performs GeoIP lookup - GetUserHistory returns geolocation data - GET /auth/login-history endpoint TASK-SECADV-004: Password expiration (F016) - password_changed_at column on users table - CheckPasswordExpiration() method on PasswordService - All password change/reset methods now set password_changed_at - NewPasswordServiceWithPolicy() supports expiration days config Migration: 971_security_advanced_v0133.sql Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
45 lines
1.8 KiB
Go
45 lines
1.8 KiB
Go
package models
|
|
|
|
import (
|
|
"time"
|
|
|
|
"github.com/google/uuid"
|
|
)
|
|
|
|
// WebAuthnCredential represents a FIDO2/WebAuthn credential (passkey) stored for a user.
|
|
// F022: ORIGIN_FEATURES_REGISTRY.md — WebAuthn/Passkeys support.
|
|
type WebAuthnCredential struct {
|
|
ID uuid.UUID `json:"id" gorm:"type:uuid;primaryKey" db:"id"`
|
|
UserID uuid.UUID `json:"user_id" gorm:"type:uuid;not null" db:"user_id"`
|
|
CredentialID []byte `json:"-" gorm:"type:bytea;not null;uniqueIndex" db:"credential_id"`
|
|
PublicKey []byte `json:"-" gorm:"type:bytea;not null" db:"public_key"`
|
|
AttestationType string `json:"attestation_type" gorm:"size:50;not null;default:'none'" db:"attestation_type"`
|
|
AAGUID []byte `json:"-" gorm:"type:bytea" db:"aaguid"`
|
|
SignCount uint32 `json:"sign_count" gorm:"not null;default:0" db:"sign_count"`
|
|
Name string `json:"name" gorm:"size:100;not null;default:'My Passkey'" db:"name"`
|
|
CreatedAt time.Time `json:"created_at" gorm:"autoCreateTime" db:"created_at"`
|
|
LastUsedAt *time.Time `json:"last_used_at,omitempty" db:"last_used_at"`
|
|
}
|
|
|
|
// TableName defines the GORM table name.
|
|
func (WebAuthnCredential) TableName() string {
|
|
return "webauthn_credentials"
|
|
}
|
|
|
|
// WebAuthnCredentialPublicInfo is the safe-to-expose subset for API responses.
|
|
type WebAuthnCredentialPublicInfo struct {
|
|
ID uuid.UUID `json:"id"`
|
|
Name string `json:"name"`
|
|
CreatedAt time.Time `json:"created_at"`
|
|
LastUsedAt *time.Time `json:"last_used_at,omitempty"`
|
|
}
|
|
|
|
// ToPublicInfo converts a credential to its public representation.
|
|
func (c *WebAuthnCredential) ToPublicInfo() WebAuthnCredentialPublicInfo {
|
|
return WebAuthnCredentialPublicInfo{
|
|
ID: c.ID,
|
|
Name: c.Name,
|
|
CreatedAt: c.CreatedAt,
|
|
LastUsedAt: c.LastUsedAt,
|
|
}
|
|
}
|