using System; using System.IdentityModel.Tokens.Jwt; using System.Linq; using System.Text; using System.Threading.Tasks; using Flexitime.Objects; using FlexitimeAPI.Models; using FlexitimeAPI.Services; using Microsoft.AspNetCore.Http; using Microsoft.AspNetCore.Mvc; using Microsoft.AspNetCore.Mvc.Filters; using Microsoft.Extensions.Options; using Microsoft.IdentityModel.Tokens; namespace FlexitimeAPI.Helpers { [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)] public class AuthorizeAttribute : Attribute, IAuthorizationFilter { public string[] Permissions { get; set; } public void OnAuthorization(AuthorizationFilterContext context) { var user = (User)context.HttpContext.Items["User"]; if (user == null) { // not logged in context.Result = new JsonResult( new {message = "Unauthorized"}) {StatusCode = StatusCodes.Status401Unauthorized}; } else if(Permissions.Any() && !user.Permissions.Select(y=>y.Tag) .Intersect(Permissions) .Any()) //check we have permissions if they have been specified { context.Result = context.Result = new JsonResult( new { message = "Unauthorized" }) { StatusCode = StatusCodes.Status401Unauthorized }; } } } public class JwtMiddleware { private readonly RequestDelegate _next; private readonly AppSettings _appSettings; public JwtMiddleware(RequestDelegate next, IOptions appSettings) { _next = next; _appSettings = appSettings.Value; } public async Task Invoke(HttpContext context, IUserService userService) { var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last(); if (token != null) AttachUserToContext(context, userService, token); await _next(context); } private void AttachUserToContext(HttpContext context, IUserService userService, string token) { try { var tokenHandler = new JwtSecurityTokenHandler(); var key = Encoding.ASCII.GetBytes(_appSettings.Secret); tokenHandler.ValidateToken(token, new TokenValidationParameters { ValidateIssuerSigningKey = true, IssuerSigningKey = new SymmetricSecurityKey(key), ValidateIssuer = false, ValidateAudience = false, // set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later) ClockSkew = TimeSpan.Zero }, out SecurityToken validatedToken); var jwtToken = (JwtSecurityToken)validatedToken; var userId = int.Parse(jwtToken.Claims.First(x => x.Type == "id").Value); // attach user to context on successful jwt validation context.Items["User"] = userService.GetById(userId); } catch { // do nothing if jwt validation fails // user is not attached to context so request won't have access to secure routes } } } }