-
Notifications
You must be signed in to change notification settings - Fork 1
Feature/framework ergonomics update #39
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Introduces CSRF protection using the Double-Submit Cookie pattern to the README, library docs, and cookbook. Adds a dedicated recipe for CSRF protection, updates feature tables, and provides configuration and usage examples for both backend and frontend integration.
Updated Cargo.toml and Cargo.lock to reference version 0.1.13 for all internal RustAPI crates. This ensures consistency across the workspace and prepares for the next release.
…amework-ergonomics-update Feature/framework ergonomics update 410a7ff
There was a problem hiding this 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 pull request adds comprehensive documentation for CSRF protection functionality in the RustAPI framework. The changes introduce a new cookbook recipe explaining CSRF protection using the Double-Submit Cookie pattern, update feature documentation, and bump version numbers from 0.1.12 to 0.1.13.
Changes:
- Added complete CSRF protection recipe documentation with examples and best practices
- Updated feature tables and documentation in rustapi-extras crate and README
- Bumped all crate versions from 0.1.12 to 0.1.13 in workspace configuration
Reviewed changes
Copilot reviewed 6 out of 7 changed files in this pull request and generated 15 comments.
Show a summary per file
| File | Description |
|---|---|
| docs/cookbook/src/recipes/csrf_protection.md | New comprehensive CSRF protection recipe with configuration, integration examples, and security considerations |
| docs/cookbook/src/crates/rustapi_extras.md | Added CSRF feature documentation with basic usage examples |
| docs/cookbook/src/SUMMARY.md | Added CSRF Protection recipe to table of contents |
| crates/rustapi-extras/src/lib.rs | Updated crate documentation to include CSRF feature and example |
| README.md | Added csrf feature to feature table |
| Cargo.toml | Version bump from 0.1.12 to 0.1.13 for all workspace crates |
| Cargo.lock | Updated lock file to reflect version changes |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| let config = CsrfConfig::new() | ||
| // Cookie settings | ||
| .cookie_name("csrf_token") // Default: "csrf_token" | ||
| .cookie_path("/") // Default: "/" |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method cookie_path() does not exist in the CsrfConfig implementation. There is no setter method for cookie_path in the config.rs file.
| .cookie_path("/") // Default: "/" |
| <form method="POST" action="/submit"> | ||
| <input type="hidden" name="csrf_token" value="{}" /> | ||
| <button type="submit">Submit</button> | ||
| </form> |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This example is misleading. The CSRF middleware validates the token from the request header (X-CSRF-Token or the configured header name), not from form data. Including the token as a hidden input field in a traditional HTML form will not work with this middleware implementation. For HTML forms to work with this CSRF protection, JavaScript would be needed to read the cookie and include it as a header, or the form would need to be submitted via JavaScript/AJAX with the header included.
| <form method="POST" action="/submit"> | |
| <input type="hidden" name="csrf_token" value="{}" /> | |
| <button type="submit">Submit</button> | |
| </form> | |
| <button id="submit-btn" type="button">Submit</button> | |
| <script> | |
| document.getElementById('submit-btn').addEventListener('click', async () => {{ | |
| const response = await fetch('/submit', {{ | |
| method: 'POST', | |
| headers: {{ | |
| 'X-CSRF-Token': '{}' | |
| }} | |
| }}); | |
| const text = await response.text(); | |
| alert(text); | |
| }}); | |
| </script> |
| |--------------|--------| | ||
| | Token in cookie | ✅ HttpOnly=false (JS needs access) | | ||
| | Token validation | ✅ Constant-time comparison | | ||
| | SameSite cookie | ✅ Configurable (Strict by default) | |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default SameSite policy is incorrect. According to the implementation in config.rs line 55, the default is SameSite::Lax, not Strict.
| | SameSite cookie | ✅ Configurable (Strict by default) | | |
| | SameSite cookie | ✅ Configurable (Lax by default) | |
| | Consideration | Status | | ||
| |--------------|--------| | ||
| | Token in cookie | ✅ HttpOnly=false (JS needs access) | | ||
| | Token validation | ✅ Constant-time comparison | |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The documentation claims constant-time comparison is used for token validation, but the actual implementation at layer.rs line 73 uses a regular string equality check (h_token == token.as_str()), which is vulnerable to timing attacks. This should use a constant-time comparison function to prevent timing attacks that could leak information about the token.
| | Token validation | ✅ Constant-time comparison | | |
| | Token validation | Uses regular equality check (not constant-time in this example) | |
| ```rust | ||
| let config = CsrfConfig::new() | ||
| // Cookie settings | ||
| .cookie_name("csrf_token") // Default: "csrf_token" |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The default cookie name is incorrect. According to the actual implementation in crates/rustapi-extras/src/csrf/config.rs, the default cookie name is "XSRF-TOKEN", not "csrf_token".
| .header_name("X-CSRF-Token") // Default: "X-CSRF-Token" | ||
| .token_length(32); // Default: 32 bytes |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method token_length() does not exist in the CsrfConfig implementation. There is no setter method for token_length in the config.rs file.
| .header_name("X-CSRF-Token") // Default: "X-CSRF-Token" | |
| .token_length(32); // Default: 32 bytes | |
| .header_name("X-CSRF-Token"); // Default: "X-CSRF-Token" | |
| // Token length is fixed at 32 bytes by default and is not configurable. |
| let csrf_config = CsrfConfig::new() | ||
| .cookie_name("csrf_token") | ||
| .header_name("X-CSRF-Token") | ||
| .cookie_secure(true); // HTTPS only |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method name is incorrect. The actual method is secure(), not cookie_secure(). According to the implementation in config.rs, the builder method is named secure().
| .cookie_secure(true); // HTTPS only | |
| .secure(true); // HTTPS only |
| function getCsrfToken() { | ||
| return document.cookie | ||
| .split('; ') | ||
| .find(row => row.startsWith('csrf_token=')) |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This example assumes the cookie name is "csrf_token", but the actual default cookie name is "XSRF-TOKEN" according to the implementation. The cookie name should match what is configured or use the default.
| .find(row => row.startsWith('csrf_token=')) | |
| .find(row => row.startsWith('XSRF-TOKEN=')) |
| For traditional form submissions, include the token as a hidden field: | ||
|
|
||
| ```html | ||
| <form method="POST" action="/api/submit"> | ||
| <input type="hidden" name="_csrf" value="{{ csrf_token }}" /> | ||
| <!-- form fields --> | ||
| <button type="submit">Submit</button> | ||
| </form> |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This example is misleading. The CSRF middleware validates the token from the request header, not from form data. A hidden input field with name="_csrf" will not be validated by the middleware as implemented. For traditional HTML forms to work with this CSRF protection, JavaScript is needed to intercept the form submission and add the token as a header.
| For traditional form submissions, include the token as a hidden field: | |
| ```html | |
| <form method="POST" action="/api/submit"> | |
| <input type="hidden" name="_csrf" value="{{ csrf_token }}" /> | |
| <!-- form fields --> | |
| <button type="submit">Submit</button> | |
| </form> | |
| For traditional HTML form submissions, the CSRF middleware still expects the token in a request header (not in form data). Use JavaScript to intercept the submission, read the token, and send the request with the header: | |
| ```html | |
| <form id="csrf-form" method="POST" action="/api/submit"> | |
| <!-- form fields --> | |
| <button type="submit">Submit</button> | |
| </form> | |
| <script> | |
| document.getElementById('csrf-form').addEventListener('submit', function (event) { | |
| event.preventDefault(); | |
| const form = event.target; | |
| const formData = new FormData(form); | |
| // Read token from cookie (must match the cookie name set by the backend) | |
| const csrfToken = document.cookie | |
| .split('; ') | |
| .find(row => row.startsWith('csrf_token=')) | |
| ?.split('=')[1]; | |
| fetch(form.action, { | |
| method: form.method, | |
| body: formData, | |
| headers: { | |
| 'X-CSRF-Token': csrfToken, | |
| }, | |
| }); | |
| }); | |
| </script> |
| use cookie::SameSite; | ||
|
|
||
| let config = CsrfConfig::new() | ||
| .cookie_same_site(SameSite::Strict); // Most restrictive |
Copilot
AI
Jan 19, 2026
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The method name is incorrect. The actual method is same_site(), not cookie_same_site(). According to the implementation in config.rs, the builder method is named same_site().
| .cookie_same_site(SameSite::Strict); // Most restrictive | |
| .same_site(SameSite::Strict); // Most restrictive |
Description
Please include a summary of the changes and the related issue. Please also include relevant motivation and context.
Type of Change
Checklist
Related Issues
Fixes # (issue number)
Testing
Please describe the tests that you ran to verify your changes:
Screenshots (if applicable)
Add screenshots to help explain your changes.
Additional Notes
Add any other context about the pull request here.