Is there anyone who can offer me a solution to this error? I can't understand where I'm wrong in configuring IdentityServer4 - asp.net-core

The image is here for Postman Request
It's a problem that I can't understand in the end, which is that it always gets a {"error": "invalid_request"}.
I request from Postman, using ImplicitFlow and OAuth 2.0, but I always get the same error.
I attach below the settings I made for IdentityServer4. I hope it will be useful to someone and can give me a solution.
public static class Config
{
public static IEnumerable<ApiResource> ApiResources()
{
return new[] {
new ApiResource("SkyEye.API", "SkyEye.API")
};
}
public static IEnumerable<Client> GetClients()
{
return new List<Client>
{
new Client
{
ClientId="ReactClient",
ClientName="SkyEye.SPA",
AllowedGrantTypes = GrantTypes.Implicit,
AllowAccessTokensViaBrowser = true,
RedirectUris = new List<string>
{
"https://localhost:5002",
},
PostLogoutRedirectUris = new []{
"https://localhost:5002"
},
AllowedScopes= {
// IdentityServerConstants.StandardScopes.Profile,
// IdentityServerConstants.StandardScopes.OpenId,
"SkyEye.API"
}
}
};
}
public static IEnumerable<TestUser> Users()
{
return new[]
{
new TestUser
{ SubjectId = "1",
Username = "lascodaniil",
Password="password"
}
};
}
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddIdentityServer()
.AddDeveloperSigningCredential()
.AddTestUsers(Config.Users().ToList())
.AddInMemoryClients(Config.GetClients())
.AddInMemoryApiResources(Config.ApiResources());
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseIdentityServer();
app.UseRouting();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}

I think your redirect URL is not valid. The URL in postman is different than the one provided in IDs configuration. The URL should be an exact match
Try changing this
RedirectUris = new List<string>
{
"https://localhost:5002",
},
to this
RedirectUris = new List<string>
{
"https://localhost:5002/connect/token",
},
Hope it helps

Related

Certificate Authentication events not triggered in ASP .NET Core 3.1 API

