-
Notifications
You must be signed in to change notification settings - Fork 14
feat: add GenerateSecureToken and SecureToken (256-bit default) #165
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
…tion - Add GenerateSecureToken(length int) for configurable secure tokens - Add SecureToken() with default 32 bytes (256 bits entropy) - Uses crypto/rand and base64.RawURLEncoding for URL-safe output - Includes comprehensive tests and benchmarks - Performance comparable to UUIDv4 with higher security - Intended to replace UUIDv4 as default for Fiber v3 token/key/sessionID generation
Summary of ChangesHello @sixcolors, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed! This pull request significantly upgrades the token generation capabilities within the utility package by introducing Highlights
Using Gemini Code AssistThe full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips. Invoking Gemini You can request assistance from Gemini at any point by creating a comment using either
Customization To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a Limitations & Feedback Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here. You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension. Footnotes
|
Codecov Report❌ Patch coverage is
Additional details and impacted files@@ Coverage Diff @@
## master #165 +/- ##
==========================================
+ Coverage 83.48% 84.59% +1.10%
==========================================
Files 11 12 +1
Lines 763 954 +191
==========================================
+ Hits 637 807 +170
- Misses 115 126 +11
- Partials 11 21 +10
Flags with carried forward coverage won't be shown. Click here to find out more. ☔ View full report in Codecov by Sentry. 🚀 New features to boost your workflow:
|
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.
Code Review
This pull request introduces GenerateSecureToken and SecureToken as modern, secure alternatives for token generation, which is a great improvement. The implementation is solid and well-tested. However, I've identified a critical security issue in the error handling of GenerateSecureToken. When crypto/rand.Read fails, the function silently falls back to UUIDv4, which can mask critical system errors and produce tokens that violate the function's contract in terms of format, length, and entropy. I've recommended panicking in this scenario, which is the idiomatic Go approach for such unrecoverable errors in cryptographic contexts.
|
If accepted, I will update |
|
Note Other AI code review bot(s) detectedCodeRabbit has detected other AI code review bot(s) in this pull request and will avoid duplicating their findings in the review comments. This may lead to a less comprehensive review. WalkthroughAdds cryptographically secure token utilities and extensive tests: new GenerateSecureToken(length int) (string, error) with package-level randRead indirection, its Must wrapper and 32-byte convenience helpers; concurrency and error-path tests, benchmarks; and removal of t.Parallel() from two string tests. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes
Poem
Pre-merge checks and finishing touches❌ Failed checks (1 warning)
✅ Passed checks (2 passed)
✨ Finishing touches
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
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.
Actionable comments posted: 1
🧹 Nitpick comments (1)
common.go (1)
101-105: Add upper bound validation for token length.The function doesn't validate excessively large length values, which could cause memory allocation issues or denial-of-service if a large value is passed.
Apply this diff to add reasonable upper bound validation:
func GenerateSecureToken(length int) string { if length <= 0 { length = 32 + } else if length > 1024 { + // Cap at 1024 bytes to prevent excessive memory allocation + length = 1024 } bytes := make([]byte, length)
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
common.go(2 hunks)common_test.go(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
common_test.go (1)
common.go (3)
GenerateSecureToken(101-111)SecureToken(114-116)UUIDv4(90-96)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
- GitHub Check: Compare
🔇 Additional comments (7)
common.go (2)
9-9: LGTM!The
encoding/base64import is correctly added to support URL-safe base64 encoding in the new token generation functions.
113-116: LGTM!The wrapper function provides a clear, convenient API for the default 32-byte secure token generation.
common_test.go (5)
102-123: LGTM!The test comprehensively validates default length, custom lengths, uniqueness, and edge cases. The base64 length assertions are mathematically correct.
125-140: LGTM!The concurrency test properly validates that tokens generated concurrently are unique, following the same pattern as existing UUID concurrency tests.
142-151: LGTM!The test adequately validates the SecureToken wrapper function's output length, non-emptiness, and uniqueness.
348-363: LGTM!The benchmark properly measures performance for different token lengths and includes validation assertions.
365-380: LGTM!The comparative benchmark effectively measures the performance difference between UUIDv4 and SecureToken generation, as referenced in the PR objectives.
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 PR introduces secure token generation functions (GenerateSecureToken and SecureToken) as modern alternatives to UUIDv4 for session IDs and security tokens. The implementation uses crypto/rand with base64 URL-safe encoding to provide 256-bit entropy tokens.
Key Changes:
- Added
GenerateSecureToken(length int)for configurable-length secure token generation with base64 URL encoding - Added
SecureToken()as a convenience wrapper that generates 32-byte (256-bit) tokens - Comprehensive test coverage including concurrency tests and performance benchmarks
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 4 comments.
| File | Description |
|---|---|
| common.go | Implements GenerateSecureToken() and SecureToken() functions using crypto/rand for cryptographically secure random bytes and base64.RawURLEncoding for URL-safe output |
| common_test.go | Adds unit tests for basic functionality, edge cases (invalid lengths), uniqueness verification, concurrency safety, and performance benchmarks comparing against UUIDv4 |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
|
@sixcolors We shouldn't fall back to UUIDv4 |
…d negative length test - Replace UUIDv4 fallback with panic for security (fail fast) - Fix misleading documentation about default length - Add test case for negative length values - Addresses comments from gemini-code-assist, coderabbitai, and Copilot
AllocsPerRun cannot be called during parallel tests. Removed t.Parallel() from Test_ToUpper and Test_ToLower to fix panic in test runs.
|
The panic path for crypto/rand.Read failure is not tested because:
|
…ecureTokenMust; update SecureToken to use Must; update tests/benchmarks
|
Updated PR summary: Summary Key changes
Why
Migration
Notes
(Changes already pushed to branch |
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.
Actionable comments posted: 3
🧹 Nitpick comments (1)
common_test.go (1)
157-158: Useerrors.Newinstead offmt.Errorffor static error messages.Since the error message has no format parameters,
errors.Newis more appropriate and slightly more efficient.Apply this diff:
// Simulate read failure randRead = func(b []byte) (int, error) { - return 0, fmt.Errorf("simulated failure") + return 0, errors.New("simulated failure") }Add the import:
import ( "crypto/rand" + "errors" "fmt"
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
common.go(3 hunks)common_test.go(2 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
common_test.go (1)
common.go (4)
GenerateSecureToken(109-118)GenerateSecureTokenMust(121-127)SecureToken(136-138)UUIDv4(94-100)
🪛 GitHub Actions: golangci-lint
common.go
[error] 9-9: golangci-lint: File is not properly formatted (gofmt)
🪛 GitHub Check: lint
common_test.go
[failure] 158-158:
use-errors-new: replace fmt.Errorf by errors.New (revive)
[failure] 158-158:
unnecessary-format: unnecessary use of formatting function "fmt.Errorf", you can replace it with "errors.New" (revive)
[failure] 158-158:
error-format: fmt.Errorf can be replaced with errors.New (perfsprint)
[failure] 157-157:
unused-parameter: parameter 'b' seems to be unused, consider removing or renaming it as _ (revive)
[failure] 380-380:
File is not properly formatted (gofumpt)
[failure] 380-380:
File is not properly formatted (gofmt)
common.go
[failure] 9-9:
File is not properly formatted (gofumpt)
[failure] 9-9:
File is not properly formatted (gofmt)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build (1.23.x, windows-latest)
- GitHub Check: Compare
🔇 Additional comments (5)
common.go (1)
109-138: LGTM! Well-designed API surface.The three-function design is excellent:
GenerateSecureToken(length)returns(string, error)for explicit error handlingGenerateSecureTokenMust(length)panics for convenience when errors are unexpectedSecureToken()provides a sensible default (32 bytes)The documentation clearly warns about panic behavior, and the implementation correctly uses
crypto/randvia the testablerandReadindirection.common_test.go (4)
102-132: LGTM! Comprehensive test coverage.The test covers all essential cases:
- Various token lengths (8, 16, 32 bytes) with correct base64 length assertions
- Uniqueness verification
- Edge cases (zero and negative lengths properly default to 32 bytes)
134-149: LGTM! Solid concurrency test.The test verifies that 1000 concurrent token generations produce unique results, ensuring thread-safety. The pattern matches existing UUID concurrency tests in this file.
170-179: LGTM! Proper coverage of the convenience function.The test verifies
SecureToken()produces the expected 32-byte token (43 base64 characters) and ensures uniqueness across multiple calls.
393-408: LGTM! Excellent performance comparison benchmark.This benchmark provides a direct comparison between
UUIDv4andSecureToken, supporting the PR's performance claims. The structure with post-benchmark assertions ensures correctness alongside performance measurement.
- Remove duplicate GenerateSecureToken doc comments (AI error) - Replace fmt.Errorf with errors.New for simple error messages - Rename unused parameter 'b' to '_' in test mock function - Fix formatting (gofmt): import order and indentation - Add errors import to common_test.go All tests pass. Ready for review.
gaby
left a comment
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.
LGTM, just one small thing
- SecureToken() now returns (string, error) for explicit error handling - Add SecureTokenMust() as convenience wrapper that panics on failure - Update tests to handle new SecureToken() error return - Update benchmarks to use SecureTokenMust() for performance This provides both error-handling and fail-fast APIs, giving users choice while maintaining backward compatibility through the Must variant.
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.
Actionable comments posted: 1
🧹 Nitpick comments (2)
common_test.go (1)
152-169: Error-path test via randRead override is effective; consider a small commentOverriding
randReadwith a failing implementation and restoring it viadeferis a clean way to exercise both the error-returning API and the panic behavior of the Must variant. Since this test is non-parallel and the only place that mutatesrandRead, there’s no race today, but a brief comment noting the global mutation assumption would help future maintainers avoid addingt.Parallel()here or introducing other overrides that could race.common.go (1)
27-30: randRead indirection pattern looks good; comment can be tightenedUsing a package-level variable for the random reader makes the error-path testable without affecting production behavior, which is a solid approach. Minor nit: the comment is split mid-sentence (
so tests/can override it), which reads a bit awkwardly—consider rewriting as a single two-line comment for clarity.Example:
// randRead is a package-level indirection for crypto/rand.Read // that tests can override to simulate failures. var randRead = rand.Read
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (2)
common.go(3 hunks)common_test.go(3 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
common_test.go (1)
common.go (5)
GenerateSecureToken(107-116)GenerateSecureTokenMust(119-125)SecureToken(129-131)UUIDv4(95-101)SecureTokenMust(135-137)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (2)
- GitHub Check: Build (1.24.x, macos-latest)
- GitHub Check: Compare
🔇 Additional comments (5)
common_test.go (4)
103-133: GenerateSecureToken test coverage looks strongThis hits explicit sizes (8, 16, 32), validates encoded lengths, checks non-emptiness, and exercises the
length <= 0fallback with both0and-1. The uniqueness check between two 32-byte tokens is also appropriate. No changes needed.
135-150: Concurrency test is race-free and appropriateUsing a buffered channel and only populating the map from the main goroutine avoids data races while still stressing concurrent token generation across 1000 goroutines. This is a good balance of simplicity and coverage.
171-182: SecureToken test matches the helper’s contractThe test correctly asserts a 32-byte-derived encoded length, non-emptiness, and basic uniqueness across calls, which aligns with
SecureToken’s fixed-length wrapper behavior. Looks good.
379-411: Benchmarks for secure tokens vs UUIDv4 are well-structuredThe dedicated benchmarks for 16-byte and 32-byte tokens plus the comparative
Benchmark_TokenGeneratorsgive useful signal on performance and allocations. The final length checks in each sub-benchmark ensure the work isn’t optimized away. No changes needed.common.go (1)
103-131: Secure token core implementation and helpers look correctThe main helper defaults non-positive lengths to 32 bytes, allocates exactly that many bytes, and returns a wrapped error if reading randomness fails, which matches the intended contract and avoids silent fallbacks. The Must wrapper and
SecureTokenhelper cleanly delegate to this core, so callers can choose between explicit error handling and fail-fast behavior while always getting 32-byte entropy when using the convenience APIs. Implementation looks solid.
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
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.
Actionable comments posted: 0
🧹 Nitpick comments (1)
common.go (1)
103-116: GenerateSecureToken implementation matches its contractThe function correctly:
- Normalizes non‑positive
lengthto 32 (256‑bit entropy default).- Allocates exactly
lengthbytes and uses the indirectedrandRead.- Wraps failures with context via
%w.- Returns a URL‑safe token using
base64.RawURLEncoding.Behavior and doc comment are aligned. The only optional refinement would be to consider guarding against unreasonably large
lengthvalues if this ever takes user‑supplied input directly, but that’s not required for this utility.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (1)
common.go(3 hunks)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
- GitHub Check: Compare
- GitHub Check: Build (1.23.x, windows-latest)
- GitHub Check: Build (1.24.x, windows-latest)
🔇 Additional comments (5)
common.go (5)
9-12: New imports correctly support token encoding and error handling
encoding/base64andfmtare appropriate additions for URL‑safe encoding and wrapped error messages; nothing to change here.
27-29: randRead indirection is a clean test hookUsing a package‑level
randRead = rand.Readwith a short comment is a straightforward way to simulatecrypto/randfailures in tests without impacting production behavior. Just ensure any tests that overriderandReadrestore it (e.g., viadefer) before running in parallel.
118-125: GenerateSecureTokenMust wrapper is idiomatic and focusedThe Must variant delegates to
GenerateSecureTokenand panics only on error, which is idiomatic for “must” helpers around crypto randomness. The panic value includes the wrapped error, preserving useful context.
127-131: SecureToken convenience with error return is consistent and safe
SecureToken()simply fixes the entropy at 32 bytes while preserving the(string, error)contract. This keeps naming consistent withGenerateSecureTokenand pairs well with theSecureTokenMusthelper for callers that prefer fail‑fast behavior.
133-137: SecureTokenMust helper cleanly exposes the panic-on-failure path
SecureTokenMustclearly documents and implements the panic‑on‑failure behavior by delegating toGenerateSecureTokenMust(32). The doc comment now matches the exported name and semantics; no further changes needed.
|
@gemini-code-assist Should we add a maximum length for generating tokens? If so, what should it be and why? |
|
That's a great question, @gaby! I recommend adding a maximum length for generating tokens in Recommended Maximum Length: Reasoning:
This limit provides ample flexibility for various secure token needs while safeguarding against potential issues from overly large requests. |
Summary
This PR introduces a safer, flexible secure token API for Fiber, following the established google/uuid library pattern:
Why
Key changes
Safety & behavior
Checklist
Summary by CodeRabbit
New Features
Tests
✏️ Tip: You can customize this high-level summary in your review settings.