diff --git a/server/ente/errors.go b/server/ente/errors.go index 96e7bd4a1e..89fdebb17f 100644 --- a/server/ente/errors.go +++ b/server/ente/errors.go @@ -125,6 +125,12 @@ var ErrFileNotFoundInAlbum = ApiError{ Message: "File is either deleted or moved to different collection", } +var ErrSessionAlreadyClaimed = ApiError{ + Code: "SESSION_ALREADY_CLAIMED", + Message: "Session is already claimed", + HttpStatusCode: http.StatusConflict, +} + var ErrPublicCollectDisabled = ApiError{ Code: PublicCollectDisabled, Message: "User has not enabled public collect for this url", diff --git a/server/pkg/api/user.go b/server/pkg/api/user.go index c38df8f7b7..71e050fdeb 100644 --- a/server/pkg/api/user.go +++ b/server/pkg/api/user.go @@ -325,6 +325,17 @@ func (h *UserHandler) BeginPasskeyAuthenticationCeremony(c *gin.Context) { return } + isSessionAlreadyClaimed, err := h.UserController.PasskeyRepo.IsSessionAlreadyClaimed(request.SessionID) + if err != nil { + handler.Error(c, stacktrace.Propagate(err, "")) + return + } + + if isSessionAlreadyClaimed { + handler.Error(c, stacktrace.Propagate(&ente.ErrSessionAlreadyClaimed, "Session already claimed")) + return + } + user, err := h.UserController.UserRepo.Get(userID) if err != nil { handler.Error(c, stacktrace.Propagate(err, "")) diff --git a/server/pkg/repo/passkey/passkey.go b/server/pkg/repo/passkey/passkey.go index 3e4778e540..131f16b836 100644 --- a/server/pkg/repo/passkey/passkey.go +++ b/server/pkg/repo/passkey/passkey.go @@ -174,6 +174,19 @@ func (r *Repository) GetUserIDWithPasskeyTwoFactorSession(sessionID string) (use return } +// IsSessionAlreadyClaimed checks if the both token_data and verified_at are not null for a given session ID +func (r *Repository) IsSessionAlreadyClaimed(sessionID string) (bool, error) { + var verifiedAt sql.NullInt64 + err := r.DB.QueryRow(`SELECT verified_at FROM passkey_login_sessions WHERE session_id = $1`, sessionID).Scan(&verifiedAt) + if err != nil { + if err == sql.ErrNoRows { + return false, nil + } + return false, stacktrace.Propagate(err, "") + } + return verifiedAt.Valid, nil +} + // StoreTokenData takes a sessionID, and tokenData, and updates the tokenData in the database func (r *Repository) StoreTokenData(sessionID string, tokenData ente.TwoFactorAuthorizationResponse) error { tokenDataJson, err := json.Marshal(tokenData)