I want to implement certificate authentication on my .NET Core 3.1 API. I followed the steps outlined by Microsoft here:https://learn.microsoft.com/en-us/aspnet/core/security/authentication/certauth?view=aspnetcore-5.0
But, I don't think the events "OnCertificateValidated" and "OnAuthenticationFailed" even fire. Breakpoints are never hit, and I even tried adding code that should break in there just to test, but it never fails (most likely because it never reaches there)
I'm trying to validate the Client Certificate CN and I want to only allow certain CNs to be able to call my API.
You can find my code below. What am I doing wrong?
Startup
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(
CertificateAuthenticationDefaults.AuthenticationScheme)
.AddCertificate(options =>
{
options.AllowedCertificateTypes = CertificateTypes.All;
options.Events = new CertificateAuthenticationEvents
{
OnCertificateValidated = context =>
{
var validationService =
context.HttpContext.RequestServices.GetRequiredService<ICertificateValidationService>();
if (validationService.ValidateCertificate(context.ClientCertificate))
{
context.Success();
}
else
{
context.Fail($"Unrecognized client certificate: {context.ClientCertificate.GetNameInfo(X509NameType.SimpleName, false)}");
}
int test = Convert.ToInt32("test");
return Task.CompletedTask;
},
OnAuthenticationFailed = context =>
{
int test = Convert.ToInt32("test");
context.Fail($"Invalid certificate");
return Task.CompletedTask;
}
};
});
services.AddHealthChecks();
services.AddControllers(setupAction =>
{
setupAction.Filters.Add(new ProducesResponseTypeAttribute(StatusCodes.Status406NotAcceptable));
setupAction.Filters.Add(new ProducesResponseTypeAttribute(StatusCodes.Status500InternalServerError));
setupAction.ReturnHttpNotAcceptable = true;
}).AddXmlDataContractSerializerFormatters();
services.AddScoped<IJobServiceRepository, JobServiceRepository>();
services.AddScoped<ICaptureServiceRepository, CaptureServiceRepository>();
services.Configure<KTAEndpointConfig>(Configuration.GetSection("KTAEndpointConfig"));
services.AddAutoMapper(AppDomain.CurrentDomain.GetAssemblies());
services.AddVersionedApiExplorer(setupAction =>
{
setupAction.GroupNameFormat = "'v'VV";
});
services.AddApiVersioning(setupAction =>
{
setupAction.AssumeDefaultVersionWhenUnspecified = true;
setupAction.DefaultApiVersion = new ApiVersion(1, 0);
setupAction.ReportApiVersions = true;
});
var apiVersionDecriptionProvider = services.BuildServiceProvider().GetService<IApiVersionDescriptionProvider>();
services.AddSwaggerGen(setupAction =>
{
foreach (var description in apiVersionDecriptionProvider.ApiVersionDescriptions)
{
setupAction.SwaggerDoc($"TotalAgilityOpenAPISpecification{description.GroupName}", new Microsoft.OpenApi.Models.OpenApiInfo()
{
Title = "TotalAgility API",
Version = description.ApiVersion.ToString(),
Description = "Kofax TotalAgility wrapper API to allow creating KTA jobs and uploading documents",
Contact = new Microsoft.OpenApi.Models.OpenApiContact()
{
Email = "shivam.sharma#rbc.com",
Name = "Shivam Sharma"
}
});
setupAction.OperationFilter<AddRequiredHeaderParameter>();
var xmlCommentsFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlCommentsFullPath = Path.Combine(AppContext.BaseDirectory, xmlCommentsFile);
setupAction.IncludeXmlComments(xmlCommentsFullPath);
}
setupAction.DocInclusionPredicate((documentName, apiDescription) =>
{
var actionApiVersionModel = apiDescription.ActionDescriptor
.GetApiVersionModel(ApiVersionMapping.Explicit | ApiVersionMapping.Implicit);
if (actionApiVersionModel == null)
{
return true;
}
if (actionApiVersionModel.DeclaredApiVersions.Any())
{
return actionApiVersionModel.DeclaredApiVersions.Any(v =>
$"TotalAgilityOpenAPISpecificationv{v.ToString()}" == documentName);
}
return actionApiVersionModel.ImplementedApiVersions.Any(v =>
$"TotalAgilityOpenAPISpecificationv{v.ToString()}" == documentName);
});
});
services.AddHttpsRedirection((httpsOpts) =>
{
//httpsOpts.HttpsPort = 443;
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IApiVersionDescriptionProvider apiVersionDescriptionProvider)
{
app.UseApiExceptionHandler();
app.UseHeaderLogContextMiddleware();
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseSwagger(setupAction =>
{
setupAction.SerializeAsV2 = true;
});
app.UseSwaggerUI(setupAction =>
{
foreach (var decription in apiVersionDescriptionProvider.ApiVersionDescriptions)
{
setupAction.SwaggerEndpoint($"/swagger/TotalAgilityOpenAPISpecification{decription.GroupName}/swagger.json",
decription.GroupName.ToUpperInvariant());
}
//setupAction.SwaggerEndpoint("/swagger/TotalAgilityOpenAPISpecification/swagger.json",
//"TotalAgility API");
setupAction.RoutePrefix = "";
});
app.UseSerilogRequestLogging();
app.UseHeaderValidation();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers().RequireAuthorization();
endpoints.MapHealthChecks("/health");
});
}
}
CertificateValidationService
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Security.Cryptography.X509Certificates;
using System.Threading.Tasks;
namespace TotalAgility_API.Services
{
public class CertificateValidationService : ICertificateValidationService
{
private readonly IConfiguration _configuration;
private readonly ILogger<CertificateValidationService> _logger;
public CertificateValidationService(IConfiguration configuration, ILogger<CertificateValidationService> logger)
{
_logger = logger;
_configuration = configuration;
}
public bool ValidateCertificate(X509Certificate2 clientCert)
{
List<string> allowedCNs = new List<string>();
_configuration.GetSection("AllowedClientCertCNList").Bind(allowedCNs);
string cn = clientCert.GetNameInfo(X509NameType.SimpleName, false);
if (allowedCNs.Contains(cn))
{
return true;
}
else
{
_logger.LogWarning("Invalid Cert CN: {CN}", cn);
return false;
}
}
}
}
appsettings.json
{
"Serilog": {
"MinimumLevel": {
"Default": "Information",
"Override": {
"Microsoft": "Warning",
"System": "Warning"
}
}
},
"AllowedHosts": "*",
"AllowedClientCertCNList": [
"1",
"2",
"3",
"4"
]
}
Any help would be appreciated here. I'm new to this and I'm don't know how to proceed.
I had a similar issue on IIS Express where only "OnChallenge" fired but not "OnCertificateValidated" or "OnAuthenticationFailed", and kept getting 403 status responses.
This answer from a different question solved it for my case (IIS Express). Set the access sslFlags in .vs\config\applicationhost.config to use client certificate:
<access sslFlags="Ssl, SslNegotiateCert, SslRequireCert" />

