i desided to login with steam in asp.net core 2.1 ,
i use AspNet.Security.OpenId.Steam nuget package for connection
,when call sigin method ,client page redirect to steam and after login with steam call back to my server,but not authenticaed request and rejected...
1-in Startup.cs
public void Configure(IApplicationBuilder app, IHostingEnvironment env,IConfiguration configuration,ApplicationDbContext applicationDbContext,ApplicationDbContextBase applicationDbContextBase)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseHsts();
}
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
app.UseCors(option => option.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin());
app.UseStaticFiles();
app.UseAuthentication();
app.UseHttpsRedirection();
AppHttpContext.Configure(app.ApplicationServices.GetRequiredService<IHttpContextAccessor>());
applicationDbContext.MigrateToLastChange();
}
2 - in service.cs
public static IServiceCollection SetupNegatechApi(this IServiceCollection services, IConfiguration configuration)
{
//TODO: add services here...
services.AddMvc()
.AddJsonOptions(options =>
{
options.SerializerSettings.ContractResolver =
new CamelCasePropertyNamesContractResolver();
options.SerializerSettings.ReferenceLoopHandling = ReferenceLoopHandling.Ignore;
});
//Assign User & Role Model And DbContext To Identity
services.AddIdentity<ApplicationIdentityUser, ApplicationIdentityRole>().AddDefaultTokenProviders().AddEntityFrameworkStores<ApplicationDbContextBase>();
//Get Auth Key & Convert To Byte;
var AuthInfo = configuration.GetSection("Auth").Get<AppSettings>();
var SSKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(AuthInfo.SecurityKey));
//Config Identity Password & JWT Config
services.Configure<IdentityOptions>(options =>
{
options.Password.RequiredLength = 6;
options.Password.RequireNonAlphanumeric = false;
options.Password.RequireUppercase = false;
options.Password.RequireLowercase = false;
options.Password.RequireDigit = false;
})
.AddAuthentication(option =>
{
option.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
option.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(option =>
{
option.RequireHttpsMetadata = false;
option.SaveToken = true;
option.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = AuthInfo.Issuer,
ValidAudience = AuthInfo.Audienc,
IssuerSigningKey = SSKey,
ClockSkew = TimeSpan.Zero
};
})
.AddCookie()
.AddSteam(op =>
{
configuration.Bind(op);
op.ClaimsIssuer = AuthInfo.Issuer;
op.SaveTokens = true;
op.CallbackPath = "/api/Steam/SteamCallBack";
op.RequireHttpsMetadata = false;
});
services.Configure<IISOptions>(op => op.AutomaticAuthentication = false);
//Register Configuration For Dependncy Injection
services.AddSingleton<IConfiguration>(configuration);
services.AddSingleton<IFileProvider>(new PhysicalFileProvider(Path.Combine(Directory.GetCurrentDirectory(), "wwwroot/$gallery")));
return services;
}
3-in Controller
[ApiController]
[ApiExplorerSettings(GroupName = "public")]
[Route("api/[controller]/[action]")]
public class SteamController : BaseController
{
[HttpPost]
public async Task<IActionResult> Signin()
{
var auth = new AuthenticationProperties { RedirectUri = "/api/Steam/SteamCallBack" };
return Challenge(auth,"Steam" );
}
[HttpGet]
public IActionResult SteamCallBack(string state,openid openid)
{
//breack point
return Redirect("http://localhost:3000/profile?id=" + "test");
}
}
public class openid
{
public string claimed_id { get; set; }
public string identity { get; set; }
public string return_to { get; set; }
public string response_nonce { get; set; }
public string assoc_handle { get; set; }
public string signed { get; set; }
public string sig { get; set; }
}
4-in html file
<form id="steam_form" action="https://localhost:44315/api/Steam/Signin" method="post">
//Submit Login form to api server
<button type="submit"> Login</button>
</form>
5- result error after call back http://s8.picofile.com/file/8365103326/Untitled.png
I don't know why, but AddSteam option is above the OpenID rules.
If you look closer, then you can see that Steams OpenId is only name and some random standards.
Check your form with change endpoint to your.address/signin and make a post form:
<form id="steamAuth" action="https://localhost:44315/signin" method="post">
<input type='hidden' name='Provider' value='Steam'>
<input type = 'hidden' name='ReturnUrl' value='your.address/returnurl'></form>
<button type="submit"> Login</button>
</form>
Im not sure, but i think that .AddSteam() option not including any settings added in services configuration.
If you check repo of this library you can see examples, and here its just AddSteam(), when other providers are described:
services.AddAuthentication(options => { /* Authentication options */ })
.AddSteam()
.AddOpenId("StackExchange", "StackExchange", options =>
{
options.Authority = new Uri("https://openid.stackexchange.com/");
options.CallbackPath = "/signin-stackexchange";
});
Related
I am trying to add authorization and and authentication to an ASP.NET entity framework program. I'm new to it and it's doing my head in.
I originally had a Context class that inherited from DbContext. Now that I'm trying to add security I have to inherit from IdentityDbContext but when I do this I get an exception in my Program.cs file on the line context.Database.EnsureCreated();
My DbContext which is SchoolContext.cs
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using AdvisementApp.Models;
using Microsoft.AspNetCore.Identity.EntityFrameworkCore;
namespace AdvisementApp.Data
{
public class SchoolContext : IdentityDbContext //Inheriting
{
public SchoolContext (DbContextOptions<SchoolContext> options)
: base(options)
{
}
public DbSet<AdvisementApp.Models.Student> Students { get; set; } = default!;
public DbSet<Advisor> Advisors { get; set; }
public DbSet<Major> Majors { get; set; }
public DbSet<Course> Courses { get; set; }
public DbSet<StudentCourse> StudentCourses { get; set; }
public DbSet<MajorCourse> MajorCourses { get; set; }
protected override void OnModelCreating(ModelBuilder modelBuilder)
{
modelBuilder.Entity<Student>().ToTable("Student");
modelBuilder.Entity<Course>().ToTable("Course");
modelBuilder.Entity<StudentCourse>().ToTable("StudentCourse");
modelBuilder.Entity<Major>().ToTable("Major");
modelBuilder.Entity<Advisor>().ToTable("Advisor");
modelBuilder.Entity<MajorCourse>().ToTable("MajorCourse");
modelBuilder.Entity<Advisor>()
.HasMany(a => a.Students)
.WithOne(s => s.Advisor)
.HasForeignKey(s => s.AdvisorId);
}
}
}
Program.cs
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.DependencyInjection;
using AdvisementApp.Data;
using Microsoft.AspNetCore.Identity;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
builder.Services.AddRazorPages();
builder.Services.AddDbContext<SchoolContext>(options =>
options.UseSqlite(builder.Configuration.GetConnectionString("SchoolContext") ?? throw new InvalidOperationException("Connection string 'SchoolContext' not found.")));
builder.Services.AddIdentity<IdentityUser, IdentityRole>().AddEntityFrameworkStores<SchoolContext>();
builder.Services.AddDatabaseDeveloperPageExceptionFilter();
builder.Services.AddControllersWithViews();
builder.Services.Configure<IdentityOptions>(options =>
{
// Password settings.
options.Password.RequireDigit = true;
options.Password.RequireLowercase = true;
options.Password.RequireNonAlphanumeric = true;
options.Password.RequireUppercase = true;
options.Password.RequiredLength = 6;
options.Password.RequiredUniqueChars = 1;
// Lockout settings.
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(5);
options.Lockout.MaxFailedAccessAttempts = 5;
options.Lockout.AllowedForNewUsers = true;
// User settings.
options.User.AllowedUserNameCharacters =
"abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789-._#+";
options.User.RequireUniqueEmail = false;
});
builder.Services.ConfigureApplicationCookie(options =>
{
// Cookie settings
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(5);
options.LoginPath = "/Identity/Account/Login";
options.AccessDeniedPath = "/Identity/Account/AccessDenied";
options.SlidingExpiration = true;
});
var app = builder.Build();
// Configure the HTTP request pipeline.
if (!app.Environment.IsDevelopment())
{
app.UseExceptionHandler("/Error");
// The default HSTS value is 30 days. You may want to change this for production scenarios, see https://aka.ms/aspnetcore-hsts.
app.UseHsts();
}
else
{
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
using (var scope = app.Services.CreateScope())
{
var services = scope.ServiceProvider;
var context = services.GetRequiredService<SchoolContext>();
context.Database.EnsureCreated();//This is where I am getting the error.
DbInitializer.Initialize(context);
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
app.UseAuthentication();
//app.UseAuthorization();
app.MapRazorPages();
app.Run();
This is the error:
Am I intending to do something incorrectly?
I'm using ASP.NET Core to create a REST API. If I use the [APIController] class attribute, the method that uses the POST method with complex parameters always gets null value even though using/without using [FromBody] (I'm using Postman, Raw Json). But if I omit the [APIController] attribute, the parameters on the POST method work normally. I'm using ASP.NET Core 6. What is the effect without using the [APIController] attribute?
Program.cs
var builder = WebApplication.CreateBuilder(args);
builder.Services.Configure<Microsoft.AspNetCore.Server.Kestrel.Core.KestrelServerOptions>(options =>
{
options.AllowSynchronousIO = true;
});
builder.Services.Configure<IISServerOptions>(options =>
{
options.AllowSynchronousIO = true;
});
// Add services to the container.
builder.Services.AddMvc(option =>
{
option.AllowEmptyInputInBodyModelBinding = true;
option.EnableEndpointRouting = true;
option.FormatterMappings.SetMediaTypeMappingForFormat("json", Microsoft.Net.Http.Headers.MediaTypeHeaderValue.Parse("application/json"));
}).AddNewtonsoftJson(opt => {
opt.SerializerSettings.DateFormatString = "dd/MM/yyyy HH:mm:ss";
}).AddJsonOptions(options => {
options.JsonSerializerOptions.PropertyNameCaseInsensitive = true;
options.JsonSerializerOptions.PropertyNamingPolicy = null;
});
builder.Services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddAuthentication();
builder.Services.AddAuthorization();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
app.Run();
Model:
public class BillRequest
{
public string? CompCode { get; set; } = String.Empty;
public string? CustNo { get; set; } = String.Empty;
public string? ID { get; set; } = String.Empty;
public string? Type { get; set; } = String.Empty;
public string? TransDate { get; set; } = String.Empty;
public string? Remark { get; set; } = String.Empty;
public BillRequest()
{
}
}
Controller:
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Authorization;
namespace FI.Controllers;
[ApiController]
[Route("[Controller]")]
public class VAController : ControllerBase
{
private readonly ILogger<VAController> _logger;
public VAController(ILogger<VAController> logger)
{
_logger = logger;
}
[HttpPost]
//[Authorize(AuthenticationSchemes = VAAuthSchemeConstants.VAAuthScheme)]
[Route("[Action]")]
public BillRequest Bills(BillRequest billReq)
{
try
{
if (ModelState.IsValid)
{
return new BillRequest
{
CompanyCode = "Test - success"
};
}
else
{
return new BillRequest()
{
CompanyCode = "Test - not valid model"
};
}
}
catch(Exception ex)
{
return new BillRequest()
{
CompCode = "Test - error"
};
}
}
}
Postman Payload (Postman Body (Raw, Json)):
{
"CompCode": "Test Comp"
"CustNo": "1235",
"ID": "123123123",
"Type": "01",
"TransDate": "Test Date",
"Remark": "My Remark"
}
Even though I changed the parameter using a string the parameter value is still null.
Been trying to get email sending to work in Blazor to allow users to confirm emails and reset passwords. I have followed the documentation https://learn.microsoft.com/en-us/aspnet/core/security/authentication/accconfirm?view=aspnetcore-3.1&tabs=visual-studio and looked over it over and over but I can't seem to figure out why the email does not send.
API Key has been set to Full Access
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddDefaultIdentity<ApplicationUser>(options => options.SignIn.RequireConfirmedAccount = true)
.AddRoles<IdentityRole>()
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options => {
options.IdentityResources["openid"].UserClaims.Add("role");
options.ApiResources.Single().UserClaims.Add("role");
});
// Need to do this as it maps "role" to ClaimTypes.Role and causes issues
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Remove("role");
services.AddAuthentication()
.AddIdentityServerJwt();
services.AddControllersWithViews();
services.AddTransient<IEmailSender, EmailSender>();
services.Configure<AuthMessageSenderOptions>(Configuration);
services.AddRazorPages();
services.Configure<IdentityOptions>(options =>
options.ClaimsIdentity.UserIdClaimType = ClaimTypes.NameIdentifier);
services.AddControllers().AddNewtonsoftJson(x => x.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Serialize);
}
public class AuthMessageSenderOptions
{
public string SendGridKey { get; set; }
}
Email sender
public class EmailSender : IEmailSender
{
public EmailSender(IOptions<AuthMessageSenderOptions> optionsAccessor)
{
Options = optionsAccessor.Value;
}
public AuthMessageSenderOptions Options { get; }
public Task SendEmailAsync(string email, string subject, string message)
{
return Execute(Options.SendGridKey, subject, message, email);
}
public Task Execute(string apiKey, string subject, string message, string email)
{
var client = new SendGridClient(apiKey);
var msg = new SendGridMessage()
{
From = new EmailAddress("info#example.com", "Identity Demo"),
Subject = subject,
PlainTextContent = message,
HtmlContent = message
};
msg.AddTo(new EmailAddress(email));
// Disable click tracking.
// See https://sendgrid.com/docs/User_Guide/Settings/tracking.html
msg.SetClickTracking(false, false);
return client.SendEmailAsync(msg);
}
}
RegisterConfirmation.cshtml:
DisplayConfirmAccountLink = false;
Can't get any detail about the user after he logged in although client side have the AspNetCore.Identity.Application token. Seem like he isn't authenticated although the cookies are set correctly. I searched a lot of question and didn't manage to solve it yet.
Startup.cs:
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<ApplicationDbContext>(options =>
options.UseSqlServer(
Configuration.GetConnectionString("DefaultConnection")));
services.AddDatabaseDeveloperPageExceptionFilter();
services.AddDefaultIdentity<ApplicationUser>(options => {
options.Password.RequireDigit = true;
options.Password.RequiredLength = 8;
options.Password.RequireUppercase = true;
options.Password.RequireNonAlphanumeric = true;
options.SignIn.RequireConfirmedAccount = false;
options.SignIn.RequireConfirmedEmail = false;
options.SignIn.RequireConfirmedEmail = false;
}
/*options => options.SignIn.RequireConfirmedAccount = true*/)
.AddEntityFrameworkStores<ApplicationDbContext>();
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>();
services.AddAuthentication()
.AddIdentityServerJwt();
services.AddControllersWithViews();
services.AddRazorPages();
// In production, the Angular files will be served from this directory
services.AddSpaStaticFiles(configuration => {
configuration.RootPath = "ClientApp/dist";
});
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.ConfigureApplicationCookie(options => {
// Cookie settings
options.Cookie.HttpOnly = false;
//options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
//options.LoginPath = "/Account/Login";
//options.AccessDeniedPath = "/Identity/Account/AccessDenied";
//options.SlidingExpiration = true;
});
//services.AddIdentity<ApplicationUser, IdentityRole>(config => {
// config.SignIn.RequireConfirmedEmail = false;
//});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env) {
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
app.UseMigrationsEndPoint();
}
else {
app.UseExceptionHandler("/Error");
// 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();
if (!env.IsDevelopment()) {
app.UseSpaStaticFiles();
}
app.UseRouting();
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints => {
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
endpoints.MapRazorPages();
});
app.UseSpa(spa => {
// To learn more about options for serving an Angular SPA from ASP.NET Core,
// see https://go.microsoft.com/fwlink/?linkid=864501
spa.Options.SourcePath = "ClientApp";
if (env.IsDevelopment()) {
spa.UseAngularCliServer(npmScript: "start");
}
});
}
}
IdentityHostingStartup.cs:
public class IdentityHostingStartup : IHostingStartup
{
public void Configure(IWebHostBuilder builder)
{
builder.ConfigureServices((context, services) => {
});
}
}
Relavant controller:
private SignInManager<ApplicationUser> _signManager; //User
private UserManager<ApplicationUser> _userManager;
public AccountController(UserManager<ApplicationUser> userManager, SignInManager<ApplicationUser> signManager) {
_userManager = userManager;
_signManager = signManager;
}
// GET: AccountController
public ActionResult Index() {
return View();
}
// GET: AccountController/Details/5
public ActionResult Details(int id) {
return View();
}
// GET: AccountController/Create
public ActionResult Create() {
return View();
}
// POST: AccountController/Create
[HttpPost]
public async Task<JsonResult> Create([FromBody] RegistrtionData registrtionData) {
if (registrtionData != null) {
var user = new ApplicationUser { UserName = registrtionData.name, Email = registrtionData.email };
var result = await _userManager.CreateAsync(user, registrtionData.password);
return Json(result);
}
return Json("null data");
}
[HttpPost]
//[ValidateAntiForgeryToken]
public async Task<ActionResult> Login([FromBody] LoginData loginData) {
if (loginData != null) {
var result = await _signManager.PasswordSignInAsync(loginData.userName,
loginData.password, true, false);
return Ok("login result" + result);
}
return Ok("got null data as login data");
}
[HttpPost]
public async Task<JsonResult> Logout() {
try {
await _signManager.SignOutAsync();
return Json(true);
} catch (Exception e) {
return Json(false);
}
}
ApplicationUser.cs:
public class ApplicationUser : IdentityUser {
//public string password { get; set; }
//public string name { get; set; }
//public string familyName { get; set; }
//public int age { get; set; }
}
Client Side:
nSubmit(formData: NgForm) {
this.serverService.login(formData.form.value).subscribe(result => {
if (result.succeeded) {
this.loginMeassage = ''
this.router.navigate(['/heroScreen'])
}
else {
this.loginMeassage = 'ERROR, password or username incorrect!!'
}
}, error => console.error('error', error));
Network request, response cookie (jwt) ontop, and the req cookie down (it's suppose to change every communication??):
User after more then 1 request:
OK, so after wasting probably 15 hours on this..
Turns out u need to set DefaultAuthenticateScheme and DefaultChallengeScheme.
So instead of:
services.AddAuthentication().AddIdentityServerJwt();
Do this:
services.AddAuthentication(options => {
options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
}).AddIdentityServerJwt();
Also, possible unrelated walkaround (but kinda ugly):
var cookie = await HttpContext.AuthenticateAsync(IdentityConstants.ApplicationScheme);
var user = await _userManager.GetUserAsync(cookie.Principal);
hope it's will help somebody someday.
I just setup a basic integration to Identity Framework Core 2.0.1 in my app. I've configured IdentityUser to use INT instead of STRING as its primary key as per this article (https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity-primary-key-configuration?tabs=aspnetcore2x).
I'm using code first with a blank database created via an initial migration.
The problem I'm running into is that when I create a user (I'm using postman), I get back the following exception:
Cannot insert explicit value for identity column in table 'AspNetUsers' when IDENTITY_INSERT is set to OFF.
I checked the value of appUser.Id when it is passed to UserManager.CreateAsync and it is definitely = 0 (which is the CLR default value for an Int property)
If I configure AppUser to use a String as a primary key (the default settings), the Id column is not set as an identity column and I get no error - the user is created without a problem with a GUID as the ID generated internally by the Identity framework.
From postman:
{
"email":"someemail#comcast.net",
"password":"Password123",
"firstName":"Alex",
"lastName":"Florin",
"userName":"aflorin"
}
AppUser.cs
using Microsoft.AspNetCore.Identity;
namespace Driver.Models.Entities
{
public class AppUser : IdentityUser<int>
{
public string FirstName { get; set; }
public string LastName { get; set; }
}
}
AppRole.cs
using Microsoft.AspNetCore.Identity;
namespace Driver.Models.Entities
{
public class AppRole : IdentityRole<int> {
}
}
IAppUserRepository.cs
using System.Threading.Tasks;
namespace Driver.Repositories
{
public interface IAppUserRepository<AppUser>
{
Task Create(AppUser appUser, string password);
}
}
AppUserRepository.cs
using Driver.DBContext;
using Driver.Models.Entities;
using Microsoft.AspNetCore.Identity;
using System.Threading.Tasks;
namespace Driver.Repositories
{
public class AppUserRepository : IAppUserRepository<AppUser>
{
private UserManager<AppUser> _userManager;
protected DriverDbContext _dbContext;
public AppUserRepository(UserManager<AppUser> userManager, DriverDbContext dbContext)
{
_userManager = userManager;
_dbContext = dbContext;
}
public async Task Create(AppUser appUser, string password)
{
var result = await _userManager.CreateAsync(appUser, password);
//ToDo: if (!result.Succeeded) { }
await _dbContext.SaveChangesAsync();
}
}
}
AccountsController.cs
using Driver.Models.Entities;
using Driver.ViewModels.Identity;
using AutoMapper;
using Microsoft.AspNetCore.Mvc;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Identity;
using Driver.Repositories;
namespace Driver.Controllers
{
[Produces("application/json")]
[Route("api/[controller]")]
public class AccountsController : Controller
{
private IAppUserRepository<AppUser> _appUserRepository;
public AccountsController(UserManager<AppUser> userManager, IAppUserRepository<AppUser> appUserRepository)
{
_appUserRepository = appUserRepository;
}
[HttpPost]
public async Task<IActionResult> Create([FromBody]RegistrationViewModel registrationVM)
{
if (registrationVM == null)
{
return BadRequest();
}
var appUser = Mapper.Map<AppUser>(registrationVM);
await _appUserRepository.Create(appUser, registrationVM.Password);
return CreatedAtAction("Create", new { id = appUser.Id }, Mapper.Map<RegistrationViewModel>(appUser));
}
}
}
RegistrationViewModel.cs
namespace Driver.ViewModels.Identity
{
public class RegistrationViewModel
{
public string Email { get; set; }
public string Password { get; set; }
public string FirstName { get; set; }
public string LastName { get; set; }
public string UserName { get; set; }
}
}
ConfigureServices from Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DriverDbContext>(options => options.UseSqlServer(_config.GetConnectionString("DriverDBConnection")));
services.AddSingleton<IJwtFactory, JwtFactory>();
services.TryAddTransient<IHttpContextAccessor, HttpContextAccessor>();
services.AddIdentity<AppUser, IdentityRole<int>>()
.AddEntityFrameworkStores<DriverDbContext>()
.AddDefaultTokenProviders();
var settings = _config.GetSection("Authentication").Get<AuthenticationAppSettings>();
// Configure JwtIssuerOptions
services.Configure((Models.JwtIssuerOptions options) =>
{
options.Issuer = settings.JwtIssuerOptions.Issuer;
options.Audience = settings.JwtIssuerOptions.Audience;
options.SigningCredentials = new SigningCredentials(_signingKey, SecurityAlgorithms.HmacSha256);
});
// Specify the validation parameters to dictate how we want received tokens validated
var tokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidIssuer = settings.JwtIssuerOptions.Issuer,
ValidateAudience = true,
ValidAudience = settings.JwtIssuerOptions.Audience,
ValidateIssuerSigningKey = true,
IssuerSigningKey = _signingKey,
RequireExpirationTime = false,
ValidateLifetime = true,
ClockSkew = TimeSpan.Zero
};
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(configureOptions =>
{
configureOptions.ClaimsIssuer = settings.JwtIssuerOptions.Issuer;
configureOptions.TokenValidationParameters = tokenValidationParameters;
configureOptions.SaveToken = true;
});
// Create an authorization claim policy to guard API controllers and actions
services.AddAuthorization(options =>
{
options.AddPolicy("ApiUser", policy => policy.RequireClaim(Constants.Strings.JwtClaimIdentifiers.Rol, Constants.Strings.JwtClaims.ApiAccess));
});
services.Configure<IdentityOptions>(options =>
{
// Password settings
options.Password.RequireDigit = settings.Password.RequiredDigit;
options.Password.RequiredLength = settings.Password.RequiredLength;
options.Password.RequireNonAlphanumeric = settings.Password.RequireNonAlphanumeric;
options.Password.RequireUppercase = settings.Password.RequireUppercase;
options.Password.RequireLowercase = settings.Password.RequireLowercase;
options.Password.RequiredUniqueChars = settings.Password.RequiredUniqueChars;
// Lockout settings
options.Lockout.DefaultLockoutTimeSpan = TimeSpan.FromMinutes(settings.Lockout.DefaultLockoutTimeSpan);
options.Lockout.MaxFailedAccessAttempts = settings.Lockout.MaxFailedAccessAttempts;
options.Lockout.AllowedForNewUsers = settings.Lockout.AllowedForNewUsers;
// User settings
options.User.RequireUniqueEmail = settings.User.RequireUniqueEmail;
});
services.AddMvc().AddJsonOptions(
options => options.SerializerSettings.ReferenceLoopHandling = Newtonsoft.Json.ReferenceLoopHandling.Ignore
);
services.AddAutoMapper(typeof(Startup));
services.AddScoped<ICompanyRepository, CompanyRepository>();
services.AddScoped<ILookupRepository, LookupRepository>();
services.AddScoped<IManagerRepository, ManagerRepository>();
services.AddScoped<IAppUserRepository<AppUser>, AppUserRepository>();
}
The class declaration line for my DbContext (I'm not doing any custom configuration on the AppUser entity):
public class DriverDbContext : IdentityDbContext<AppUser, AppRole, int>