Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit 6d6d30f

Browse files
Updated to use custom jwt middleware with a custom authorize attribute
1 parent 682a0bd commit 6d6d30f

File tree

6 files changed

+108
-52
lines changed

6 files changed

+108
-52
lines changed

‎Controllers/UsersController.cs

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,9 @@
11
using Microsoft.AspNetCore.Mvc;
2-
using Microsoft.AspNetCore.Authorization;
3-
using WebApi.Services;
42
using WebApi.Models;
3+
using WebApi.Services;
54

65
namespace WebApi.Controllers
76
{
8-
[Authorize]
97
[ApiController]
108
[Route("[controller]")]
119
public class UsersController : ControllerBase
@@ -17,9 +15,8 @@ public UsersController(IUserService userService)
1715
_userService = userService;
1816
}
1917

20-
[AllowAnonymous]
2118
[HttpPost("authenticate")]
22-
public IActionResult Authenticate([FromBody]AuthenticateRequest model)
19+
public IActionResult Authenticate(AuthenticateRequest model)
2320
{
2421
var response = _userService.Authenticate(model);
2522

@@ -29,6 +26,7 @@ public IActionResult Authenticate([FromBody]AuthenticateRequest model)
2926
return Ok(response);
3027
}
3128

29+
[Authorize]
3230
[HttpGet]
3331
public IActionResult GetAll()
3432
{

‎Helpers/AuthorizeAttribute.cs

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
using Microsoft.AspNetCore.Http;
2+
using Microsoft.AspNetCore.Mvc;
3+
using Microsoft.AspNetCore.Mvc.Filters;
4+
using System;
5+
using WebApi.Entities;
6+
7+
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method)]
8+
public class AuthorizeAttribute : Attribute, IAuthorizationFilter
9+
{
10+
public void OnAuthorization(AuthorizationFilterContext context)
11+
{
12+
var user = (User)context.HttpContext.Items["User"];
13+
if (user == null)
14+
{
15+
// not logged in
16+
context.Result = new JsonResult(new { message = "Unauthorized" }) { StatusCode = StatusCodes.Status401Unauthorized };
17+
}
18+
}
19+
}

‎Helpers/JwtMiddleware.cs

Lines changed: 63 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,63 @@
1+
using Microsoft.AspNetCore.Http;
2+
using Microsoft.Extensions.Options;
3+
using Microsoft.IdentityModel.Tokens;
4+
using System;
5+
using System.IdentityModel.Tokens.Jwt;
6+
using System.Linq;
7+
using System.Text;
8+
using System.Threading.Tasks;
9+
using WebApi.Services;
10+
11+
namespace WebApi.Helpers
12+
{
13+
public class JwtMiddleware
14+
{
15+
private readonly RequestDelegate _next;
16+
private readonly AppSettings _appSettings;
17+
18+
public JwtMiddleware(RequestDelegate next, IOptions<AppSettings> appSettings)
19+
{
20+
_next = next;
21+
_appSettings = appSettings.Value;
22+
}
23+
24+
public async Task Invoke(HttpContext context, IUserService userService)
25+
{
26+
var token = context.Request.Headers["Authorization"].FirstOrDefault()?.Split(" ").Last();
27+
28+
if (token != null)
29+
attachUserToContext(context, userService, token);
30+
31+
await _next(context);
32+
}
33+
34+
private void attachUserToContext(HttpContext context, IUserService userService, string token)
35+
{
36+
try
37+
{
38+
var tokenHandler = new JwtSecurityTokenHandler();
39+
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
40+
tokenHandler.ValidateToken(token, new TokenValidationParameters
41+
{
42+
ValidateIssuerSigningKey = true,
43+
IssuerSigningKey = new SymmetricSecurityKey(key),
44+
ValidateIssuer = false,
45+
ValidateAudience = false,
46+
// set clockskew to zero so tokens expire exactly at token expiration time (instead of 5 minutes later)
47+
ClockSkew = TimeSpan.Zero
48+
}, out SecurityToken validatedToken);
49+
50+
var jwtToken = (JwtSecurityToken)validatedToken;
51+
var userId = int.Parse(jwtToken.Claims.First(x => x.Type == "id").Value);
52+
53+
// attach user to context on successful jwt validation
54+
context.Items["User"] = userService.GetById(userId);
55+
}
56+
catch
57+
{
58+
// do nothing if jwt validation fails
59+
// user is not attached to context so request won't have access to secure routes
60+
}
61+
}
62+
}
63+
}

‎Services/UserService.cs

Lines changed: 11 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,11 @@
1+
using Microsoft.Extensions.Options;
2+
using Microsoft.IdentityModel.Tokens;
13
using System;
24
using System.Collections.Generic;
35
using System.IdentityModel.Tokens.Jwt;
46
using System.Linq;
57
using System.Security.Claims;
68
using System.Text;
7-
using Microsoft.Extensions.Options;
8-
using Microsoft.IdentityModel.Tokens;
99
using WebApi.Entities;
1010
using WebApi.Helpers;
1111
using WebApi.Models;
@@ -16,14 +16,15 @@ public interface IUserService
1616
{
1717
AuthenticateResponse Authenticate(AuthenticateRequest model);
1818
IEnumerable<User> GetAll();
19+
User GetById(int id);
1920
}
2021