Use SwaggerUI to authenticate against IdentiyServer 4 with own protected API Endpoints

I'm relatively new to IdentityServer4 but I went through the docs and managed to set it up.
In my scenario I want to use IdentityServer4 and also protect additional endpoints within the identity server. I followed the documentation to use client credentials as described here. Instead of using a separate API I followed the docs here to protect the API endpoints within the identity server which works as expected.
I also want to use Swagger to provide documentation for these additional endpoints. But unfortunately I don't know how to properly setup the configuration. The "Authorize" button appears and I can enter the client and secret and also login which works fine but whenever I try to execute the action with Swagger I get a 401 Unauthorized error back.
My sample project looks like this:
Program.cs
public class Program
{
public static int Main(string[] args)
{
Log.Logger = new LoggerConfiguration()
.MinimumLevel.Debug()
.MinimumLevel.Override("Microsoft", LogEventLevel.Warning)
.MinimumLevel.Override("Microsoft.Hosting.Lifetime", LogEventLevel.Information)
.MinimumLevel.Override("System", LogEventLevel.Warning)
.MinimumLevel.Override("Microsoft.AspNetCore.Authentication", LogEventLevel.Information)
.Enrich.FromLogContext()
// uncomment to write to Azure diagnostics stream
//.WriteTo.File(
// #"D:\home\LogFiles\Application\identityserver.txt",
// fileSizeLimitBytes: 1_000_000,
// rollOnFileSizeLimit: true,
// shared: true,
// flushToDiskInterval: TimeSpan.FromSeconds(1))
.WriteTo.Console(outputTemplate: "[{Timestamp:HH:mm:ss} {Level}] {SourceContext}{NewLine}{Message:lj}{NewLine}{Exception}{NewLine}", theme: AnsiConsoleTheme.Code)
.CreateLogger();
try
{
Log.Information("Starting host...");
CreateHostBuilder(args).Build().Run();
return 0;
}
catch (Exception ex)
{
Log.Fatal(ex, "Host terminated unexpectedly.");
return 1;
}
finally
{
Log.CloseAndFlush();
}
}
public static IHostBuilder CreateHostBuilder(string[] args) =>
Host.CreateDefaultBuilder(args)
.UseSerilog()
.ConfigureWebHostDefaults(webBuilder =>
{
webBuilder.UseStartup<Startup>();
});
}
Startup.cs
public class Startup
{
public IWebHostEnvironment Environment { get; }
public Startup(IWebHostEnvironment environment)
{
Environment = environment;
}
public void ConfigureServices(IServiceCollection services)
{
services
.AddControllers();
services
.AddIdentityServer(options =>
{
// see https://identityserver4.readthedocs.io/en/latest/topics/resources.html
options.EmitStaticAudienceClaim = true;
})
.AddDeveloperSigningCredential()
.AddInMemoryIdentityResources(Config.IdentityResources)
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients);
services.AddLocalApiAuthentication();
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new OpenApiInfo {Title = "Protected API", Version = "v1"});
options.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
In = ParameterLocation.Header,
Flows = new OpenApiOAuthFlows
{
ClientCredentials = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri("https://localhost:5001/connect/authorize"),
TokenUrl = new Uri("https://localhost:5001/connect/token"),
Scopes = new Dictionary<string, string>
{
{"IdentityServerApi", "Demo API - full access"}
}
}
}
});
});
}
public void Configure(IApplicationBuilder app)
{
if (Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseRouting();
app.UseIdentityServer();
app.UseAuthentication();
app.UseAuthorization();
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "My API V1");
options.OAuthClientId("client");
options.OAuthAppName("Demo API - Swagger");
options.OAuthUsePkce();
});
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers()
.RequireAuthorization(IdentityServerConstants.LocalApi.PolicyName); // auth attribute for all controllers!
});
}
}
Config.cs
public static class Config
{
public static IEnumerable<IdentityResource> IdentityResources =>
new IdentityResource[]
{
new IdentityResources.OpenId()
};
public static IEnumerable<ApiScope> ApiScopes =>
new List<ApiScope>
{
new ApiScope(IdentityServerConstants.LocalApi.ScopeName, "My API"),
};
public static IEnumerable<Client> Clients =>
new List<Client>
{
new Client
{
ClientId = "client",
// no interactive user, use the clientid/secret for authentication
AllowedGrantTypes = GrantTypes.ClientCredentials,
// secret for authentication
ClientSecrets =
{
new Secret("secret".Sha256())
},
// scopes that client has access to
AllowedScopes = { IdentityServerConstants.LocalApi.ScopeName }
}
};
}
LocalApiController.cs
[ApiController]
[Route("localApi")]
public class LocalApiController : ControllerBase
{
[HttpGet]
public ActionResult<string> Get()
{
return Ok("ok");
}
}
I forgot to add authentication information to the swagger documentation.
After adding the following filter and registering it like described here everything works
public class AuthorizeCheckOperationFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
var hasAuthorize =
context.MethodInfo.DeclaringType.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any()
|| context.MethodInfo.GetCustomAttributes(true).OfType<AuthorizeAttribute>().Any();
if (hasAuthorize)
{
operation.Responses.Add("401", new OpenApiResponse { Description = "Unauthorized" });
operation.Responses.Add("403", new OpenApiResponse { Description = "Forbidden" });
operation.Security = new List<OpenApiSecurityRequirement>
{
new OpenApiSecurityRequirement
{
[
new OpenApiSecurityScheme {Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "oauth2"}
}
] = new[] {"api1"}
}
};
}
}
}

