Enabling Users to Sign In
Controllers/AccountController.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 |
using grocery.Models; using JWT; using JWT.Algorithms; using JWT.Serializers; using Microsoft.AspNetCore.Identity; using Microsoft.AspNetCore.Mvc; using Microsoft.Extensions.Options; using System; using System.Collections.Generic; using System.Linq; using System.Threading.Tasks; namespace grocery.Controllers { [Route("api/[controller]")] public class AccountController : Controller { private readonly UserManager<IdentityUser> _userManager; private readonly SignInManager<IdentityUser> _signInManager; private readonly JWTSettings _options; public AccountController( UserManager<IdentityUser> userManager, SignInManager<IdentityUser> signInManager, IOptions<JWTSettings> optionsAccessor) { _userManager = userManager; _signInManager = signInManager; _options = optionsAccessor.Value; } [HttpPost] public async Task<IActionResult> Register([FromBody] Credentials Credentials) { if (ModelState.IsValid) { var user = new IdentityUser { UserName = Credentials.Email, Email = Credentials.Email }; var result = await _userManager.CreateAsync(user, Credentials.Password); if (result.Succeeded) { await _signInManager.SignInAsync(user, isPersistent: false); return new JsonResult(new Dictionary<string, object> { { "access_token", GetAccessToken(Credentials.Email) }, { "id_token", GetIdToken(user) } }); } return Errors(result); } return Error("Unexpected error"); } private string GetIdToken(IdentityUser user) { var payload = new Dictionary<string, object> { { "id", user.Id }, { "sub", user.Email }, { "email", user.Email }, { "emailConfirmed", user.EmailConfirmed }, }; return GetToken(payload); } private string GetAccessToken(string Email) { var payload = new Dictionary<string, object> { { "sub", Email }, { "email", Email } }; return GetToken(payload); } private string GetToken(Dictionary<string, object> payload) { var secret = _options.SecretKey; payload.Add("iss", _options.Issuer); payload.Add("aud", _options.Audience); payload.Add("nbf", ConvertToUnixTimestamp(DateTime.Now)); payload.Add("iat", ConvertToUnixTimestamp(DateTime.Now)); payload.Add("exp", ConvertToUnixTimestamp(DateTime.Now.AddDays(7))); IJwtAlgorithm algorithm = new HMACSHA256Algorithm(); IJsonSerializer serializer = new JsonNetSerializer(); IBase64UrlEncoder urlEncoder = new JwtBase64UrlEncoder(); IJwtEncoder encoder = new JwtEncoder(algorithm, serializer, urlEncoder); return encoder.Encode(payload, secret); } private JsonResult Errors(IdentityResult result) { var items = result.Errors .Select(x => x.Description) .ToArray(); return new JsonResult(items) { StatusCode = 400 }; } private JsonResult Error(string message) { return new JsonResult(message) { StatusCode = 400 }; } private static double ConvertToUnixTimestamp(DateTime date) { DateTime origin = new DateTime(1970, 1, 1, 0, 0, 0, 0, DateTimeKind.Utc); TimeSpan diff = date.ToUniversalTime() - origin; return Math.Floor(diff.TotalSeconds); } [HttpPost("sign-in")] public async Task<IActionResult> SignIn([FromBody] Credentials Credentials) { if (ModelState.IsValid) { var result = await _signInManager.PasswordSignInAsync(Credentials.Email, Credentials.Password, false, false); if (result.Succeeded) { var user = await _userManager.FindByEmailAsync(Credentials.Email); return new JsonResult(new Dictionary<string, object> { { "access_token", GetAccessToken(Credentials.Email) }, { "id_token", GetIdToken(user) } }); } return new JsonResult("Unable to sign in") { StatusCode = 401 }; } return Error("Unexpected error"); } } } |
การ log-in เข้าใช้งาน
ใช้ Postman ทำการ POST ไปที่
https://localhost:5001/api/account/sign-in
[code]
{
"email": "mr.phaisarn@gmail.com",
"password": "123456#User"
}
[/code]
Protecting ASP.NET Core API
Controllers/GroceryListController.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 |
using grocery.Data; using grocery.Models; using Microsoft.AspNetCore.Authorization; using Microsoft.AspNetCore.Mvc; using System.Collections.Generic; using System.Linq; namespace grocery.Controllers { [Authorize] [Route("api/[controller]")] public class GroceryListController : Controller { ...... } } |
Startup.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 |
using System.Net; using Microsoft.AspNetCore.Authentication.Cookies; ... // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddDbContext<UserDbContext>(options => options.UseSqlServer( Configuration.GetConnectionString("DefaultConnection"))); services.AddIdentity<IdentityUser, IdentityRole>() .AddEntityFrameworkStores<UserDbContext>(); services.Configure<CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddDbContext<GroceryListContext>(options => options.UseSqlServer( Configuration.GetConnectionString("DefaultConnection"))); services.Configure<JWTSettings>(Configuration.GetSection("JWTSettings")); services.ConfigureApplicationCookie(options => { options.Events.OnRedirectToLogin = context => { context.Response.StatusCode = 401; return Task.CompletedTask; }; }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); } |
Validating JWTs with ASP.NET Core
Startup.cs
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 |
using Microsoft.AspNetCore.Authentication.Cookies; using Microsoft.IdentityModel.Tokens; using System.Net; using System.Text; using Microsoft.AspNetCore.Authentication.JwtBearer; using Microsoft.Extensions.Logging; ... // This method gets called by the runtime. Use this method to add services to the container. public void ConfigureServices(IServiceCollection services) { services.AddDbContext<UserDbContext>(options => options.UseSqlServer( Configuration.GetConnectionString("DefaultConnection"))); services.AddIdentity<IdentityUser, IdentityRole>() .AddEntityFrameworkStores<UserDbContext>(); services.Configure<CookiePolicyOptions>(options => { // This lambda determines whether user consent for non-essential cookies is needed for a given request. options.CheckConsentNeeded = context => true; options.MinimumSameSitePolicy = SameSiteMode.None; }); services.AddDbContext<GroceryListContext>(options => options.UseSqlServer( Configuration.GetConnectionString("DefaultConnection"))); services.Configure<JWTSettings>(Configuration.GetSection("JWTSettings")); services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme) .AddJwtBearer(options => { options.TokenValidationParameters = new TokenValidationParameters { ValidateIssuer = true, ValidateAudience = true, ValidateLifetime = true, ValidateIssuerSigningKey = true, ValidIssuer = Configuration["Jwt:Issuer"], //ValidAudience = Configuration["Jwt:Issuer"], ValidAudience = Configuration["Jwt:Audience"], IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Jwt:Key"])) }; }); services.ConfigureApplicationCookie(options => { options.Events.OnRedirectToLogin = context => { context.Response.StatusCode = 401; return Task.CompletedTask; }; }); services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1); } |
นำ access_token มาใช้ขอ รายการหนังสือ
ใช้ Postman ทำการ GET ไปที่
https://localhost:5001/api/grocerylist
และส่ง access_token ไปด้วย