1
0
mirror of https://github.com/pocket-id/pocket-id.git synced 2026-02-14 15:52:31 +00:00

feat: adding/removing passkeys creates an entry in audit logs (#1099)

Co-authored-by: Elias Schneider <login@eliasschneider.com>
This commit is contained in:
Alessandro (Ale) Segala
2025-11-16 14:51:38 -08:00
committed by GitHub
parent a54b867105
commit c56afe016e
6 changed files with 47 additions and 14 deletions

View File

@@ -34,7 +34,7 @@ func (s *AuditLogService) Create(ctx context.Context, event model.AuditLogEvent,
country, city, err := s.geoliteService.GetLocationByIP(ipAddress)
if err != nil {
// Log the error but don't interrupt the operation
slog.Warn("Failed to get IP location", "error", err)
slog.Warn("Failed to get IP location", slog.String("ip", ipAddress), slog.Any("error", err))
}
auditLog := model.AuditLog{
@@ -201,8 +201,8 @@ func (s *AuditLogService) ListUsernamesWithIds(ctx context.Context) (users map[s
WithContext(ctx).
Joins("User").
Model(&model.AuditLog{}).
Select("DISTINCT \"User\".id, \"User\".username").
Where("\"User\".username IS NOT NULL")
Select(`DISTINCT "User".id, "User".username`).
Where(`"User".username IS NOT NULL`)
type Result struct {
ID string `gorm:"column:id"`
@@ -210,7 +210,8 @@ func (s *AuditLogService) ListUsernamesWithIds(ctx context.Context) (users map[s
}
var results []Result
if err := query.Find(&results).Error; err != nil {
err = query.Find(&results).Error
if err != nil {
return nil, fmt.Errorf("failed to query user IDs: %w", err)
}
@@ -246,7 +247,8 @@ func (s *AuditLogService) ListClientNames(ctx context.Context) (clientNames []st
}
var results []Result
if err := query.Find(&results).Error; err != nil {
err = query.Find(&results).Error
if err != nil {
return nil, fmt.Errorf("failed to query client IDs: %w", err)
}

View File

@@ -2,6 +2,8 @@ package service
import (
"context"
"encoding/hex"
"errors"
"fmt"
"net/http"
"time"
@@ -114,7 +116,7 @@ func (s *WebAuthnService) BeginRegistration(ctx context.Context, userID string)
}, nil
}
func (s *WebAuthnService) VerifyRegistration(ctx context.Context, sessionID, userID string, r *http.Request) (model.WebauthnCredential, error) {
func (s *WebAuthnService) VerifyRegistration(ctx context.Context, sessionID string, userID string, r *http.Request, ipAddress string) (model.WebauthnCredential, error) {
tx := s.db.Begin()
defer func() {
tx.Rollback()
@@ -173,6 +175,9 @@ func (s *WebAuthnService) VerifyRegistration(ctx context.Context, sessionID, use
return model.WebauthnCredential{}, fmt.Errorf("failed to store WebAuthn credential: %w", err)
}
auditLogData := model.AuditLogData{"credentialID": hex.EncodeToString(credential.ID), "passkeyName": passkeyName}
s.auditLogService.Create(ctx, model.AuditLogEventPasskeyAdded, ipAddress, r.UserAgent(), userID, auditLogData, tx)
err = tx.Commit().Error
if err != nil {
return model.WebauthnCredential{}, fmt.Errorf("failed to commit transaction: %w", err)
@@ -288,16 +293,30 @@ func (s *WebAuthnService) ListCredentials(ctx context.Context, userID string) ([
return credentials, nil
}
func (s *WebAuthnService) DeleteCredential(ctx context.Context, userID, credentialID string) error {
err := s.db.
func (s *WebAuthnService) DeleteCredential(ctx context.Context, userID string, credentialID string, ipAddress string, userAgent string) error {
tx := s.db.Begin()
defer func() {
tx.Rollback()
}()
credential := &model.WebauthnCredential{}
err := tx.
WithContext(ctx).
Where("id = ? AND user_id = ?", credentialID, userID).
Delete(&model.WebauthnCredential{}).
Clauses(clause.Returning{}).
Delete(credential, "id = ? AND user_id = ?", credentialID, userID).
Error
if err != nil {
return fmt.Errorf("failed to delete record: %w", err)
}
auditLogData := model.AuditLogData{"credentialID": hex.EncodeToString(credential.CredentialID), "passkeyName": credential.Name}
s.auditLogService.Create(ctx, model.AuditLogEventPasskeyRemoved, ipAddress, userAgent, userID, auditLogData, tx)
err = tx.Commit().Error
if err != nil {
return fmt.Errorf("failed to commit transaction: %w", err)
}
return nil
}
@@ -353,7 +372,7 @@ func (s *WebAuthnService) CreateReauthenticationTokenWithAccessToken(ctx context
userID, ok := token.Subject()
if !ok {
return "", fmt.Errorf("access token does not contain user ID")
return "", errors.New("access token does not contain user ID")
}
// Check if token is issued less than a minute ago