How to delete cookie on browser close

I'm implementing asp.net core project and I authenticate the user via ldap by using Novell.Directory.Ldap.NETStandard. Now my problem is How can I delete cookie after the user close the browser. I want whenever the user close the browser and opens the system again, he confronts with login page. here below is what I've tried in startup for setting cookie:
services.AddScoped<Ldap.IAuthenticationService, LdapAuthenticationService>();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
{
// Cookie settings
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(15);
options.LoginPath = "/Account/Login";
options.SlidingExpiration = true;
});
And here is my AccountController:
public class AccountController : Controller {
private readonly Ldap.IAuthenticationService _authenticationService;
private readonly IHttpContextAccessor _httpContext;
public AccountController(Ldap.IAuthenticationService authenticationService,IHttpContextAccessor context)
{
_httpContext = context;
_authenticationService = authenticationService;
}
public IActionResult Login()
{
return View();
}
[HttpPost]
// [ChildActionOnly]
public async Task<IActionResult> Login(LoginModel model)
{
LdapEntry result1 = null;
var result = _authenticationService.ValidateUser1("mm.fr", model.UserName, model.Password);
if (result.Equals(true))
{
result1 = _authenticationService.GetLdapUserDetail("mm.fr", model.UserName, model.Password);
ViewBag.Username = result1.GetAttribute("CN").StringValue;
Index(result1.GetAttribute("CN").StringValue);
if (result != null)
{
var claims = new List<Claim>
{
new Claim(ClaimTypes.Name, model.UserName),
new Claim(ClaimTypes.Role, "Administrator"),
};
var claimsIdentity = new ClaimsIdentity(
claims, CookieAuthenticationDefaults.AuthenticationScheme);
var authProperties = new AuthenticationProperties
{
};
await HttpContext.SignInAsync(
CookieAuthenticationDefaults.AuthenticationScheme,
new ClaimsPrincipal(claimsIdentity),
authProperties);
}
}
else
{
this.TempData["ErrorMessage"] = "Password is incorrect";
}
return RedirectToAction(nameof(MainDashboardController.Index), "MainDashboard");
}
public IActionResult Index(string str)
{
_httpContext.HttpContext.Session.SetString("mystr", str);
return View();
}
}
Here below is what I wrote in Startup:
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.AddControllersWithViews();
services.AddDbContext<CSDDashboardContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("CSDDashboardContext")));
//Notice this is NOT the same class... Assuming this is a valid DBContext. You need to add this class as well.
//services.AddDbContext<CSSDDashboardContext>(options =>
// options.UseSqlServer(Configuration.GetConnectionString("CSDDashboardContext")));
//*---------------------------New Active Directory--------------------------------
services.AddScoped<IAuthenticationService, LdapAuthenticationService>();
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme).AddCookie(options =>
{
// Cookie settings
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(15);
options.LoginPath = "/Account/Login";
options.SlidingExpiration = true;
});
services.AddSession();
services.AddSingleton<MySharedDataViewComponent>();
services.AddHttpContextAccessor();
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, ILoggerFactory loggerFactory)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseSession();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller=Home}/{action=Index}/{id?}");
});
}
}
function deleteCookie(name) {
setCookie(name,"",-1);
}
function setCookie(name,value,days) {
if (days) {
var date = new Date();
date.setTime(date.getTime()+(days*24*60*60*1000));
var expires = "; expires="+date.toGMTString();
}
else expires = "";
document.cookie = name+"="+value+expires+"; path=/";
}
$(window).unload(function() {
deleteCookie('Your cookie to delete name');
});

