.NET Core 6 Web Api Authentication/ Authorization not working - asp.net-core

I have written a small .NET 6 minimal web api, which is heavily based on / copied from a tutorial I did. The tutorial worked as expected, but I have two problems in my version.
The first problem is with swagger. When I uncomment the line c.OperationFilter<SecurityRequirementsOperationFilter>(); in AddSwaggerGen() I get a swagger error in the browser "Failed to load API definition. Fetch error. response status is 500 https://localhost:7123/swagger/v1/swagger.json"
The second problem is (when the swagger line is commented out to get it to run) nothing is secured. Either from swagger or from the browser I can access all endpoints.
Here is my program.cs. It certainly looks like I have everything in the right place. Even so, I would have expected that if it was misconfigured, it would prevent me from accessing the endpoints.
using DataAccess.DbAccess;
using MyAuthAPI;
using Microsoft.AspNetCore.Authentication.JwtBearer;
using Microsoft.IdentityModel.Tokens;
using Microsoft.OpenApi.Models;
using Swashbuckle.AspNetCore.Filters;
var builder = WebApplication.CreateBuilder(args);
// Add services to the container.
// Learn more about configuring Swagger/OpenAPI at https://aka.ms/aspnetcore/swashbuckle
builder.Services.AddEndpointsApiExplorer();
builder.Services.AddSwaggerGen(
c => {
c.SwaggerDoc("v1", new OpenApiInfo { Title = "MyAPI", Version = "v1" });
c.AddSecurityDefinition("oauth2", new OpenApiSecurityScheme {
Description = "Standard Authorization header using the Bearer scheme. Example: \"bearer {token}\"",
In = ParameterLocation.Header,
Name = "Authorization",
Type = SecuritySchemeType.ApiKey
});
// When this line is uncommented I get a swagger error- Failed to load api
// c.OperationFilter<SecurityRequirementsOperationFilter>();
});
builder.Services.AddSingleton<ISqlDataAccess, SqlDataAccess>();
builder.Services.AddSingleton<IPersonAuthData, PersonAuthData>();
builder.Services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options => {
options.TokenValidationParameters = new TokenValidationParameters {
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(System.Text.Encoding.ASCII.GetBytes(builder.Configuration.GetSection("AppSettings:Token").Value)),
ValidateIssuer = false,
ValidateAudience = false
};
});
builder.Services.AddAuthorization();
var app = builder.Build();
// app.UseStatusCodePages();
// Configure the HTTP request pipeline.
if (app.Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "MyAPI v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.ConfigureRoutes();
app.Run();
The routes and db access etc all works fine, but here is the routes.cs file just in case.
using DataAccess.DTOs;
using Microsoft.AspNetCore.Authorization;
namespace MyAuthAPI;
[Authorize]
public static class Routes
{
public static void ConfigureRoutes(this WebApplication app)
{
app.MapGet("PersonAuths", GetPersonAuths);
app.MapGet("PersonAuths/{id}", GetPersonAuth);
app.MapPost("Login", Login);
}
private static async Task<IResult>GetPersonAuths(IPersonAuthData data)
{
try
{
return Results.Ok(await data.GetPersonAuths());
} catch(Exception ex)
{
return Results.Problem(ex.Message);
}
}
private static async Task<IResult> GetPersonAuth(int id, IPersonAuthData data)
{
try
{
var results = await data.GetPersonAuth(id);
if (results == null) return Results.NotFound();
return Results.Ok(results);
}
catch (Exception ex)
{
return Results.Problem(ex.Message);
}
}
[AllowAnonymous]
private static async Task<IResult> Login(LoginDTO loginDetails, IPersonAuthData data)
{
try
{
string jwt = await data.Login(loginDetails);
if (jwt != "")
{
return Results.Ok(jwt);
} else
{
return Results.Unauthorized();
}
}
catch (Exception ex)
{
return Results.Problem(ex.Message);
}
}
}
Thanks
Edit
The swagger issue looks like it may be due to Swashbuckle.AspNetCore.Filters not supporting minimal apis.
Swashbuckle.AspNetCore.Filters

Related

ASP.NET Core API: Authentication popup is not showing up in Swagger UI

I have an ASP.NET Core Web API with Swagger integrated using Swashbuckle. I have successfully integrated authorization on Swagger UI using an operation filter, because I do not want to show padlock for anonymous APIs.
.OperationFilter<AuthorizeFilter>()
Inside the filter, I have registered basic auth security requirement for Swagger UI.
My problem is, even though authentication is happening in APIs on Swagger UI, I no longer see that nice authentication popup which is giving when click on the padlock icon.
Could someone answer, why I am not seeing the auth popup now?
Assuming you have some endpoints that protected with [Authorize] attribute (can also be put on the controller).
[Route("")]
public class HelloController : ControllerBase
{
[Authorize]
[HttpGet("secure")]
public IActionResult GetSomethingPrivate()
{
return Ok("secret");
}
[HttpGet("public")]
public IActionResult GetSomethingPublic()
{
return Ok("hey");
}
}
You need to define a security scheme suitable for your needs. But do not require it globally, instead add it inside an operation filter. Here I've added a simple token auth:
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "ApiPlayground", Version = "v1" });
c.AddSecurityDefinition("token", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.ApiKey,
In = ParameterLocation.Header,
Name = HeaderNames.Authorization,
Scheme = "Bearer"
});
// dont add global security requirement
// c.AddSecurityRequirement(/*...*/);
c.OperationFilter<SecureEndpointAuthRequirementFilter>();
});
}
And here's the operation filter which references the token auth scheme we've just created. It checks if the endpoint needs authentication, then adds the requirement.
internal class SecureEndpointAuthRequirementFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (!context.ApiDescription
.ActionDescriptor
.EndpointMetadata
.OfType<AuthorizeAttribute>()
.Any())
{
return;
}
operation.Security = new List<OpenApiSecurityRequirement>
{
new OpenApiSecurityRequirement
{
[new OpenApiSecurityScheme
{
Reference = new OpenApiReference { Type = ReferenceType.SecurityScheme, Id = "token" }
}] = new List<string>()
}
};
}
}
When you run the app, it works as you expect:
So does the auth popup:
Bonus: using basic auth
Define a new security scheme with following values:
public void ConfigureServices(IServiceCollection services)
{
// ...
services.AddSwaggerGen(c =>
{
// ...
// basic auth scheme (username + password)
c.AddSecurityDefinition("basic", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.Http,
Scheme = "basic"
});
// dont add global security requirement
// c.AddSecurityRequirement(/*...*/);
c.OperationFilter<SecureEndpointAuthRequirementFilter>();
});
}
Then update the operation filter to reference basic auth scheme:
internal class SecureEndpointAuthRequirementFilter : IOperationFilter
{
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
if (!context.ApiDescription
.ActionDescriptor
.EndpointMetadata
.OfType<AuthorizeAttribute>()
.Any())
{
return;
}
operation.Security = new List<OpenApiSecurityRequirement>
{
new OpenApiSecurityRequirement
{
[new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "basic" // <-- changed "token" -> "basic"
}
}] = new List<string>()
}
};
}
}
here's how the auth popup looks:
After logging in, requests include the correct Authorization header.
In my case, I am using JWT Token Authentication with .NET Core API. I Configure the swagger with the authorization token using the below code. This code will add global security requirements.
In Startup Class ConfigureServices Method.
//Swagger Configuration
services.AddSwaggerGen(options =>
{
options.SwaggerDoc("v1", new Microsoft.OpenApi.Models.OpenApiInfo
{
Title = "API",
Version = "v2",
Description = "Your Api Description"
});
options.AddSecurityDefinition("Bearer", new OpenApiSecurityScheme
{
Description = "JWT Authorization header using the Bearer scheme (Example: 'Bearer 12345abcdef')",
Name = "Authorization",
In = ParameterLocation.Header,
Type = SecuritySchemeType.ApiKey,
Scheme = "Bearer"
});
options.AddSecurityRequirement(new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme
{
Reference = new OpenApiReference
{
Type = ReferenceType.SecurityScheme,
Id = "Bearer"
}
},
Array.Empty<string>()
}
});
});
And In Configure Method
app.UseSwagger();
app.UseSwaggerUI(options =>
{
options.SwaggerEndpoint("/swagger/v1/swagger.json", "API");
});
After running the API project Authorize button will appear on the right side. On Click the authorize button Authorization popup open and then pass the token in the text box with 'Bearer token'.
Authorization working fine for me.
the Abdusco's answer is true but modify the Apply method like this for AllowAnonymousAttribute Methods in Authorized Controllers
if (!context.ApiDescription
.ActionDescriptor
.EndpointMetadata
.OfType<AuthorizeAttribute>()
.Any() || context.ApiDescription
.ActionDescriptor
.EndpointMetadata
.OfType<AllowAnonymousAttribute>()
.Any())
{
return;
}

