How to check JWT during websocket..net core request

I am working on a small core .net application that uses JWT authentication and websites.

I have successfully completed the creation and verification of tokens for standard web-api controllers. However, I also want to check the token for the WebSocket request, which, of course, will not work with the [Authorize] attribute.

I installed the middleware pipeline as follows:

 app.UseWebSockets(); app.Use(async (http, next) => { if (http.WebSockets.IsWebSocketRequest == false) { await next(); return; } /// Handle websocket request here. How to check if token is valid? }); // secretKey contains a secret passphrase only your server knows var secretKey = .....; var signKey = new SigningCredentials ( new SymmetricSecurityKey(Encoding.ASCII.GetBytes(secretKey)), SecurityAlgorithms.HmacSha256 ); var tokenValidationParameters = new TokenValidationParameters { ValidateIssuer = false, ValidateAudience = false, // The signing key must match! ValidateIssuerSigningKey = true, IssuerSigningKey = signKey.Key, // Validate the token expiry ValidateLifetime = true, // If you want to allow a certain amount of clock drift, set that here: ClockSkew = TimeSpan.FromMinutes(1), }; app.UseJwtBearerAuthentication(new JwtBearerOptions { AutomaticAuthenticate = true, AutomaticChallenge = true, TokenValidationParameters = tokenValidationParameters }); 
+2
source share
2 answers

Hope this can help someone, although the post is old.

I found out the answer, not after Googling, but Binging! I inspired myself with this official code .

You can write your own class that handles authorization very simply using the magic of JwtBearerOptions. This class (hopefully) contains everything you need to test the JWT yourself.

So, you must enter it as a Service, and use it to configure authentication. Something like this in your Startup.ConfigureServices :

 this.JwtOptions = new JwtBearerOptions { AutomaticAuthenticate = true, AutomaticChallenge = true, TokenValidationParameters = yourTokenValidationParameters }; services.AddSingleton<JwtBearerOptions>(this.JwtOptions); 

Then you need to create a class that will validate your token ( Here my code was inspired ). Let me call him Protector because he brought you back !:

 public class JwtBearerBacker { public JwtBearerOptions Options { get; private set; } public JwtBearerBacker(JwtBearerOptions options) { this.Options = options; } public bool IsJwtValid(string token) { List<Exception> validationFailures = null; SecurityToken validatedToken; foreach (var validator in Options.SecurityTokenValidators) { if (validator.CanReadToken(token)) { ClaimsPrincipal principal; try { principal = validator.ValidateToken(token, Options.TokenValidationParameters, out validatedToken); } catch (Exception ex) { // Refresh the configuration for exceptions that may be caused by key rollovers. The user can also request a refresh in the event. if (Options.RefreshOnIssuerKeyNotFound && Options.ConfigurationManager != null && ex is SecurityTokenSignatureKeyNotFoundException) { Options.ConfigurationManager.RequestRefresh(); } if (validationFailures == null) validationFailures = new List<Exception>(1); validationFailures.Add(ex); continue; } return true; } } return false; } } 

Then in your middleware, just go into the request header, the JwtOptions dependency and call Backer:

  protected string ObtainAppTokenFromHeader(string authHeader) { if (string.IsNullOrWhiteSpace(authHeader) || !authHeader.Contains(" ")) return null; string[] authSchemeAndJwt = authHeader.Split(' '); string authScheme = authSchemeAndJwt[0]; if (authScheme != "Bearer") return null; string jwt = authSchemeAndJwt[1]; return jwt; } protected async Task<bool> AuthorizeUserFromHttpContext(HttpContext context) { var jwtBearerOptions = context.RequestServices.GetRequiredService<JwtBearerOptions>() as JwtBearerOptions; string jwt = this.ObtainAppTokenFromHeader(context.Request.Headers["Authorization"]); if (jwt == null) return false; var jwtBacker = new JwtBearerBacker(jwtBearerOptions); return jwtBacker.IsJwtValid(jwt); } public async Task Invoke(HttpContext context) { if (!context.WebSockets.IsWebSocketRequest) return; if (!await this.AuthorizeUserFromHttpContext(context)) { context.Response.StatusCode = 401; await context.Response.WriteAsync("The door is locked, dude. You're not authorized !"); return; } //... Whatever else you're doing in your middleware } 

In addition, AuthenticationTicket and any other auth information is already being processed by the JwtBearerMiddleware and will be returned anyway.

Finally, the client side. I advise you to use a client library that actually supports additional HTTP headers. For example, as far as I know, the W3C Javascript client does not provide this functionality.

Here you! Thanks to Microsoft for its open source codebase.

+4
source

I developed this a little differently, as I depended on WebSocket() on the client side.

Therefore, on the client side, I first authenticate the user to get the token and attach it to the header as a sub-protocol:

 socket = new WebSocket(connectionPath, ["client",token]); 

The token is sent in the request header under sec-websocket-protocol . Therefore, before authentication is activated, I extract the token and add it to the context.

  .AddJwtBearer(x => { // .... x.Events = new JwtBearerEvents { OnMessageReceived = context => { if (context.Request.Headers.ContainsKey("sec-websocket-protocol") && context.HttpContext.WebSockets.IsWebSocketRequest) { var token = context.Request.Headers["sec-websocket-protocol"].ToString(); // token arrives as string = "client, xxxxxxxxxxxxxxxxxxxxx" context.Token = token.Substring(token.IndexOf(',') + 1).Trim(); context.Request.Headers["sec-websocket-protocol"] = "client"; } return Task.CompletedTask; } }; 

Then on my WebSocket controller, I just [Authorize] attribute:

  [Authorize] [Route("api/[controller]")] public class WSController : Controller { [HttpGet] public async Task Get() { var context = ControllerContext.HttpContext; WebSocket currentSocket = await context.WebSockets.AcceptWebSocketAsync("client"); // it important to make sure the response returns the same subprotocol // ... } 
0
source

Source: https://habr.com/ru/post/1266162/


All Articles