2122
public class UserService : IUserService
2223
{
2324
// users hardcoded for simplicity, store in a db with hashed passwords in production applications
2425
private List<User> _users = new List<User>
25-
{
26-
new User { Id = 1, FirstName = "Test", LastName = "User", Username = "test", Password = "test" }
26+
{
27+
new User { Id = 1, FirstName = "Test", LastName = "User", Username = "test", Password = "test" }
2728
};
2829

2930
private readonly AppSettings _appSettings;
@@ -51,6 +52,11 @@ public IEnumerable<User> GetAll()
5152
return _users;
5253
}
5354

55+
public User GetById(int id)
56+
{
57+
return _users.FirstOrDefault(x => x.Id == id);
58+
}
59+
5460
// helper methods
5561

5662
private string generateJwtToken(User user)
@@ -60,10 +66,7 @@ private string generateJwtToken(User user)
6066
var key = Encoding.ASCII.GetBytes(_appSettings.Secret);
6167
var tokenDescriptor = new SecurityTokenDescriptor
6268
{
63-
Subject = new ClaimsIdentity(new Claim[]
64-
{
65-
new Claim(ClaimTypes.Name, user.Id.ToString())
66-
}),
69+
Subject = new ClaimsIdentity(new[] { new Claim("id", user.Id.ToString()) }),
6770
Expires = DateTime.UtcNow.AddDays(7),
6871
SigningCredentials = new SigningCredentials(new SymmetricSecurityKey(key), SecurityAlgorithms.HmacSha256Signature)
6972
};

‎Startup.cs

Lines changed: 10 additions & 37 deletions
Original file line numberDiff line numberDiff line change
@@ -4,57 +4,32 @@
44
using Microsoft.Extensions.DependencyInjection;
55
using WebApi.Helpers;
66
using WebApi.Services;
7-
using Microsoft.IdentityModel.Tokens;
8-
using System.Text;
9-
using Microsoft.AspNetCore.Authentication.JwtBearer;
107

118
namespace WebApi
129
{
1310
public class Startup
1411
{
12+
public IConfiguration Configuration { get; }
13+
1514
public Startup(IConfiguration configuration)
1615
{
1716
Configuration = configuration;
1817
}
1918

20-
public IConfiguration Configuration { get; }
21-
22-
// This method gets called by the runtime. Use this method to add services to the container.
19+
// add services to the DI container
2320
public void ConfigureServices(IServiceCollection services)
2421
{
2522
services.AddCors();
2623
services.AddControllers();
2724

28-
// configure strongly typed settings objects
29-
var appSettingsSection = Configuration.GetSection("AppSettings");
30-
services.Configure<AppSettings>(appSettingsSection);
31-
32-
// configure jwt authentication
33-
var appSettings = appSettingsSection.Get<AppSettings>();
34-
var key = Encoding.ASCII.GetBytes(appSettings.Secret);
35-
services.AddAuthentication(x =>
36-
{
37-
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
38-
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
39-
})
40-
.AddJwtBearer(x =>
41-
{
42-
x.RequireHttpsMetadata = false;
43-
x.SaveToken = true;
44-
x.TokenValidationParameters = new TokenValidationParameters
45-
{
46-
ValidateIssuerSigningKey = true,
47-
IssuerSigningKey = new SymmetricSecurityKey(key),
48-
ValidateIssuer = false,
49-
ValidateAudience = false
50-
};
51-
});
25+
// configure strongly typed settings object
26+
services.Configure<AppSettings>(Configuration.GetSection("AppSettings"));
5227

5328
// configure DI for application services
5429
services.AddScoped<IUserService, UserService>();
5530
}
5631

57-
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
32+
// configure the HTTP request pipeline
5833
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
5934
{
6035
app.UseRouting();
@@ -65,12 +40,10 @@ public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
6540
.AllowAnyMethod()
6641
.AllowAnyHeader());
6742

68-
app.UseAuthentication();
69-
app.UseAuthorization();
70-
71-
app.UseEndpoints(endpoints => {
72-
endpoints.MapControllers();
73-
});
43+
// custom jwt auth middleware
44+
app.UseMiddleware<JwtMiddleware>();
45+
46+
app.UseEndpoints(x => x.MapControllers());
7447
}
7548
}
7649
}

‎WebApi.csproj

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@
33
<TargetFramework>netcoreapp3.1</TargetFramework>
44
</PropertyGroup>
55
<ItemGroup>
6-
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.0" />
7-
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="5.6.0" />
6+
<PackageReference Include="Microsoft.AspNetCore.Authentication.JwtBearer" Version="3.1.6" />
7+
<PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="6.7.1" />
88
</ItemGroup>
99
</Project>

0 commit comments

Comments
(0)

AltStyle によって変換されたページ (->オリジナル) /