.NET 5 API add Access-Control-Allow-Origin headers - with Ionic 5.5

Can you please assist, I have a .NET 5 Web API and Ionic 5.5 app. I have deployed the API and the Ionic app to Azure Web Services. The API is serving the Ionic app, so they are on the same domain - I can say same origin because the scheme, and domain are the same.
The issue is that, the Ionic app is failing to call the API because it seems like CORS is blocking it.
In my API, I have allowed any origin, any header and credentials but it's still not working. I have attached my Startup.cs file for the .NET 5 API.
Startup.cs code
using AutoMapper;
using EventManager.Business.Repositories;
using EventManager.Database;
using Microsoft.AspNetCore.Builder;
using Microsoft.AspNetCore.Hosting;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Versioning;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.DependencyInjection;
using Microsoft.Extensions.DependencyInjection.Extensions;
using Microsoft.Extensions.Hosting;
using Microsoft.OpenApi.Models;
using System;
using System.IO;
using System.Reflection;
namespace EventManager.Api
{
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.AddCors(options =>
{
options.AddDefaultPolicy(
builder =>
{
builder.AllowAnyOrigin()
.AllowAnyHeader()
.AllowAnyMethod();
});
//options.AddPolicy("AllowOrigin", options => options.AllowAnyOrigin().AllowAnyHeader().AllowAnyMethod());
});
services.AddControllers();
services.AddApiVersioning(x =>
{
x.DefaultApiVersion = new ApiVersion(2, 1);
x.AssumeDefaultVersionWhenUnspecified = true;
x.ReportApiVersions = true;
// Supporting multiple versioning scheme
x.ApiVersionReader = new UrlSegmentApiVersionReader();
});
services.AddVersionedApiExplorer(options =>
{
options.GroupNameFormat = "'v'VVV";
options.SubstituteApiVersionInUrl = false;
});
var mappingConfig = new MapperConfiguration(mc =>
{
mc.AddProfile(new Business.MappingProfile());
});
var mapper = mappingConfig.CreateMapper();
services.AddSingleton(mapper);
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo
{
Title = "Event Manager API Documentation",
Version = "v1",
Contact = new OpenApiContact
{
Email = "eric#xxxxx.com",
Name = "Eric Smith",
Url = new Uri("https://www.xxxxx.org/")
},
Description = #"Used for as a self-service for event attendees.
To capture attendee details and print out attendee badges",
License = new OpenApiLicense
{
Name = "Use under LICX",
Url = new Uri("https://www.xxxxx.org/api/license"),
}
});
//Set the comments path for the Swagger JSON and UI.
var xmlFile = $"{Assembly.GetExecutingAssembly().GetName().Name}.xml";
var xmlPath = Path.Combine(AppContext.BaseDirectory, xmlFile);
c.IncludeXmlComments(xmlPath);
});
services.AddDbContext<AppDbContext>(x => x.UseSqlServer(Configuration.GetConnectionString("MSSqlConnection"),
b => b.MigrationsAssembly("EventManager.Database")));
services.TryAddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.AddScoped<EventRepository>();
services.AddScoped<AttendeeRepository>();
services.AddScoped<DesignationRepository>();
services.AddScoped<EntryQuestionRepository>();
services.AddScoped<EntryQuestionSectionRepository>();
//Sart: To serve angular app
services.AddSpaStaticFiles(configuration => { configuration.RootPath = "ClientApp"; });
//End: To serve angular app
}
// 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.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "EventManager.Api v1"));
}
app.UseRouting();
app.UseCors(builder =>
{
builder
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials();
});
app.UseAuthorization();
//Start: To serve angular app
app.UseDefaultFiles();
app.UseSpaStaticFiles();
//End: To serve angular app
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
//Start: To serve angular app
app.UseSpa(spa => { spa.Options.SourcePath = "ClientApp"; });
//End: To serve angular app
}
}
}
My API call from the Ionic app
headers = new HttpHeaders({
"Authorization": "Bearer " + "XXXXXXX",
"Content-Type": "application/json"
});
//environment.api = 'https://myazureappname.azurewebsites.net/api/'
get(cellNumber?: string): Observable<AttendeeGetModel[]> {
return this._http.get<AttendeeGetModel[]>(`${environment.api}v1/attendees?cellNumber=${cellNumber}`, { headers: this.headers });
}
**Error message from Firefox browser attached**
[![enter image description here][1]][1]
The error ERR_NAME_NOT RESOLVED typically means: Chrome cannot resolve the domain name which in most cases means you're using the wrong domain name or have a typo.
In your case it looks like your're using the wrong top level domain in your app. Postman = ".net", Your app = ".com".

