-
-
Notifications
You must be signed in to change notification settings - Fork 233
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
Add unique user session check feature to Boilerplate (#9308) #9309
Add unique user session check feature to Boilerplate (#9308) #9309
Conversation
Important Review skippedAuto incremental reviews are disabled on this repository. Please check the settings in the CodeRabbit UI or the You can disable this status message by setting the WalkthroughThe changes in this pull request involve significant updates to the Bit Boilerplate template and its associated components, focusing on enhancing configurability and SignalR integration. A new parameter Changes
Assessment against linked issues
Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media? 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
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: 10
🧹 Outside diff range and nitpick comments (13)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/HubConnectionExtensions.cs (2)
5-7
: Improve method documentationThe current documentation is incomplete and contains a typo. Consider adding comprehensive XML documentation that explains:
- The purpose of the method
- The return value meaning (true/false cases)
- The cancellation token usage
- Potential exceptions
- /// <summary> - /// <inheritdoc cref="SignalREvents.PONG"/>"/> - /// </summary> + /// <summary> + /// Checks if the current user session is unique by performing a ping-pong test with the server. + /// </summary> + /// <param name="hubConnection">The SignalR hub connection</param> + /// <param name="cancellationToken">Cancellation token to cancel the operation</param> + /// <returns> + /// True if the session is unique (no PONG received within timeout), + /// False if another session exists (PONG received within timeout) + /// </returns> + /// <exception cref="ServerConnectionException">Thrown when hub connection is not in Connected state</exception>
12-30
: Enhance error handling and cleanupThe current error handling could be more robust:
- Consider catching and wrapping exceptions from
InvokeAsync
- Add timeout exception handling
- Consider implementing IAsyncDisposable for better cleanup
try { if (hubConnection.State is not HubConnectionState.Connected) throw new ServerConnectionException(AppStrings.ServerConnectionException); // ... existing code ... - await hubConnection.InvokeAsync("Ping", cancellationToken); + try + { + await hubConnection.InvokeAsync("Ping", sessionId, cancellationToken); + } + catch (Exception ex) when (ex is not OperationCanceledException) + { + throw new ServerConnectionException(AppStrings.ServerConnectionException, ex); + } // ... existing code ... } + catch (OperationCanceledException) + { + throw; + } + catch (Exception ex) when (ex is not ServerConnectionException) + { + throw new ServerConnectionException(AppStrings.ServerConnectionException, ex); + } finally { - disposable?.Dispose(); + if (disposable is not null) + { + await hubConnection.Remove(SignalREvents.PONG); + disposable.Dispose(); + } }src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Signalr/AppHub.cs (1)
37-39
: Enhance method documentationThe current documentation only references the PONG event. Consider adding more context about the method's purpose in session uniqueness verification.
Apply this diff:
/// <summary> - /// <inheritdoc cref="SignalREvents.PONG"/> + /// Handles ping requests for verifying user session uniqueness. + /// Responds with a PONG event to the specific client identified by their session ID. + /// This method is used as part of the session uniqueness verification mechanism. + /// </summary> + /// <remarks> + /// The client must be authenticated to use this endpoint. + /// The response is sent only to the requesting client using their session ID. + /// </remarks> + /// <exception cref="HubException">Thrown when the user context is not available.</exception>src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/SharedPubSubMessages.cs (1)
30-43
: Enhance documentation with implementation details and security considerations.While the documentation is comprehensive, consider adding:
- A reference to where the 3-second timeout is implemented
- The expected format/signature of the Ping method
- A security warning about proper token handling
/// <summary> /// While users are allowed to have multiple concurrent user sessions by **signing in** on multiple devices or browsers, /// copying access and refresh tokens from one device / browser to another is prohibited. /// /// This method uses a SignalR mechanism to check for potential misuse of access and refresh tokens across different app instances: - /// - The client calls a `Ping` method on the server. + /// - The client calls a `Ping()` method on the server (implemented in AppHub). /// - The server sends a `PONG` to the specific user session. /// - Because SignalR's connection id is the same is user session's Id (Thanks to AppHubConnectionHandler's implementation), /// - Only one user session will receive the `PONG` event. - /// - If the validation event is not received within a 3-second timeout, it indicates that there's something wrong. + /// - If the validation event is not received within a 3-second timeout (configured in HubConnectionExtensions), it indicates that there's something wrong. /// - The user might have illegally copied tokens to another device or browser. /// - The user may have lost their SignalR connection to the server. /// - It could also be another open tab in the same browser or another instance of the app. + /// + /// SECURITY WARNING: Ensure proper token validation and revocation mechanisms are in place to prevent token reuse across sessions. /// </summary>src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IClientCoreServiceCollectionExtensions.cs (1)
134-134
: Consider documenting the retry policy behavior.The
SignalRInfinitiesRetryPolicy
is registered as a singleton, but its behavior and configuration are not immediately clear. Consider adding XML documentation to explain the retry intervals and maximum attempts.src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs (2)
Line range hint
70-85
: Remove duplicate session removal operations and add transaction scopeThere are several issues in the SignOut method:
- The session is being removed twice from DbContext
- The second removal creates a new entity unnecessarily
- Multiple database operations should be wrapped in a transaction
Apply this diff to fix the issues:
public async Task SignOut(CancellationToken cancellationToken) { var currentSessionId = User.GetSessionId(); var userSession = await DbContext.UserSessions //#if (notification == true) .Include(us => us.PushNotificationSubscription) //#endif .FirstOrDefaultAsync(us => us.Id == currentSessionId, cancellationToken) ?? throw new ResourceNotFoundException(); - DbContext.UserSessions.Remove(userSession); - await DbContext.SaveChangesAsync(cancellationToken); - - DbContext.UserSessions.Remove(new() { Id = currentSessionId }); - await DbContext.SaveChangesAsync(cancellationToken); + await using var transaction = await DbContext.Database.BeginTransactionAsync(cancellationToken); + try + { + DbContext.UserSessions.Remove(userSession); + await DbContext.SaveChangesAsync(cancellationToken); + await transaction.CommitAsync(cancellationToken); + } + catch + { + await transaction.RollbackAsync(cancellationToken); + throw; + } SignOut(); }
Add transaction scope but keep the current error message
The review comment's suggestion to add a transaction scope is valid, but the suggestion to change the error message isn't necessary since:
- The current comment-style error message ("Call SignOut instead") is clear and follows the codebase's pattern of using simple BadRequestException for validation errors
- The search results don't show a consistent pattern of using localized strings for all error messages, and many BadRequestException usages are without messages
- The suggested localization key "CannotRevokeCurrentSession" doesn't exist in the codebase
Here's the recommended change:
public async Task RevokeSession(Guid id, CancellationToken cancellationToken) { var userId = User.GetUserId(); var currentSessionId = User.GetSessionId(); if (id == currentSessionId) throw new BadRequestException(); // "Call SignOut instead" var userSession = await DbContext.UserSessions //#if (notification == true) .Include(us => us.PushNotificationSubscription) //#endif .FirstOrDefaultAsync(us => us.Id == id, cancellationToken) ?? throw new ResourceNotFoundException(); + await using var transaction = await DbContext.Database.BeginTransactionAsync(cancellationToken); + try + { DbContext.UserSessions.Remove(userSession); await DbContext.SaveChangesAsync(cancellationToken); //#if (signalR == true) await appHubContext.Clients.Client(userSession.Id.ToString()).SendAsync(SignalREvents.PUBLISH_MESSAGE, SharedPubSubMessages.SESSION_REVOKED, cancellationToken); //#endif + await transaction.CommitAsync(cancellationToken); + } + catch + { + await transaction.RollbackAsync(cancellationToken); + throw; + } }🔗 Analysis chain
Line range hint
92-108
: Add transaction scope and improve error messageThe RevokeSession method needs some improvements:
- Database operations should be wrapped in a transaction
- The error message for attempting to revoke current session could be more descriptive
Apply this diff to improve the implementation:
public async Task RevokeSession(Guid id, CancellationToken cancellationToken) { var userId = User.GetUserId(); var currentSessionId = User.GetSessionId(); if (id == currentSessionId) - throw new BadRequestException(); // "Call SignOut instead" + throw new BadRequestException(Localizer[nameof(AppStrings.CannotRevokeCurrentSession)]); var userSession = await DbContext.UserSessions //#if (notification == true) .Include(us => us.PushNotificationSubscription) //#endif .FirstOrDefaultAsync(us => us.Id == id, cancellationToken) ?? throw new ResourceNotFoundException(); + await using var transaction = await DbContext.Database.BeginTransactionAsync(cancellationToken); + try + { DbContext.UserSessions.Remove(userSession); await DbContext.SaveChangesAsync(cancellationToken); //#if (signalR == true) await appHubContext.Clients.Client(userSession.Id.ToString()).SendAsync(SignalREvents.PUBLISH_MESSAGE, SharedPubSubMessages.SESSION_REVOKED, cancellationToken); //#endif + await transaction.CommitAsync(cancellationToken); + } + catch + { + await transaction.RollbackAsync(cancellationToken); + throw; + } }Let's verify if the localization key exists:
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash # Check if the localization key exists in the resource files rg -l "CannotRevokeCurrentSession" --type csLength of output: 44
Script:
#!/bin/bash # Let's check for any string resource files or constants that might contain error messages fd "AppStrings" --type f --extension cs # Also check for any similar error messages about session revocation rg -i "session.*revok" --type cs rg -i "current.*session" --type csLength of output: 6456
Script:
#!/bin/bash # Let's check for AppStrings class definition and any session-related string constants fd "AppStrings" --type f --extension cs --exec ast-grep --pattern 'class AppStrings { $$$ }' # Also check for any BadRequestException usage with custom messages rg -A 2 "BadRequestException\(" --type csLength of output: 26865
src/Templates/Boilerplate/Bit.Boilerplate/.template.config/template.json (1)
Line range hint
89-95
: Consider enhancing SignalR configuration options.Given the addition of the unique user session check feature, consider:
- Adding documentation about this feature in the SignalR parameter description
- Potentially making the unique session check configurable independently for cases where users want SignalR without session uniqueness checks
Apply this diff to enhance the documentation:
"signalR": { "displayName": "Add SignalR?", "type": "parameter", "datatype": "bool", "defaultValue": "false", - "description": "Add SignalR sample." + "description": "Add SignalR sample with unique user session check capability." },src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.fa.resx (1)
1154-1157
: Consider improving the concurrent session message formatting.While the translation is accurate, the double quotes in the message value could be simplified for better maintainability.
Apply this diff to improve the formatting:
<data name="ConcurrentUserSessionOnTheSameDevice" xml:space="preserve"> - <value>"لطفاً اطمینان حاصل کنید که این اپلیکیشن بیش از یکبار، به طور همزمان روی یک دستگاه یا مرورگر باز نیست، و دوباره تلاش کنید."</value> + <value>لطفاً اطمینان حاصل کنید که این اپلیکیشن بیش از یکبار، به طور همزمان روی یک دستگاه یا مرورگر باز نیست، و دوباره تلاش کنید.</value>src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/ClientAppCoordinator.cs (1)
Line range hint
149-182
: Incorrect initialization ofsignalROnDisposables
The
signalROnDisposables
list is initialized incorrectly using[]
, which is not valid in C#. It should be initialized usingnew List<IDisposable>()
.Apply this diff to fix the initialization:
-private List<IDisposable> signalROnDisposables = []; +private List<IDisposable> signalROnDisposables = new List<IDisposable>();src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs (3)
16-17
: Be explicit with access modifiers for consistency.Consider adding the
private
access modifier to thehub
field to maintain consistency with other field declarations and improve code readability.Apply this diff to add the
private
access modifier:-[AutoInject] HubConnection hub = default!; +[AutoInject] private HubConnection hub = default!;
Line range hint
30-31
: Correct the lifecycle method name toOnInitializedAsync
.The method
OnInitAsync
does not exist in the Blazor component lifecycle. It should beOnInitializedAsync
. Ensure you're overriding the correct method to initialize your component asynchronously.Apply this diff to fix the method name:
-protected override async Task OnInitAsync() +protected override async Task OnInitializedAsync()
60-62
: Provide user-friendly feedback when the session is not unique.Throwing a
ForbiddenException
may not provide the best user experience. Consider displaying a user-friendly message or handling the situation in a way that guides the user on how to resolve the issue of concurrent sessions.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
📒 Files selected for processing (11)
src/Templates/Boilerplate/Bit.Boilerplate/.template.config/template.json
(1 hunks)src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/ClientAppCoordinator.cs
(8 hunks)src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs
(3 hunks)src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/HubConnectionExtensions.cs
(1 hunks)src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IClientCoreServiceCollectionExtensions.cs
(2 hunks)src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs
(2 hunks)src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Signalr/AppHub.cs
(1 hunks)src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.fa.resx
(2 hunks)src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.nl.resx
(2 hunks)src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.resx
(1 hunks)src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/SharedPubSubMessages.cs
(1 hunks)
🔇 Additional comments (18)
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/HubConnectionExtensions.cs (1)
17-24
:
Review the session uniqueness logic and timeout handling
Several concerns with the current implementation:
- The return value seems counterintuitive - a timeout (no PONG) indicates a unique session
- The 3-second timeout is hardcoded and might not be suitable for all network conditions
- The ping-pong exchange doesn't include any session-specific data for validation
Consider these improvements:
+ private const int DefaultTimeoutSeconds = 3;
+
public static async Task<bool> IsUserSessionUnique(this HubConnection hubConnection,
- CancellationToken cancellationToken)
+ CancellationToken cancellationToken, int timeoutSeconds = DefaultTimeoutSeconds)
{
- disposable = hubConnection.On(SignalREvents.PONG, () =>
+ var sessionId = Guid.NewGuid().ToString();
+ disposable = hubConnection.On<string>(SignalREvents.PONG, (responseSessionId) =>
{
+ if (responseSessionId == sessionId)
pongTcs!.SetResult();
});
- await hubConnection.InvokeAsync("Ping", cancellationToken);
+ await hubConnection.InvokeAsync("Ping", sessionId, cancellationToken);
- return await Task.WhenAny(Task.Delay(TimeSpan.FromSeconds(3), cancellationToken), pongTcs!.Task) != pongTcs.Task;
+ var timeoutTask = Task.Delay(TimeSpan.FromSeconds(timeoutSeconds), cancellationToken);
+ var completedTask = await Task.WhenAny(timeoutTask, pongTcs.Task);
+ return completedTask == pongTcs.Task;
Let's verify the server-side implementation matches these changes:
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Signalr/AppHub.cs (1)
36-44
: Implementation aligns well with session uniqueness check requirements
The Ping method implementation provides a secure way to verify session uniqueness by:
- Requiring authentication
- Using session-specific client communication
- Integrating with the broader SignalR infrastructure
Let's verify the complete session check implementation:
✅ Verification successful
Session uniqueness check implementation is correctly implemented and verified
The implementation is well-designed and complete:
- Server-side
AppHub.Ping()
uses session-specific client communication viaGetSessionId()
AppHubConnectionHandler
enforces unique session IDs by settingConnectionId
to user's session ID- Client-side
HubConnectionExtensions.IsUserSessionUnique()
implements timeout-based uniqueness check:- Sets up PONG handler
- Invokes Ping
- Returns true if no PONG received within 3 seconds (indicating session is unique)
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify the complete session uniqueness check implementation
# Looking for the HubConnectionExtensions class that uses this Ping method
# Search for HubConnectionExtensions implementation
ast-grep --pattern 'class HubConnectionExtensions {
$$$
IsUserSessionUnique($$$) {
$$$
}
$$$
}'
# Search for any other session uniqueness related code
rg -l "IsUserSessionUnique|GetSessionId"
Length of output: 1536
Script:
#!/bin/bash
# Let's examine the client-side implementation and the hub connection handler
# Check HubConnectionExtensions implementation
cat src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/HubConnectionExtensions.cs
# Check AppHubConnectionHandler implementation
cat src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Signalr/AppHubConnectionHandler.cs
# Check ClaimsPrincipalExtensions for GetSessionId implementation
cat src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Extensions/ClaimsPrincipalExtensions.cs
Length of output: 4685
src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Services/SharedPubSubMessages.cs (2)
44-44
: LGTM! Constant declaration follows best practices.
The constant declaration is well-implemented:
- Uses
nameof
operator to prevent string literals - Maintains consistency with other SignalR event constants
- Has appropriate visibility level
29-44
: Verify the complete implementation chain for session uniqueness check.
Let's verify that all components required for the session uniqueness check are properly implemented:
✅ Verification successful
Let me gather more information about the implementation chain, particularly focusing on the HubConnectionExtensions and AppHub implementations.
Let me verify one final aspect about the session ID handling to complete the verification chain.
Complete implementation chain for session uniqueness check is properly implemented
The verification confirms that all components are correctly implemented and connected:
-
Server-side (AppHub):
- Correctly implements
Ping
method that sends PONG to specific client using session ID - AppHubConnectionHandler properly sets connection ID to user's session ID
- Correctly implements
-
Client-side:
- HubConnectionExtensions implements
IsUserSessionUnique
with proper timeout handling - PONG event handler is properly registered and managed
- HubConnectionExtensions implements
-
Session Management:
- Session IDs are consistently managed through ClaimsPrincipal extensions
- Database properly tracks user sessions with appropriate relationships
- Session revocation and cleanup are properly implemented
The implementation ensures that only one device/browser instance can validate its session through the SignalR ping-pong mechanism, effectively preventing token misuse across different app instances.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Description: Verify the complete implementation chain for session uniqueness check
# Check for Ping method implementation
echo "Checking for Ping method implementation..."
ast-grep --pattern 'class AppHub {
$$$
Ping() {
$$$
}
$$$
}'
# Check for IsUserSessionUnique implementation
echo "Checking for IsUserSessionUnique extension method..."
ast-grep --pattern 'IsUserSessionUnique($$$) {
$$$
}'
# Check for timeout configuration
echo "Checking for timeout configuration..."
rg -A 2 "3.*second.*timeout"
# Check for PONG usage in connection handling
echo "Checking for PONG constant usage..."
rg "PONG" --type cs
Length of output: 2334
Script:
#!/bin/bash
# Check for the complete implementation of IsUserSessionUnique
echo "Checking HubConnectionExtensions implementation..."
rg -A 10 "IsUserSessionUnique" --type cs
# Check for AppHub's Ping implementation
echo "Checking AppHub's Ping implementation..."
rg -A 5 "public.*Ping" --type cs
# Check for AppHubConnectionHandler implementation
echo "Checking AppHubConnectionHandler implementation..."
rg -A 5 "class AppHubConnectionHandler" --type cs
Length of output: 5914
Script:
#!/bin/bash
# Check for GetSessionId implementation and usage
echo "Checking GetSessionId implementation..."
rg -A 5 "GetSessionId" --type cs
# Check for any session ID related configuration
echo "Checking session ID configuration..."
rg -A 5 "SessionId|UserSession" --type cs --type json
Length of output: 85837
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/IClientCoreServiceCollectionExtensions.cs (3)
14-17
: LGTM: SignalR imports are properly configured.
The required SignalR client packages are correctly imported and appropriately wrapped in conditional compilation.
133-164
: Verify unique session check implementation.
While the SignalR infrastructure is set up, the actual unique session check implementation is not visible in this file.
Let's verify the implementation exists:
✅ Verification successful
Unique session check implementation is properly implemented
The unique session check is implemented in HubConnectionExtensions.cs
through the IsUserSessionUnique
method and is being utilized in ProfileSection.razor.cs
to prevent concurrent user sessions on the same device.
HubConnectionExtensions.cs
implements the check using SignalR's hub connectionProfileSection.razor.cs
enforces the check by throwing aForbiddenException
when concurrent sessions are detected
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for unique session check implementation
rg -A 10 "IsUserSessionUnique|CheckUserSession|ValidateSession"
# Search for hub methods related to session checks
ast-grep --pattern 'public $_ $_($$) {
$$$
// Check for unique session related code
$$$
}'
Length of output: 3839
144-147
: Review WebSocket-only configuration.
While WebSockets offer better performance, skipping negotiation and disabling fallback transports might cause connectivity issues in environments where WebSocket connections are blocked (e.g., certain corporate proxies).
Consider running this script to check if the codebase handles connection failures:
Consider implementing a fallback mechanism using Server-Sent Events (SSE) when WebSocket connections fail.
✅ Verification successful
Let's search for connection handling patterns with broader terms and also look for any SignalR configuration code that might include error handling or reconnection logic.
Based on the search results, I can see that the application has proper error handling and reconnection logic for SignalR:
- The client uses
WithAutomaticReconnect()
when configuring the hub connection - The
ClientAppCoordinator
class implements comprehensive connection state handling:- Tracks connection state changes via
HubConnectionStateChange
- Handles reconnection events via
HubConnectionConnected
- Publishes online/offline status changes
- Logs connection errors and attempts to refresh tokens when unauthorized
- Has retry logic built in
- Tracks connection state changes via
WebSocket-only configuration is appropriate with the implemented fallback mechanisms.
The code already has robust error handling and automatic reconnection capabilities built in. While WebSocket is preferred for performance, the automatic reconnection will handle any connection issues gracefully.
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for connection error handling patterns
rg -A 5 "HubConnectionState\.Disconnected|OnReconnecting|OnReconnected|catch.*HubException"
Length of output: 93
Script:
#!/bin/bash
# Search for SignalR connection handling patterns with broader context
rg -i -A 5 "hubconnection|signalr.*connect|withautomaticreconnect|connection.*error"
# Search for any error handling in client-side code
rg -i -A 5 "catch.*exception.*signalr|onerror.*signalr|connection.*failed"
# Look for SignalR configuration and connection setup
ast-grep --pattern 'WithAutomaticReconnect($$$)'
Length of output: 165738
src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Controllers/Identity/UserController.cs (1)
Line range hint 1-1
: Verify implementation of session uniqueness check feature
While the session management improvements are good, I don't see the implementation of the unique user session check feature mentioned in the PR objectives. This might be implemented in other files, but we should verify its integration with these session management methods.
Let's check for the implementation:
✅ Verification successful
Let me check the implementation of the unique session check by examining the hub files.
Session uniqueness check is properly implemented
The unique session check feature is correctly implemented through SignalR:
- Server assigns user's session ID as the SignalR connection ID in
AppHubConnectionHandler
- Client's
IsUserSessionUnique
method inHubConnectionExtensions
performs a ping-pong check - If multiple sessions exist, the ping response won't be received within timeout, indicating non-unique session
- The feature is integrated with session management as seen in
ProfileSection.razor.cs
🏁 Scripts executed
The following scripts were executed for the analysis:
Script:
#!/bin/bash
# Search for unique session check implementation
echo "Searching for unique session check implementation..."
rg -A 5 "IsUserSessionUnique|UniqueSession" --type cs
# Search for hub connection related files
echo "Searching for hub connection related files..."
fd -e cs -e razor | rg -i "hub|connection"
Length of output: 3296
Script:
#!/bin/bash
# Check AppHub implementation
echo "Checking AppHub implementation..."
cat "src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Signalr/AppHub.cs"
# Check AppHubConnectionHandler implementation
echo "Checking AppHubConnectionHandler implementation..."
cat "src/Templates/Boilerplate/Bit.Boilerplate/src/Server/Boilerplate.Server.Api/Signalr/AppHubConnectionHandler.cs"
# Check HubConnectionExtensions implementation
echo "Checking HubConnectionExtensions implementation..."
cat "src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Extensions/HubConnectionExtensions.cs"
Length of output: 5178
src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.fa.resx (2)
Line range hint 1145-1152
: LGTM: reCAPTCHA localization strings properly implemented.
The Google reCAPTCHA error messages are well-structured and appropriately placed within conditional compilation directives.
1154-1157
: Implementation aligns well with PR objectives.
The added localization strings effectively support the unique user session check feature by providing clear Persian language feedback to users when concurrent sessions are detected.
src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.resx (1)
1154-1158
: LGTM! The string resource aligns with the unique session check feature.
The addition of this localized string resource:
- Supports the PR's objective of implementing unique session checks
- Is properly scoped within SignalR conditional compilation
- Provides clear user guidance when concurrent sessions are detected
src/Templates/Boilerplate/Bit.Boilerplate/src/Shared/Resources/AppStrings.nl.resx (1)
Line range hint 1145-1150
: LGTM!
The Google reCAPTCHA error messages are properly formatted and correctly localized in Dutch.
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/ClientAppCoordinator.cs (5)
22-22
: Injection of HubConnection
is correctly implemented
The HubConnection
is properly auto-injected for SignalR functionality.
36-38
: Injection of IPushNotificationService
is correctly implemented
The IPushNotificationService
is appropriately auto-injected when notifications are enabled.
72-74
: SignalR event subscription initialized appropriately
The SubscribeToSignalREventsMessages()
method is correctly invoked when SignalR is enabled.
134-134
: SignalR connection started after user authentication
The StartSignalR()
method is appropriately called after user ID propagation to establish the SignalR connection.
267-270
: Proper disposal of event handlers and subscriptions
The event handlers attached to hubConnection
are correctly detached, and signalROnDisposables
are disposed of to prevent memory leaks.
src/Templates/Boilerplate/Bit.Boilerplate/src/Client/Boilerplate.Client.Core/Components/Pages/Authorized/Settings/ProfileSection.razor.cs (1)
2-4
: Verify conditional compilation directives.
The use of //#if
and //#endif
for conditional compilation may not be recognized by the standard C# preprocessor. Ensure that your build system or templating engine correctly processes these directives to include or exclude SignalR code as intended.
If you're using a custom preprocessing step or templating system, please confirm that it handles these directives appropriately.
This closes #9308
Summary by CodeRabbit
Release Notes
New Features
Ping
method for client communication in the SignalR hub.Bug Fixes
Localization
Documentation