blazor webassembly System.Net.Http.HttpRequestException: Response status code does not indicate success: 400 (Bad Request)

I have a blazor project that is dotnet core hosted. I am working on blazor authentication following this tutorial. Everything is ok on the server side because I was able to use Postman to create users successfully. I have tried different suggestions online but non work for me. Some suggested CORS which I fixed, some route which I amended but problem still persist.
I have been having issue debugging as I cant get break point in the browser console. I have debugged some blazor project I could set break points and view local variables but I couldnt with the current project. I dont know if its a bug.
server startup.cs
namespace EssentialShopCoreBlazor.Server
{
public class Startup
{
public IConfiguration Configuration { get; }
public Startup(IConfiguration _configuration)
{
Configuration = _configuration;
}
readonly string AllowSpecificOrigins = "allowSpecificOrigins";
// This method gets called by the runtime. Use this method to add services to the container.
// For more information on how to configure your application, visit https://go.microsoft.com/fwlink/?LinkID=398940
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<DbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection")));
services.AddIdentity<IdentityUsersModel, IdentityRole>()
.AddEntityFrameworkStores<DbContext>()
.AddDefaultTokenProviders();
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme).AddJwtBearer(options =>{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = Configuration["JwtIssuer"],
ValidAudience = Configuration["JwtAudience"],
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes(Configuration["JwtSecurityKey"]))
};
});
services.AddCors(options =>
{
options.AddPolicy(AllowSpecificOrigins,
builder =>
{
builder.WithOrigins("https://localhost:44365", "https://localhost:44398")
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
});
services.AddScoped<AccountAuthController>();
services.AddMvc().AddNewtonsoftJson();
services.AddResponseCompression(opts =>
{
opts.MimeTypes = ResponseCompressionDefaults.MimeTypes.Concat(
new[] { "application/octet-stream" });
});
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
app.UseResponseCompression();
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseBlazorDebugging();
}
app.UseCors(AllowSpecificOrigins);
app.UseStaticFiles();
app.UseClientSideBlazorFiles<Client.Startup>();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapFallbackToClientSideBlazor<Client.Startup>("index.html");
});
}
}
}
controller
namespace EssentialShopCoreBlazor.Server.Controllers
{
[Route("essential/users/")]
public class AccountAuthController : ControllerBase
{
private static UserModel LoggedOutUser = new UserModel { IsAuthenticated = false };
private readonly UserManager<IdentityUsersModel> userManager;
private readonly IConfiguration configuration;
private readonly SignInManager<IdentityUsersModel> signInManager;
public AccountAuthController(UserManager<IdentityUsersModel> _userManager, SignInManager<IdentityUsersModel> _signInManager, IConfiguration _configuration)
{
userManager = _userManager;
signInManager = _signInManager;
configuration = _configuration;
}
[HttpPost]
[Route("create")]
public async Task<ActionResult<ApplicationUserModel>> CreateUser([FromBody] ApplicationUserModel model)
{
var NewUser = new IdentityUsersModel
{
UserName = model.UserName,
BusinessName = model.BusinessName,
Email = model.Email,
PhoneNumber = model.PhoneNumber
};
var result = await userManager.CreateAsync(NewUser, model.Password);
if (!result.Succeeded)
{
var errors = result.Errors.Select(x => x.Description);
return BadRequest(new RegistrationResult { Successful = false, Errors = errors });
}
return Ok(new RegistrationResult { Successful = true });
}
}}
client service
public class AuthService : IAuthService
{
private readonly HttpClient _httpClient;
private readonly AuthenticationStateProvider _authenticationStateProvider;
private readonly ILocalStorageService _localStorage;
string BaseUrl = "https://localhost:44398/essential/users/";
public AuthService(HttpClient httpClient,
AuthenticationStateProvider authenticationStateProvider,
ILocalStorageService localStorage)
{
_httpClient = httpClient;
_authenticationStateProvider = authenticationStateProvider;
_localStorage = localStorage;
}
public async Task<RegistrationResult> Register(ApplicationUserModel registerModel)
{
var result = await _httpClient.PostJsonAsync<RegistrationResult>(BaseUrl + "create", registerModel);
return result;
}
}