ASP.NET Core returns InternalServerError while using Identity server

I trying to add identity server for my web API as its identity server4 documentation. when I was trying to call API from my console application it's every time returns InternalServerError.
Here is My Identity server Config.cs
public static class Config
{
// register api
public static IEnumerable<ApiScope> ApiScopes => new List<ApiScope>
{
// in here add your api name
new ApiScope("api1", "My API")
};
// register client which is going to access api. eg: front-end application, mobile apps etc. can add multiple client.
public static IEnumerable<Client> Clients => new List<Client>
{
new Client
{
// which is going to access
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 = { "api1" }
}
};
}
and here the identity server startup file configuration service and configure functions
public void ConfigureServices(IServiceCollection services)
{
// uncomment, if you want to add an MVC-based UI
services.AddControllersWithViews();
var builder = services.AddIdentityServer()
.AddInMemoryApiScopes(Config.ApiScopes)
.AddInMemoryClients(Config.Clients);
builder.AddDeveloperSigningCredential();
builder.AddDeveloperSigningCredential();
}
public void Configure(IApplicationBuilder app)
{
if (Environment.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
// uncomment if you want to add MVC
app.UseStaticFiles();
app.UseRouting();
app.UseIdentityServer();
// uncomment, if you want to add MVC
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
});
}
and here is my API startup file's congurationService and configure functions
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers();
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://localhost:14030/";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidateAudience = false
};
}
);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
here is my API controller
[Route("identity")]
public class IdentityController : ControllerBase
{
[HttpGet]
[Authorize]
public IActionResult Get() => Ok(new JsonResult(from c in User.Claims select new { c.Type, c.Value }));
}
and here is my console application client request a api
static async System.Threading.Tasks.Task Main(string[] args)
{
// discover endpoints from metadata
var client = new HttpClient();
var disco = await client.GetDiscoveryDocumentAsync("http://localhost:14030");
if (disco.IsError)
{
Console.WriteLine(disco.Error);
return;
}
// request token
var tokenResponse = await client.RequestClientCredentialsTokenAsync(new ClientCredentialsTokenRequest
{
Address = disco.TokenEndpoint,
ClientId = "client",
ClientSecret = "secret",
Scope = "api1"
});
if (tokenResponse.IsError)
{
Console.WriteLine(tokenResponse.Error);
return;
}
Console.WriteLine(tokenResponse.Json);
Console.WriteLine("\n\n");
// call api
var apiClient = new HttpClient();
apiClient.SetBearerToken(tokenResponse.AccessToken);
var response = await apiClient.GetAsync("https://localhost:5001/identity");
if (!response.IsSuccessStatusCode)
{
Console.WriteLine(response.StatusCode);
}
else
{
var content = await response.Content.ReadAsStringAsync();
Console.WriteLine(content);
}
}
what are the mistakes should I have to fix. Im really appreciates your valuable answers and efforts.
Thank
I got the code working, I would do the following:
use HTTPS here, not HTTP:
var disco = await
client.GetDiscoveryDocumentAsync("http://localhost:14030");
Remove the duplicate lines of in IdentityServer startup class:
builder.AddDeveloperSigningCredential();
I would add in your API startup.cs
services.AddAuthorization();
Remove the trailing / at the end of the URL here:
options.Authority = "https://localhost:14030/";
To get more debugging output from your API, you can add the following two trace lines to your appsettings.Development.json file:
{
"Logging": {
"LogLevel": {
"Default": "Information",
"Microsoft": "Warning",
"Microsoft.Hosting.Lifetime": "Information",
"Microsoft.AspNetCore.Authentication": "Trace",
"Microsoft.AspNetCore.Authorization": "Trace"
}
}
}
If you want to validate the Audience (and using IdentityServer4 v4.00) you can add:
services.AddControllers();
services.AddAuthentication("Bearer")
.AddJwtBearer("Bearer", options =>
{
options.Authority = "https://localhost:14030";
options.TokenValidationParameters = new TokenValidationParameters
{
ValidAudiences = new[] {"https://localhost:14030/resources"},
ValidateAudience = true
};
}
);

