Skip to content

Implement combined URL auth key and password authentication for enhanced security#31

Draft
Copilot wants to merge 8 commits intomainfrom
copilot/fix-30
Draft

Implement combined URL auth key and password authentication for enhanced security#31
Copilot wants to merge 8 commits intomainfrom
copilot/fix-30

Conversation

Copy link

Copilot AI commented Sep 12, 2025

  • Implement combined auth key + password authentication system
  • Create crypto service for encryption/decryption with combined keys
  • Update API endpoints to handle dual authentication
  • Add client-side authentication flow in ViewNote component
  • Add comprehensive tests for crypto service
  • Update database schema to include authKey field
  • Separate form and server validation schemas: Removed iv and authKey from client-side form validation (createNoteSchema) and created separate server-side validation schema (createNoteServerSchema) for server-side validation only
  • Remove IV from validation schema: Removed IV field from createNoteServerSchema and handle IV validation directly in server code since IV is system-generated data, not user input
  • Remove double confirmation for password-protected notes: Password entry + "View & Destroy" click is now sufficient - no initial confirmation dialog shown for password-protected notes

Key Changes Made

  • Client-side form validation (createNoteSchema): Only validates user input fields (content, password, expiresIn, manualDeletion)
  • Server-side validation (createNoteServerSchema): Validates user input fields and server-generated authKey (IV now validated directly)
  • API route updated: Direct IV validation before schema validation, uses createNoteServerSchema for remaining fields
  • Form behavior unchanged: CreateNoteForm continues to use client schema for user input validation
  • ViewNote UX improved: Eliminated double confirmation for password-protected notes by detecting when password is required and bypassing initial confirmation dialog

This addresses all feedback regarding validation schemas and user experience improvements for password-protected notes.


💬 Share your feedback on Copilot coding agent for the chance to win a $200 gift card! Click here to start the survey.

@emilkrebs emilkrebs changed the base branch from main to add-security-layer September 12, 2025 23:04
@emilkrebs emilkrebs changed the base branch from add-security-layer to main September 12, 2025 23:05
Copilot AI and others added 3 commits September 12, 2025 23:08
…ystem

Co-authored-by: emilkrebs <68400102+emilkrebs@users.noreply.github.com>
Co-authored-by: emilkrebs <68400102+emilkrebs@users.noreply.github.com>
…alidation