ASP.NET Core - can't get simple bearer token auth working

I am building an API with ASP.NET Core 2, and I am trying to get a simple auth example that uses a Bearer token to work.
First, here is my Startup code...
public class Startup
{
public Startup(IConfiguration configuration)
{
Configuration = configuration;
}
public IConfiguration Configuration { get; }
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuer = true,
ValidateAudience = true,
ValidateLifetime = true,
ValidateIssuerSigningKey = true,
ValidIssuer = "mydomain.com",
ValidAudience = "mydomain.com",
IssuerSigningKey = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("THESECRETKEY THESECRETKEY THESECRETKEY THESECRETKEY"))
};
});
// goes last
services.AddMvc();
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler();
}
app.UseStatusCodePages();
// goes last
app.UseMvc();
}
}
Then I have an Auth controller, that returns the token...
[Route("api/[controller]")]
public class AuthController : Controller
{
[AllowAnonymous]
[HttpPost("RequestToken")]
public IActionResult RequestToken([FromBody] TokenRequest request)
{
if (request.Username == "theuser" && request.Password == "thepassword")
{
var claims = new[]
{
new Claim(ClaimTypes.Name, request.Username)
};
var key = new SymmetricSecurityKey(Encoding.UTF8.GetBytes("THESECRETKEY THESECRETKEY THESECRETKEY THESECRETKEY"));
var creds = new SigningCredentials(key, SecurityAlgorithms.HmacSha256);
var token = new JwtSecurityToken(
issuer: "mydomain.com",
audience: "mydomain.com",
claims: claims,
expires: DateTime.Now.AddMinutes(30),
signingCredentials: creds);
return Ok(new
{
token = new JwtSecurityTokenHandler().WriteToken(token)
});
}
return BadRequest("Could not verify username and password");
}
}
public class TokenRequest
{
public string Username { get; set; }
public string Password { get; set; }
}
So in Postman you can see I get a token back...
And then I try GET from a Values controller...
[Route("api/[controller]")]
[Authorize]
public class ValuesController : Controller
{
[HttpGet("{id}")]
public string Get(int id)
{
return "value";
}
}
If I add the [Anonymous] attribute, it works fine. However, when it requires authorization, I get a 401...
You haven't referenced the Authentication middleware in your Startup.cs
You can reference it by adding app.UseAuthentication(); preferably just before the app.UseMvc();
Here's how your Startup.cs's Configure method should look like:
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseExceptionHandler();
}
app.UseStatusCodePages();
app.UseAuthentication();
app.UseMvc();
}
You can read more into middleware here and more into authorization with ASP.NET Core here.
You should also look here for everything else about ASP.NET Core