.netcore web API Post does not work without SSL

I have an MVC core 2.2 application with Controllers, API controllers, and some Views. Some of the Views make use of the API endpoints within the same application. All was ok until I enabled TLS 1.2 in my windows server.
Now all endpoints decorated as GET methods work. But all POSTs have stopped working with chrome reporting this POST 'link' net::ERR_CONNECTION_RESET.
Other browsers catch exception at fail and display my error text with object
$.ajax({
url: "/api/Cart",
method: "post",
data: JSON.stringify(vm),
contentType: "application/json"
}).done(function(result) {
console.log(result);
}).fail(function(ex) {
console.log("Error occured while adding to cart" + ex)
});
I want to be able to POST even without SSL. I have disabled TLS 1.2 and restarted but still the same result.
If I browse the site with https://, POST and GET endpoints all work perfectly but if I browse with HTTP://, only GET endpoints work, all POST endpoints do not work.
I have combed the internet for almost 5 hours for something I thought would be simple.
public void ConfigureServices(IServiceCollection services)
{
var connectionString = Configuration.GetConnectionString("DataConnection");
services.AddDbContext<DataContext>(options => options.UseSqlServer(connectionString));
//Inject Connection String to other Classes
services.AddSingleton(_ => connectionString);
services.AddIdentity<ApplicationUser, IdentityRole>()
.AddEntityFrameworkStores<DataContext>()
.AddDefaultTokenProviders();
services.ConfigureApplicationCookie(options =>
{
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromHours(1);
});
services.AddScoped<IUnitOfWork, UnitOfWork>();
services.AddMediatR(typeof(CreateProductCommand).Assembly, typeof(CreateProductCommandHandler).Assembly);
services.AddAutoMapper(typeof(MappingProfile));
// Add memory cache services
services.AddMemoryCache();
services.AddMvc(o =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
o.Filters.Add(new AuthorizeFilter(policy));
}) .SetCompatibilityVersion(Microsoft.AspNetCore.Mvc.CompatibilityVersion.Version_2_2);
}
// HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
}
var serviceProvider = app.ApplicationServices.GetRequiredService<IServiceScopeFactory>().CreateScope().ServiceProvider;
DataContextSeed.Initialize(serviceProvider);
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
Above is the Startup class and a sample post is here
[HttpPost]
public IActionResult Post([FromBody] SaleItemDTO md)
{
if(md != null)
{
if(md.Quantity <= md.Stock)
{
_sales.SalesPerson = User.Identity.Name;
_sales.SalesType = md.SalesType;
return Ok(_sales.ItemsInDb);
}
}
return BadRequest(new { Message = "Not Valid Content posted" });
}
Help with your thoughts.

