public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication()
.AddJwtBearer(cfg =>
{
cfg.RequireHttpsMetadata = false;
cfg.SaveToken = true;
cfg.TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = Configuration["Tokens:Issuer"],
ValidAudience = Configuration["Tokens:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Tokens:Key"]))
};
});
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvc();
}
}
Controller
[ApiController]
public class TokenController : ControllerBase
{
private readonly UserManager<JwtApiUser> _userManager;
private readonly SignInManager<JwtApiUser> _signInManager;
private readonly IConfiguration _config;
public TokenController(
UserManager<JwtApiUser> userManager,
SignInManager<JwtApiUser> signInManager,
IConfiguration config)
{
_userManager = userManager;
_signInManager = signInManager;
_config = config;
}
//POST: /api/token
[HttpPost]
public async Task<IActionResult> GenerateToken([FromBody] LoginViewModel model)
{
if (ModelState.IsValid)
{
var user = await _userManager.FindByEmailAsync(model.Email);
if (user != null)
{
var result = await _signInManager.CheckPasswordSignInAsync(user, model.Password, false);
if (result.Succeeded)
{
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(_config["Tokens:Issuer"],
_config["Tokens:Issuer"],
claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds);
return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
}
}
}
return BadRequest("Could not create token");
}
Is it possible to provide with complete code where once the token is successfully accepted than you can access the data from any test controller.
I want once user authenticate with the token and username & password it should access the data.
EmployeeController.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using MyCoreApi.Models;
// For more information on enabling Web API for empty projects, visit https://go.microsoft.com/fwlink/?LinkID=397860
namespace MyCoreApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class EmployeeController : ControllerBase
{
private readonly ApiDbContext _context;
public EmployeeController(ApiDbContext context)
{
_context = context;
}
// GET: api/<controller>
[HttpGet]
public async Task<ActionResult<IEnumerable<Employee>>> Get()
{
return await _context.Employee.ToListAsync();
}
}
}
After you got a token, to access data/resources, you can make request and pass valid token in HTTP Authorization header.
With Regards,
Fei Han
.NET forums are moving to a new home on Microsoft Q&A, we encourage you to go to Microsoft Q&A for .NET for posting new questions and get involved today.
Is it possible to provide with complete code where once the token is successfully accepted than you can access the data from any test controller.
Add the [Authorize] attribute to the actions or controllers you wish to secure.
[Authorize(AuthenticationSchemes = JwtBearerDefaults.AuthenticationScheme)]
[Route("api/[controller]")]
[ApiController]
public class ValuesController : ControllerBase
{
// GET api/values
[HttpGet]
public ActionResult<IEnumerable<string>> Get()
{
return new string[] { "value1", "value2" };
}
shabbir_215
I want once user authenticate with the token and username & password it should access the data.
Your client application must send the token in the HTTP header when accessing secured resources. You client application is responsible for persisting the token as well. Frankly, it is a bit confusing that you previously created token based security but
do not understand how token security works.
When i made a request to the Postman, i get below error
{"type":"https://tools.ietf.org/html/rfc7231#section-6.5.13","title":"Unsupported Media Type","status":415,"traceId":"80000008-0001-fd00-b63f-84710c7967bb"}
Please share the screenshot of the request you sent (with request headers and body details), so that we can understand and troubleshoot the issue better.
With Regards,
Fei Han
.NET forums are moving to a new home on Microsoft Q&A, we encourage you to go to Microsoft Q&A for .NET for posting new questions and get involved today.
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.HttpsPolicy;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.Logging;
using Microsoft.Extensions.Options;
using Microsoft.IdentityModel.Tokens;
namespace MyCoreApi
{
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
// This method gets called by the runtime. Use this method to add services to the container.
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<MyCoreApi.Models.ApiDbContext>(options => options.UseSqlServer(Configuration.GetConnectionString("ApiDbContext")));
services.AddAuthentication()
.AddJwtBearer(cfg =>
{
cfg.RequireHttpsMetadata = false;
cfg.SaveToken = true;
cfg.TokenValidationParameters = new TokenValidationParameters()
{
ValidIssuer = Configuration["Tokens:Issuer"],
ValidAudience = Configuration["Tokens:Issuer"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["Tokens:Key"]))
};
});
services.AddDefaultIdentity<IdentityUser>()
.AddRoles<IdentityRole>()
.AddDefaultUI(Microsoft.AspNetCore.Identity.UI.UIFramework.Bootstrap4)
.AddEntityFrameworkStores<MyCoreApi.Models.ApiDbContext>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_2);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseAuthentication();
app.UseDefaultFiles();
app.UseMvc();
}
}
}
using System;
using System.Collections.Generic;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Security.Claims;
using System.Text;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Identity;
using Microsoft.AspNetCore.Mvc;
using Microsoft.Extensions.Configuration;
using Microsoft.IdentityModel.Tokens;
using MyCoreApi.Models;
namespace MyCoreApi.Controllers
{
[Route("api/[controller]")]
[ApiController]
public class TokenController : ControllerBase
{
private readonly UserManager<IdentityUser> _userManager;
private readonly SignInManager<IdentityUser> _signInManager;
private readonly IConfiguration _config;
public TokenController(
UserManager<IdentityUser> userManager,
SignInManager<IdentityUser> signInManager,
IConfiguration config)
{
_userManager = userManager;
_signInManager = signInManager;
_config = config;
}
[HttpPost]
public async Task<IActionResult> GenerateToken([FromBody] UserModel model)
{
if (ModelState.IsValid)
{
var user = await _userManager.FindByIdAsync(model.UserName);
if (user != null)
{
var result = await _signInManager.CheckPasswordSignInAsync(user, model.Password, false);
if (result.Succeeded)
{
var claims = new[]
{
new Claim(JwtRegisteredClaimNames.Sub, user.Email),
new Claim(JwtRegisteredClaimNames.Jti, Guid.NewGuid().ToString()),
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(_config["Tokens:Issuer"],
_config["Tokens:Issuer"],
claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds);
return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
}
}
}
return BadRequest("Could not create token");
}
}
}
Usermodel.cs
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Web;
namespace MyCoreApi.Models
{
public class UserModel
{
[Required]
[Display(Name = "User name")]
public string UserName { get; set; }
[Required]
[StringLength(100, ErrorMessage = "The {0} must be at least {2} characters long.", MinimumLength = 6)]
[DataType(DataType.Password)]
[Display(Name = "Password")]
public string Password { get; set; }
[DataType(DataType.Password)]
[Display(Name = "Confirm password")]
[Compare("Password", ErrorMessage = "The password and confirmation password do not match.")]
public string ConfirmPassword { get; set; }
}
}
ApiDbContext.cs
using Microsoft.EntityFrameworkCore;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace MyCoreApi.Models
{
public class ApiDbContext : DbContext
{
public ApiDbContext(DbContextOptions<ApiDbContext> options)
: base(options)
{
}
public DbSet<Employee> Employee { get; set; }
}
}
When i made a request to the Postman, i get below error
{"type":"https://tools.ietf.org/html/rfc7231#section-6.5.13","title":"Unsupported Media Type","status":415,"traceId":"80000008-0001-fd00-b63f-84710c7967bb"}
Thanks,
Shabbir
A 415 error means the content type is unexpected. The example uses Identity to store user accounts. Did you create a user account? Were you able to generated a JWT token? Can you explain the steps that cause the error.
Unfortunately I haven't able to create user account neither JWT token.
I thought of using ASP.NET identity using SQL ASP.NET identity user table for authentication.
You still have not clarified what you're doing. Below is a simplified token generator that does not use Identity.
[HttpGet]
public IActionResult GenerateTokens(string userId)
{
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, userId)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(_config["Tokens:Issuer"],
_config["Tokens:Issuer"],
claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds);
return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
}
So my final requirement is before i generate a token it should Authenticate with username and password which can be ASP.NET Identity users and after authentication it generates a token for you. Do you have any example like that would be much appreciated
So my final requirement is before i generate a token it should Authenticate with username and password which can be ASP.NET Identity users and after authentication it generates a token for you. Do you have any example like that would be much appreciated
That is exactly how the original code works. First you need to create a user account.
Or create a project with the Individual Account option.
Or add Identity manually.
shabbir_215
How can I create this JwtApiUser ?
You used IdentityUser.
My scaffolded DbContext.
namespace JwtApi.Areas.Identity.Data
{
// Add profile data for application users by adding properties to the JwtApiUser class
public class JwtApiUser : IdentityUser
{
}
}
namespace JwtApi.Models
{
public class JwtApiContext : IdentityDbContext<JwtApiUser>
{
public JwtApiContext(DbContextOptions<JwtApiContext> options)
: base(options)
{
}
protected override void OnModelCreating(ModelBuilder builder)
{
base.OnModelCreating(builder);
// Customize the ASP.NET Identity model and override the defaults if needed.
// For example, you can rename the ASP.NET Identity table names and more.
// Add your customizations after calling base.OnModelCreating(builder);
}
}
}
public class IdentityHostingStartup : IHostingStartup
{
public void Configure(IWebHostBuilder builder)
{
builder.ConfigureServices((context, services) => {
services.AddDbContext<JwtApiContext>(options =>
options.UseSqlServer(
context.Configuration.GetConnectionString("JwtApiContextConnection")));
services.AddDefaultIdentity<JwtApiUser>()
.AddEntityFrameworkStores<JwtApiContext>();
});
}
}
[HttpGet]
public async Task<IActionResult> GenerateTokens(string userId,string password)
{
var user = await _userManager.FindByNameAsync(userId);
var result = await _signInManager.CheckPasswordSignInAsync(user, password, false);
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, userId)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(_config["Tokens:Issuer"],
_config["Tokens:Issuer"],
claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds);
return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
}
var user = await _userManager.FindByNameAsync(userId);
In the above code when i pass a username as parameter it return as null :(
In the above code when i pass a username as parameter it return as null :(
Works for me. Did you create an account? Did you verify the account exists? How are you submitting the userId and password? Have you tried using the Visual Studio debugger? Can you explain what troubleshooting steps you are performed up to this point?
Below is my revised code, I missed UserId.ToString it worked :)
[HttpGet]
public async Task<IActionResult> GenerateTokens(string userId,string password)
{
var user = await _userManager.FindByNameAsync(userId.ToString());
if(user != null)
{
var result = await _signInManager.CheckPasswordSignInAsync(user, password, false);
if(result.Succeeded)
{
var claims = new Claim[]
{
new Claim(JwtRegisteredClaimNames.Sub, userId)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(_config["Tokens:Key"]));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(_config["Tokens:Issuer"],
_config["Tokens:Issuer"],
claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds);
return Ok(new { token = new JwtSecurityTokenHandler().WriteToken(token) });
}
}
return BadRequest("Could not create token");
}
My last query, I know you must be irritated with me :)
Instead of passing as Param userID and password can we pass as part of the Body ?
Member
179 Points
721 Posts
.NET CORE API Authentication
Oct 10, 2019 11:52 AM|shabbir_215|LINK
Hi Guys,
I have created an Web API with ASP.NET Identity Authentication with Token, How to create the similar Authentication using .NET Core 2.2 API ?
Much appreciate for your reply
Many Thanks,
Shabbir
All-Star
52971 Points
23574 Posts
Re: .NET CORE API Authentication
Oct 10, 2019 12:52 PM|mgebhard|LINK
Basic JWT example.
appsettings.json
Startup
Controller
Member
179 Points
721 Posts
Re: .NET CORE API Authentication
Oct 11, 2019 08:49 AM|shabbir_215|LINK
Great mgebhard, much appreciated.
Is it possible to provide with complete code where once the token is successfully accepted than you can access the data from any test controller.
I want once user authenticate with the token and username & password it should access the data.
EmployeeController.cs
All-Star
40565 Points
6233 Posts
Microsoft
Re: .NET CORE API Authentication
Oct 11, 2019 09:31 AM|Fei Han - MSFT|LINK
Hi shabbir_215,
After you got a token, to access data/resources, you can make request and pass valid token in HTTP Authorization header.
With Regards,
Fei Han
All-Star
52971 Points
23574 Posts
Re: .NET CORE API Authentication
Oct 11, 2019 11:20 AM|mgebhard|LINK
Add the [Authorize] attribute to the actions or controllers you wish to secure.
Your client application must send the token in the HTTP header when accessing secured resources. You client application is responsible for persisting the token as well. Frankly, it is a bit confusing that you previously created token based security but do not understand how token security works.
Member
179 Points
721 Posts
Re: .NET CORE API Authentication
Oct 14, 2019 09:28 AM|shabbir_215|LINK
Thanks mgebhard much appreciated
When i made a request to the Postman, i get below error
{"type":"https://tools.ietf.org/html/rfc7231#section-6.5.13","title":"Unsupported Media Type","status":415,"traceId":"80000008-0001-fd00-b63f-84710c7967bb"}
Thanks,
Shabbir
All-Star
40565 Points
6233 Posts
Microsoft
Re: .NET CORE API Authentication
Oct 14, 2019 09:37 AM|Fei Han - MSFT|LINK
Hi Shabbir,
Please share the screenshot of the request you sent (with request headers and body details), so that we can understand and troubleshoot the issue better.
With Regards,
Fei Han
Member
179 Points
721 Posts
Re: .NET CORE API Authentication
Oct 14, 2019 10:38 AM|shabbir_215|LINK
Hi Fei,
Below is my complete code
Startup.cs
appsettings.json
TokenController.cs
Usermodel.cs
ApiDbContext.cs
Post : https://localhost:44396/api/token
Header
Body (x-www-form-urlencoded)
Let me know if you require any more information
Thanks,
Shabbir
All-Star
52971 Points
23574 Posts
Re: .NET CORE API Authentication
Oct 14, 2019 10:45 AM|mgebhard|LINK
A 415 error means the content type is unexpected. The example uses Identity to store user accounts. Did you create a user account? Were you able to generated a JWT token? Can you explain the steps that cause the error.
Member
179 Points
721 Posts
Re: .NET CORE API Authentication
Oct 14, 2019 10:53 AM|shabbir_215|LINK
Unfortunately I haven't able to create user account neither JWT token.
I thought of using ASP.NET identity using SQL ASP.NET identity user table for authentication.
All-Star
52971 Points
23574 Posts
Re: .NET CORE API Authentication
Oct 14, 2019 11:21 AM|mgebhard|LINK
You still have not clarified what you're doing. Below is a simplified token generator that does not use Identity.
Example URL
https://localhost:44301/api/token?userId=123
Example result
Member
179 Points
721 Posts
Re: .NET CORE API Authentication
Oct 14, 2019 11:55 AM|shabbir_215|LINK
Perfect, I think i have got it.
I manage to generate the token :)
So my final requirement is before i generate a token it should Authenticate with username and password which can be ASP.NET Identity users and after authentication it generates a token for you. Do you have any example like that would be much appreciated
You're amazing, It's been great help so far
Regards,
Shabbir
All-Star
52971 Points
23574 Posts
Re: .NET CORE API Authentication
Oct 14, 2019 12:05 PM|mgebhard|LINK
That is exactly how the original code works. First you need to create a user account.
Member
179 Points
721 Posts
Re: .NET CORE API Authentication
Oct 14, 2019 01:33 PM|shabbir_215|LINK
How can I create this JwtApiUser ?
All-Star
52971 Points
23574 Posts
Re: .NET CORE API Authentication
Oct 14, 2019 01:42 PM|mgebhard|LINK
Scaffold Identity.
https://docs.microsoft.com/en-us/aspnet/core/security/authentication/scaffold-identity?view=aspnetcore-3.0&tabs=visual-studio
Or create a project with the Individual Account option.
Or add Identity manually.
You used IdentityUser.
My scaffolded DbContext.
Member
179 Points
721 Posts
Re: .NET CORE API Authentication
Oct 14, 2019 03:34 PM|shabbir_215|LINK
Great Thanks for the manual Scaffold identity.
var user = await _userManager.FindByNameAsync(userId);
In the above code when i pass a username as parameter it return as null :(
All-Star
52971 Points
23574 Posts
Re: .NET CORE API Authentication
Oct 14, 2019 05:43 PM|mgebhard|LINK
Works for me. Did you create an account? Did you verify the account exists? How are you submitting the userId and password? Have you tried using the Visual Studio debugger? Can you explain what troubleshooting steps you are performed up to this point?
Member
179 Points
721 Posts
Re: .NET CORE API Authentication
Oct 14, 2019 06:33 PM|shabbir_215|LINK
Sorry works for me as well
Below is my revised code, I missed UserId.ToString it worked :)
My last query, I know you must be irritated with me :)
Instead of passing as Param userID and password can we pass as part of the Body ?
Much Appreciate your help
Kind Regards,
Shabbir
All-Star
52971 Points
23574 Posts
Re: .NET CORE API Authentication
Oct 14, 2019 06:53 PM|mgebhard|LINK
That does not make sense since userId is already defined as a string in the method signature.
Yes, and illustrated above.
//POST: /api/token [HttpPost] public async Task<IActionResult> GenerateToken([FromBody] LoginViewModel model) {
Member
179 Points
721 Posts
Re: .NET CORE API Authentication
Oct 15, 2019 12:38 PM|shabbir_215|LINK
Thanks mgebhard.
How to pass in body using Postman, I am getting same error.
<div> <div> "type": "https://tools.ietf.org/html/rfc7231#section-6.5.13",</div> <div> "title": "Unsupported Media Type",</div> <div> "status": 415,</div> <div> "traceId": "80000039-0005-ff00-b63f-84710c7967bb"</div> <div>}</div> </div>All-Star
52971 Points
23574 Posts
Re: .NET CORE API Authentication
Oct 15, 2019 02:01 PM|mgebhard|LINK
It's very simple, set the request to POST, set the body to "Raw", set the type to JSON, enter JSON format that matches your view model.
IMHO, your best source of information is PostMan support or going through the help file.
https://learning.getpostman.com/docs/postman/sending_api_requests/requests/
Member
179 Points
721 Posts
Re: .NET CORE API Authentication
Oct 15, 2019 02:35 PM|shabbir_215|LINK
Yes just realise that it worked.
I would like to thank you so much for your help with this.
I will close this post now with positive note.
Kind Regards,
Shabbir