4
0

webauthn.go 4.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183
  1. // SPDX-FileCopyrightText: Copyright The Miniflux Authors. All rights reserved.
  2. // SPDX-License-Identifier: Apache-2.0
  3. package storage // import "miniflux.app/v2/internal/storage"
  4. import (
  5. "database/sql"
  6. "fmt"
  7. "log/slog"
  8. "github.com/go-webauthn/webauthn/webauthn"
  9. "miniflux.app/v2/internal/model"
  10. )
  11. // AddWebAuthnCredential handles storage of webauthn credentials.
  12. func (s *Storage) AddWebAuthnCredential(userID int64, handle []byte, credential *webauthn.Credential) error {
  13. query := `
  14. INSERT INTO webauthn_credentials
  15. (handle, cred_id, user_id, public_key, attestation_type, aaguid, sign_count, clone_warning)
  16. VALUES
  17. ($1, $2, $3, $4, $5, $6, $7, $8)
  18. `
  19. _, err := s.db.Exec(
  20. query,
  21. handle,
  22. credential.ID,
  23. userID,
  24. credential.PublicKey,
  25. credential.AttestationType,
  26. credential.Authenticator.AAGUID,
  27. credential.Authenticator.SignCount,
  28. credential.Authenticator.CloneWarning,
  29. )
  30. return err
  31. }
  32. func (s *Storage) WebAuthnCredentialByHandle(handle []byte) (int64, *model.WebAuthnCredential, error) {
  33. var credential model.WebAuthnCredential
  34. var userID int64
  35. query := `
  36. SELECT
  37. user_id,
  38. cred_id,
  39. public_key,
  40. attestation_type,
  41. aaguid,
  42. sign_count,
  43. clone_warning,
  44. added_on,
  45. last_seen_on,
  46. name
  47. FROM
  48. webauthn_credentials
  49. WHERE
  50. handle = $1
  51. `
  52. var nullName sql.NullString
  53. err := s.db.
  54. QueryRow(query, handle).
  55. Scan(
  56. &userID,
  57. &credential.Credential.ID,
  58. &credential.Credential.PublicKey,
  59. &credential.Credential.AttestationType,
  60. &credential.Credential.Authenticator.AAGUID,
  61. &credential.Credential.Authenticator.SignCount,
  62. &credential.Credential.Authenticator.CloneWarning,
  63. &credential.AddedOn,
  64. &credential.LastSeenOn,
  65. &nullName,
  66. )
  67. if err != nil {
  68. return 0, nil, err
  69. }
  70. if nullName.Valid {
  71. credential.Name = nullName.String
  72. } else {
  73. credential.Name = ""
  74. }
  75. credential.Handle = handle
  76. return userID, &credential, err
  77. }
  78. func (s *Storage) WebAuthnCredentialsByUserID(userID int64) ([]model.WebAuthnCredential, error) {
  79. query := `
  80. SELECT
  81. handle,
  82. cred_id,
  83. public_key,
  84. attestation_type,
  85. aaguid,
  86. sign_count,
  87. clone_warning,
  88. name,
  89. added_on,
  90. last_seen_on
  91. FROM
  92. webauthn_credentials
  93. WHERE
  94. user_id = $1
  95. `
  96. rows, err := s.db.Query(query, userID)
  97. if err != nil {
  98. return nil, err
  99. }
  100. defer rows.Close()
  101. var creds []model.WebAuthnCredential
  102. var nullName sql.NullString
  103. for rows.Next() {
  104. var cred model.WebAuthnCredential
  105. err = rows.Scan(
  106. &cred.Handle,
  107. &cred.Credential.ID,
  108. &cred.Credential.PublicKey,
  109. &cred.Credential.AttestationType,
  110. &cred.Credential.Authenticator.AAGUID,
  111. &cred.Credential.Authenticator.SignCount,
  112. &cred.Credential.Authenticator.CloneWarning,
  113. &nullName,
  114. &cred.AddedOn,
  115. &cred.LastSeenOn,
  116. )
  117. if err != nil {
  118. return nil, err
  119. }
  120. if nullName.Valid {
  121. cred.Name = nullName.String
  122. } else {
  123. cred.Name = ""
  124. }
  125. creds = append(creds, cred)
  126. }
  127. return creds, nil
  128. }
  129. func (s *Storage) WebAuthnSaveLogin(handle []byte) error {
  130. query := "UPDATE webauthn_credentials SET last_seen_on=NOW() WHERE handle=$1"
  131. _, err := s.db.Exec(query, handle)
  132. if err != nil {
  133. return fmt.Errorf(`store: unable to update last seen date for webauthn credential: %v`, err)
  134. }
  135. return nil
  136. }
  137. func (s *Storage) WebAuthnUpdateName(handle []byte, name string) error {
  138. query := "UPDATE webauthn_credentials SET name=$1 WHERE handle=$2"
  139. _, err := s.db.Exec(query, name, handle)
  140. if err != nil {
  141. return fmt.Errorf(`store: unable to update name for webauthn credential: %v`, err)
  142. }
  143. return nil
  144. }
  145. func (s *Storage) CountWebAuthnCredentialsByUserID(userID int64) int {
  146. var count int
  147. query := "SELECT COUNT(*) FROM webauthn_credentials WHERE user_id = $1"
  148. err := s.db.QueryRow(query, userID).Scan(&count)
  149. if err != nil {
  150. slog.Error("store: unable to count webauthn certs for user",
  151. slog.Int64("user_id", userID),
  152. slog.Any("error", err),
  153. )
  154. return 0
  155. }
  156. return count
  157. }
  158. func (s *Storage) DeleteCredentialByHandle(userID int64, handle []byte) error {
  159. query := "DELETE FROM webauthn_credentials WHERE user_id = $1 AND handle = $2"
  160. _, err := s.db.Exec(query, userID, handle)
  161. return err
  162. }
  163. func (s *Storage) DeleteAllWebAuthnCredentialsByUserID(userID int64) error {
  164. query := "DELETE FROM webauthn_credentials WHERE user_id = $1"
  165. _, err := s.db.Exec(query, userID)
  166. return err
  167. }