Getting an error "blocked by CORS policy" only for "https://localhost:44361/connect/token" in Angular 7 web app

Complete error is as shown below.
Access to XMLHttpRequest at 'https://localhost:44361/connect/token' from origin 'http://localhost:4200' has been blocked by CORS policy: No 'Access-Control-Allow-Origin' header is present on the requested resource.
I have enabled cores policy in my start up class in .net core web api as shown below.
My ConfigureService method
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
services.AddDbContext<LEAFDDbContext>(options =>
options.UseSqlServer(Configuration.GetConnectionString("DefaultConnection"), builder =>
{
builder.EnableRetryOnFailure(5, TimeSpan.FromSeconds(10), null);
}));
services.AddIdentity<ApplicationUser, ApplicationRole>()
.AddEntityFrameworkStores<LEAFDDbContext>()
.AddDefaultTokenProviders();
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
Installer.ConfigureServices(services);
//services.AddCors();
//services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
//services.AddSingleton<IAuthenticationSchemeProvider, CustomAuthenticationSchemeProvider>();
//services.AddAuthentication(options =>
//{
// //options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
// //options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
// //options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
// options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
// options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
//})
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddCookie(options =>
{
options.Events.OnRedirectToLogin = ctx =>
{
// if it is an ajax/api request, don't redirect
// to login page.
if (!(IsAjaxRequest(ctx.Request) || IsApiRequest(ctx.Request)))
{
ctx.Response.Redirect(ctx.RedirectUri);
return Task.CompletedTask;
}
ctx.Response.StatusCode = StatusCodes.Status401Unauthorized;
return ctx.Response.WriteAsync("Unauthorized");
};
})
.AddOAuthValidation()
//services.AddAuthentication()
.AddOpenIdConnectServer(options =>
{
options.Provider = new AuthorizationProvider();
// Enable the authorization and token endpoints.
options.AuthorizationEndpointPath = "/connect/authorize";
options.TokenEndpointPath = "/connect/token";
options.AllowInsecureHttp = true;
// Note: to override the default access token format and use JWT, assign AccessTokenHandler:
//
options.AccessTokenHandler = new JwtSecurityTokenHandler
{
InboundClaimTypeMap = new Dictionary<string, string>(),
OutboundClaimTypeMap = new Dictionary<string, string>()
};
//
// Note: when using JWT as the access token format, you have to register a signing key.
//
// You can register a new ephemeral key, that is discarded when the application shuts down.
// Tokens signed using this key are automatically invalidated and thus this method
// should only be used during development:
//
options.SigningCredentials.AddEphemeralKey();
//
// On production, using a X.509 certificate stored in the machine store is recommended.
// You can generate a self-signed certificate using Pluralsight's self-cert utility:
// https://s3.amazonaws.com/pluralsight-free/keith-brown/samples/SelfCert.zip
//
//options.SigningCredentials.AddCertificate("7D2A741FE34CC2C7369237A5F2078988E17A6A75");
});
}
Configure method
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
}
else
{
app.UseHsts();
}
//app.UseOAuthValidation();
app.UseAuthentication();
app.UseMvc();
app.UseMiddleware();
app.UseCors("CorsPolicy");
//app.UseCors(builder => builder.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin().AllowCredentials());
app.UseWelcomePage();
}
This is working as expected for all other calls except 'https://localhost:44361/connect/token' : When getting the token.
I'm sending all the requests from Angular 7 web app to .net core web api.
As a work around i have installed cross-origin resource sharing extension from google chrome and I need a code level change to fix this issue permanently.
This is the final solution i came up with.
Instead of calling "https://localhost:44361/connect/token" directly from Angular 7 app, I have created a method in API controller that will invoke above call and get the token. So when ever i need all i need is to call the newly created method in API controller.
New api call as follows
[HttpPost]
[Route("/api/[controller]/Token")]
[EnableCors("CorsPolicy")]
public async Task<ContentResult> Token(LoginTokenRequestModel model)
{
string UserId = string.Empty;
try
{
using (HttpClient httpClient = new HttpClient())
{
if (!String.IsNullOrEmpty(model.Email))
{
var content = new FormUrlEncodedContent(new KeyValuePair<string, string>[]{
new KeyValuePair<string, string>("grant_type", model.grant_type),
new KeyValuePair<string, string>("client_secret", model.client_secret),
new KeyValuePair<string, string>("client_id", model.client_id),
new KeyValuePair<string, string>("username", model.Email),
new KeyValuePair<string, string>("password", model.Password),
});
string apiURL = "https://localhost:44361";
var response = await httpClient.PostAsync(apiURL + "/connect/token", content);
var resultContent = await response.Content.ReadAsStringAsync();
if (response.IsSuccessStatusCode)
{
var token = JsonConvert.DeserializeObject<object>(resultContent);
return new ContentResult
{
Content = token.ToString(),
ContentType = "text/plain",
StatusCode = 200
};
}
return new ContentResult
{
Content = JsonConvert.DeserializeObject<object>(resultContent).ToString(),
ContentType = "text/plain",
StatusCode = 200
};
}
}
}
catch (Exception ex)
{
return new ContentResult
{
Content = ex.Message,
ContentType = "text/plain",
StatusCode = 400
};
}
return new ContentResult
{
Content = "",
ContentType = "text/plain",
StatusCode = 400
};
}
This resolved my issue.
Use app.UseCors() before app.useMvc(). Order matters when it comes to middleware invocation. Personally, I keep it in environment determining if-else.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseCors("CorsDevPolicy");
}
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.UseMiddleware(typeof(ErrorHandlingMiddleware));
app.UseMvc();
}