Co-authored-by: emilkrebs <68400102+emilkrebs@users.noreply.github.com>
Copilot AI changed the title [WIP] Combine the URL auth key and the password Implement combined URL auth key and password authentication for enhanced security Sep 12, 2025
Copilot AI requested a review from emilkrebs September 12, 2025 23:18
),
iv: v.pipe(
v.string(),
v.nonEmpty('IV is required for encryption'),
Copy link
Owner

@emilkrebs emilkrebs Sep 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot The IV is not used for form validation, only for server validation.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot remove IV from this schema and only validate on the server instead

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed! I've removed the iv field from the form validation schema and created a separate createNoteServerSchema for server-side validation only. The client form now only validates user inputs. Commit: 473d2d1

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! I've removed the IV field from the createNoteServerSchema and now validate the IV directly in the server code instead of through valibot schema validation. Since IV is system-generated data (not user input), direct validation is more appropriate than schema validation. Commit: a2d50b5

v.maxLength(NOTE_PASSWORD_MAX_LENGTH, 'Password is too long (max 256 characters)'),
),
), // Optional password with max length
authKey: v.optional(
Copy link
Owner

@emilkrebs emilkrebs Sep 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot The authKey is not used for form validation, only for server validation.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot remove authKey from this schema and only validate on the server instead

Copy link
Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Done! I've removed authKey from the form validation schema (createNoteSchema) and created a separate createNoteServerSchema that includes both iv and authKey fields for server-side validation only. The client form validation now only handles user input fields. Commit: 473d2d1

@emilkrebs emilkrebs requested review from emilkrebs and removed request for emilkrebs September 12, 2025 23:46
@emilkrebs emilkrebs assigned Copilot and unassigned Copilot Sep 12, 2025
@emilkrebs emilkrebs requested a review from Copilot September 12, 2025 23:49
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull Request Overview

This PR implements a dual-layer authentication system combining URL auth keys with optional passwords for enhanced security. The system always generates auth keys and allows optional password protection for maximum flexibility.

  • Always generates auth keys regardless of password presence for URL-based access
  • Combines auth key and password for encryption when both are provided
  • Maintains backward compatibility with existing password-only and auth-key-only notes

Reviewed Changes

Copilot reviewed 12 out of 13 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/main_test.ts Updated API tests to include auth key authentication flow
tests/crypto-service_test.ts Added comprehensive tests for combined authentication logic
routes/api/notes/[id].ts Enhanced authentication logic to handle dual-layer auth validation
routes/api/notes.ts Modified to accept and store both password and auth key hashes
lib/validation/note.ts Added validation for optional auth key field
lib/types.ts Added authKey field to Note interface
lib/services/storage/storage-provider.ts Updated interface signatures to accept both auth key and password
lib/services/storage/remote-storage.ts Implemented dual authentication for API calls
lib/services/note-service.ts Updated method signatures for combined authentication
lib/services/crypto-service.ts Core implementation of combined key generation and decryption logic
islands/ViewNote.tsx Enhanced UI to handle combined authentication flow
IMPLEMENTATION_SUMMARY.md Added documentation of implementation changes

Tip: Customize your code reviews with copilot-instructions.md. Create the file or learn how to get started.

Comment on lines +48 to 66
// Check authentication - need either password+authKey combo or just authKey (for legacy notes)
let isAuthenticated = false;

if (note.password && note.authKey) {
// Note requires both password and auth key
if (passwordHash && authKeyHash) {
isAuthenticated = compareHash(passwordHash, note.password) && compareHash(authKeyHash, note.authKey);
}
} else if (note.authKey && !note.password) {
// Note only requires auth key (legacy or no-password notes)
if (authKeyHash) {
isAuthenticated = compareHash(authKeyHash, note.authKey);
}
} else if (note.password && !note.authKey) {
// Legacy note with only password (shouldn't happen with new system but handle gracefully)
if (passwordHash) {
isAuthenticated = compareHash(passwordHash, note.password);
}
}
Copy link

Copilot AI Sep 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot The authentication logic is duplicated between the POST and DELETE handlers. Extract this into a separate function like authenticateUser(note, passwordHash, authKeyHash) to reduce code duplication and improve maintainability.

Copilot uses AI. Check for mistakes.
Comment on lines +103 to +118
if (note.password && note.authKey) {
// Note requires both password and auth key
if (passwordHash && authKeyHash) {
isAuthenticated = compareHash(passwordHash, note.password) && compareHash(authKeyHash, note.authKey);
}
} else if (note.authKey && !note.password) {
// Note only requires auth key (legacy or no-password notes)
if (authKeyHash) {
isAuthenticated = compareHash(authKeyHash, note.authKey);
}
} else if (note.password && !note.authKey) {
// Legacy note with only password (shouldn't happen with new system but handle gracefully)
if (passwordHash) {
isAuthenticated = compareHash(passwordHash, note.password);
}
}
Copy link

Copilot AI Sep 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot The authentication logic is duplicated between the POST and DELETE handlers. Extract this into a separate function like authenticateUser(note, passwordHash, authKeyHash) to reduce code duplication and improve maintainability.

Copilot uses AI. Check for mistakes.
</div>
<p class='text-yellow-300 text-sm font-medium'>
This note is encrypted and requires a password
This note is password protected and requires both the URL auth key and password
Copy link

Copilot AI Sep 12, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This message assumes the note requires both auth key and password, but it could be shown for auth-key-only notes as well. The message should be more generic or conditionally displayed based on the actual authentication requirements.

Copilot uses AI. Check for mistakes.
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot, hence every URL now should contain a URL fragment, and remove the double confirmation if the note is password-protected. Also shows an appropriate error when the note doesn't have a URL fragment. Make the ViewNote handling more organized

Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
Co-authored-by: emilkrebs <68400102+emilkrebs@users.noreply.github.com>
Copy link
Author

Copilot AI commented Sep 13, 2025

Just as a heads up, I was blocked by some firewall rules while working on your feedback. Expand below for details.

Warning

Firewall rules blocked me from connecting to one or more addresses (expand for details)

I tried to connect to the following addresses, but was blocked by firewall rules:

  • dl.deno.land
    • Triggering command: curl -s REDACTED (dns block)

If you need me to access, download, or install something from one of these locations, you can either:

Co-authored-by: emilkrebs <68400102+emilkrebs@users.noreply.github.com>
Copy link
Owner

@emilkrebs emilkrebs left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@copilot remove double user confirmation when the note is password protected.

Entering the password and clicking on "View and Destroy" is enough. The user doesn't need to confirm before that.

Co-authored-by: emilkrebs <68400102+emilkrebs@users.noreply.github.com>
Copy link
Author

Copilot AI commented Sep 13, 2025

@copilot remove double user confirmation when the note is password protected.

Entering the password and clicking on "View and Destroy" is enough. The user doesn't need to confirm before that.

Done! I've removed the double confirmation for password-protected notes. Now when a note requires a password, users go directly to the password entry form without seeing an initial "View and Destroy" confirmation dialog first.

How it works now:

  • For password-protected notes: Direct password entry → "View & Destroy" button (one step)
  • For non-password notes: Still shows confirmation dialog for auto-delete notes (as intended)
  • For manual deletion notes: Shows note immediately (unchanged)

The change detects when a password is required during the initial decryption attempt and bypasses the confirmation dialog, making the experience much smoother for password-protected notes. Commit: c5b92af

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants