We were testing concurrent session feature , we need to return a specific error code for the old session to notify the user that another session was opened so user will know if others can access the account and can change the password, how can we do?
1 Answer(s)
-
0
Hi @smartech
To notify a user that they have been logged out because of a new session, we utilize the Security Stamp mechanism. When a new login occurs, the stamp is refreshed, making the old JWT token invalid.
Define the Error Code
Create a constant for the error code. This allows the frontend to identify the error programmatically without relying on string matching localized text.
namespace MyCompanyName.AbpZeroTemplate.Authorization { public static class AbpZeroTemplateErrorCodes { public const string ConcurrentSessionDetected = "ConcurrentSession"; } }Update the JWT Security Stamp Handler
Ensure your
JwtSecurityStampHandler.csis correctly validating the stamp against the cache or database. This method should returntrueif the session is still valid.public async Task<bool> Validate(ClaimsPrincipal claimsPrincipal) { if (claimsPrincipal?.Claims == null || !claimsPrincipal.Claims.Any()) { return false; } var securityStampKey = claimsPrincipal.Claims.FirstOrDefault(c => c.Type == AppConsts.SecurityStampKey); if (securityStampKey == null) { return false; } var userIdentifierString = claimsPrincipal.Claims.First(c => c.Type == AppConsts.UserIdentifier); var userIdentifier = UserIdentifier.Parse(userIdentifierString.Value); var isValid = await ValidateSecurityStampFromCache(userIdentifier, securityStampKey.Value); if (!isValid) { isValid = await ValidateSecurityStampFromDb(userIdentifier, securityStampKey.Value); } return isValid; }Throw the Exception in the Token Handler
In
AbpZeroTemplateAsyncJwtSecurityTokenHandler.cs, we intercept the validation result. If it returnsfalse, we throw aBusinessException.private static async Task ValidateSecurityStampAsync(ClaimsPrincipal principal) { ValidateUserDelegation(principal); using (var securityStampHandler = IocManager.Instance.ResolveAsDisposable<IJwtSecurityStampHandler>()) { if (!await securityStampHandler.Object.Validate(principal)) { throw new BusinessException(AbpZeroTemplateErrorCodes.ConcurrentSessionDetected) { Severity = LogSeverity.Warn }; } } }If you want a simpler approach that includes a built in message, you can create a custom exception class:
public class ConcurrentSessionException : UserFriendlyException { public ConcurrentSessionException() : base("ConcurrentSessionDetected", "Your session has been terminated due to a new login from another location.") { } public ConcurrentSessionException(string message) : base("ConcurrentSessionDetected", message) { } }Expected API Response Format
When the exception is thrown, the ABP Framework automatically wraps the result. Your frontend will receive a
500or403status with the following JSON body:{ "result": null, "targetUrl": null, "success": false, "error": { "code": 0, "message": "ConcurrentSessionDetected", "details": "Your session has been terminated due to a new login from another location.", "validationErrors": null }, "unAuthorizedRequest": false, "__abp": true }Handling the Error in Angular
In your
AppHttpInterceptor.ts(or your primary HTTP interceptor), you can catch this specific message and redirect the user to the login page with a warning.intercept(request: HttpRequest<any>, next: HttpHandler): Observable<HttpEvent<any>> { return next.handle(request).pipe( catchError(error => { if (error.error?.error?.message === 'ConcurrentSessionDetected') { abp.message.warn('A new login was detected from another device. Please log in again.'); this._router.navigate(['/account/login']); } return throwError(error); }) ); }Markdown is supportedCopy & paste or drag & drop images (max 30 MB per image)