How to check user-agent in ASP.NET Core health check calls when using own authentication, authorization? - c#

I used the accepted answer to How to check user-agent in ASP.NET Core health check calls (MapHealthChecks)? , with one difference in requirement:
My application is not using App services authentication and authorization. Therefore, I needed to allow anonymous access for healthcheck as per documentation.
Here are changes to Startup.cs
//other services
services.AddHttpContextAccessor();
services.AddScoped<IAuthorizationHandler, UserAgentAuthorizationHandler>();
services.AddHealthChecks()
.AddCheck<HealthCheckFoo>("health_check_foo")
.AddCheck<HealthCheckBar>("health_check_bar");
//other services.AddAuthorization
services.AddAuthorization(options =>
{
options.AddPolicy("HealthCheckPolicy", builder =>
{
builder.AddRequirements(new UserAgentRequirement("HealthCheck/1.0"));
});
});
//...
app.UseEndpoints(endpoints =>
{
//other endpoints...
endpoints.MapHealthChecks("/health", new HealthCheckOptions { AllowCachingResponses = false })
.RequireAuthorization("HealthCheckPolicy");
.WithMetadata(new AllowAnonymousAttribute());
My expectation is that when testing locally, https://localhost:5001/health return an error. It does not.

It looks as your startup class has a mistake on the endpoints.MapHealthChecks adds a RequireAuthorization but as the same time you also add the AllowAnonymousAttribute.
Try with:
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
endpoints.MapHealthChecks("/health", new HealthCheckOptions()
{
AllowCachingResponses = false,
})
.RequireAuthorization("HealthCheckPolicy");
});

Related

Having issues with IAntiforgery in ASP.NET Core 6.0

Please look at my long answer at the end about how I resolved this. I had gotten too frustrated and after another day with a fresh perspective and more sleep, I got to a solution.
I did this in 5.0 with no issues in the Startup.Configure method.
Basically I created a header for the request on a protected route. I'm using React as the front end. I'm finding when I place everything in Program.cs the dependency injection, authorization doesn't work right so I split up into separate Program and Startup files.
But I can't use the following signature in 6.0 like I did in 5.0:
example that worked in 5.0:
public void Configure(IApplicationBuilder app, IWebHostEnvironment env, IAntiforgery antiforgery)
{
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("antiforgery/token", context =>
{
var tokens = antiforgery.GetAndStoreTokens(context);
context.Response.Headers.Append("XYZ", tokens.RequestToken!);
return Task.FromResult(StatusCodes.Status200OK);
});
endpoints.MapControllerRoute(
name: "default",
pattern: "{controller}/{action=Index}/{id?}");
});
}
Program.cs (my attempt to split up program and startup - 6.0)
var startup = new dolpassword.Startup(builder.Configuration);
startup.ConfigureServices(builder.Services);
var app = builder.Build();
startup.Configure(app,app.Environment);
Saw this example on Microsoft website:
app.UseRouting();
app.UseAuthorization();
// app.Services syntax error in Configure for 6.0
var antiforgery = **app.Services.GetRequiredService<IAntiforgery>();**
app.Use((context, next) =>
{
var requestPath = context.Request.Path.Value;
if (string.Equals(requestPath, "/",
StringComparison.OrdinalIgnoreCase)
|| string.Equals(requestPath, "/index.html",
StringComparison.OrdinalIgnoreCase))
{
var tokenSet = antiforgery.GetAndStoreTokens(context);
context.Response.Cookies.Append("XSRF-TOKEN",
tokenSet.RequestToken!,
new CookieOptions { HttpOnly = false });
}
return next(context);
});
I was able to successfully do this in 6.0 so I will share some of the code and how I resolved it. I also had Windows authentication baked in with a policy-based authorization. The reason I'm putting all the authentication/authorization wireup in this post is because the entire solution relies on authentication, authorization and antiforgery.
First I set up my services. I get IAntiforgery by default by adding ControllersWithViews but I want to use my own header name, which is X-XSRF-TOKEN instead of the .AspNet.Antiforgery.xxxx or whatever the default is. I also needed options.DefaultAuthenticateScheme = NegotiateDefaults.AuthenticationScheme; to get Windows auth working.
string CorsPolicy = "CorsPolicy";
//===================================formerly Configure Services
WebApplicationBuilder? builder = WebApplication.CreateBuilder(args);
ConfigurationManager _configuration = builder.Configuration;
// Add services to the container.
**IServiceCollection? services = builder.Services;
services.AddAntiforgery(options => { options.HeaderName = "X-XSRF-TOKEN";
options.Cookie.HttpOnly = false; });**
services.AddTransient<IActiveDirectoryUserService, ActiveDirectoryUserService>();
services.AddControllersWithViews();
services.AddAuthentication(options => {//needed for Windows authentication
options.DefaultAuthenticateScheme = NegotiateDefaults.AuthenticationScheme;
});
Adding more...
I'm using Windows auth so I'm using the Negotiate provider. Then I set up my Authorization. I insert my own authorization policy and also add my claims transformers to get the authenticated user into a claim. The fallback policy in Authorization was causing an Authentication exception.
services.AddAuthorization(options =>
{
// options.FallbackPolicy = options.DefaultPolicy;//authorization bombs if you include this line
options.AddPolicy("AuthenticatedOnly", policy => {
policy.Requirements.Add(new AuthenticatedRequirement(true));
});
});
services.AddTransient<IClaimsTransformation, MyClaimsTransformer>();
services.AddTransient<IAuthorizationHandler, AppUserRoleHandler>();
services.AddTransient<IAuthorizationHandler, AuthenticatedRoleHandler>();
services.AddCors(options =>
{
options.AddPolicy(CorsPolicy,
builder => builder
.WithOrigins("https://localhost:7021","https://localhost:44414")
//Note: The URL must be specified without a trailing slash (/).
.AllowAnyMethod()
.AllowAnyHeader()
.AllowCredentials());
});
Now I'm in the middleware territory...as you know, order matters in your middleware! In 5.0 you could add IAntiforgery to your constructor and DI would handle the rest. In program.cs you don't have that luxury. Fortunately you can just grab it out of your services collection and you see that in the following code.
//==============formerly Startup.Configure=====================
WebApplication app = builder.Build();
app.UseAuthentication();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseAuthorization();
app.UseCookiePolicy();
app.UseCors(CorsPolicy);
IAntiforgery? antiforgery = app.Services.GetRequiredService<IAntiforgery>();
Now when I'm setting up my endpoint routing. Found out that UseRouting and Use.Endpoints are married at the hip and need to be paired.
I also create a protected route "/auth" (protected by my authorization policy) to grab the antiforgery request token generated when we added it in the services collection. So this header won't be persisted from request to request like a cookie would. The minimal API allows me to create a route without creating the controller and action in a separate controller class.
app.UseEndpoints(endpoints =>
{
endpoints.MapGet("/auth", context =>
{
var tokens = antiforgery.GetAndStoreTokens(context);
context.Response.Headers.Append("XYZ", tokens.RequestToken!);
return Task.FromResult(Results.Ok());
}).RequireAuthorization("AuthenticatedOnly");
endpoints.MapControllerRoute(
name: "default"
pattern: "{controller}/{action=Index}/{id?}");
});
My React front end will use a fetch get request to get the token from the headers collection and then stick into a second post request and voila it works.
BTW, React doesn't provide Antiforgery functionality out of the box like Angular, in step with it's minimalist API ethos.
The action I'm posting to looks like this:
[HttpPost]
[Authorize(Policy="AuthenticatedOnly")]
[ValidateAntiForgeryToken]
public string Update()
I fully realize there are other ways to do this.

How do I add AllowAnonymousAttribute to a route/request from custom midleware?

I have an ASP.NET Core website that is build with static files and ASP.NET Core routing middleware, so no MVC.
I requiring all request to be authenticated by default.
services.AddAuthorization(options =>
{
options.FallbackPolicy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
});
I have a mix of middlewares and routing configuration - but how do I specify that some routes of the static files and my custom middleware needs to Allow Anonymous?
Is there a way to app.Use(...) and tell the context that authorization has been applied and it should not use the fallback? So my custom middlewares can be set up correctly or am I required to set everything up with endpoint routing and added metadata to the routes?
app.UseAuthentication();
app.UseAuthorization();
app.UseMiddleware<NextJSMiddleware>();
app.UseDefaultFiles();
app.UseStaticFiles();
app.UseEndpoints(config =>
{
///This is for detecting whether the application process has crashed/deadlocked. If a liveness probe fails, app can be stopped/restarted, or create a new one.
config.MapHealthChecks("/.well-known/live", new HealthCheckOptions { Predicate = _ => false }).WithMetadata(new AllowAnonymousAttribute()); ;
///Readiness probe. This is for detecting whether the application is ready to handle requests.
config.MapHealthChecks("/.well-known/ready").WithMetadata(new AllowAnonymousAttribute());
config.MapEAVFrameworkRoutes();
config.MapPost("/.auth/login/passwordless", async (httpcontext) =>
{
...
}).WithMetadata(new AllowAnonymousAttribute());
config.MapGet("/.auth/login/passwordless/callback", async (httpcontext) =>
{
...
});
});

OAuth Implementation in ASP.NET Core using Swagger

I want to implement OAuth in my web application and for that I added the following code in my startup.cs
public static IServiceCollection AddSwaggerDocumentation(this IServiceCollection services)
{
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "CombiTime API v1.0", Version = "v1" });
c.AddSecurityDefinition("OAuth2", new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
AuthorizationCode = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri("http://localhost:4200/login"),
TokenUrl = new Uri("http://localhost:4200/connect/token")
}
}
});
c.OperationFilter<AuthorizeOperationFilter>();
c.AddSecurityRequirement(new OpenApiSecurityRequirement{
{
new OpenApiSecurityScheme{
Reference = new OpenApiReference{
Id = "Bearer", //The name of the previously defined security scheme.
Type = ReferenceType.SecurityScheme
}
},new List<string>()
}
});
});
return services;
}
public static IApplicationBuilder UseSwaggerDocumentation(this IApplicationBuilder app)
{
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Versioned API v1.0");
c.DocumentTitle = "Title Documentation";
c.DocExpansion(DocExpansion.None);
c.RoutePrefix = string.Empty;
c.OAuthClientId("combitimeapi_swagger");
c.OAuthAppName("Combitime API");
c.OAuthUsePkce();
});
return app;
}
and the AuthorizeOperationFilter Code is as follows :
public void Apply(OpenApiOperation operation, OperationFilterContext context)
{
// Since all the operations in our api are protected, we need not
// check separately if the operation has Authorize attribute
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[] {"combitimeapi"}
}
};
}
By using this code, I get an "Authorize" button on my swagger UI and when I click that button I am redirecting to my login page(front end based on angular). So I gave my AuthorizationUrl as http://localhost:4200/login and then when I am redirected to login page, I login with valid credentials, I have used jwt token for login and for that I added the following code in my startup.cs
services.AddAuthentication(x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddJwtBearer(x =>
{
x.RequireHttpsMetadata = false;
x.SaveToken = true;
x.TokenValidationParameters = new TokenValidationParameters
{
ValidateIssuerSigningKey = true,
IssuerSigningKey = new SymmetricSecurityKey(key),
ValidateIssuer = false,
ValidateAudience = false
};
});
I want to redirect back to the swagger UI after I login with valid credentials but the problem is that I am being redirected to the dashboard after I login. Please help me or let me know what I am doing wrong.
The url that is being formed after I am redirected to login page from swagger is :
http://localhost:4200/login?response_type=code&client_id=combitimeapi_swagger&redirect_uri=http:%2F%2Flocalhost:61574%2Foauth2-redirect.html&state=V2VkIEZlYiAxNyAyMDIxIDIyOjU3OjQ2IEdNVCswNTMwIChJbmRpYSBTdGFuZGFyZCBUaW1lKQ%3D%3D&code_challenge=mT0amBTJgczCZmNSZAYVfjzzpaTiGb68XlyR3RNHuas&code_challenge_method=S256
My front-end is running on port 4200.
My swagger is running on port 61574.
But I am not being redirected to swagger UI after putting in valid credentials
Please help me.
First, let me add some details to your picture:
You have two applications, one with API (based on ASP.NET Core) and one with frontend UI (Angular, but it doesn't matter), and, it's important, with authorization/authentication functions.
You use .NETCore 3.1
You configure an authorization for swagger that means any call from swagger UI page will use given authorization parameters.
So, for API application we have to add a class that has helper methods configuring our swagger:
public static class ServiceCollectionExtensions
{
public static IServiceCollection AddSwaggerDocumentation(this IServiceCollection services)
{
services.AddSwaggerGen(c =>
{
c.SwaggerDoc("v1", new OpenApiInfo { Title = "CombiTime API v1.0", Version = "v1" });
c.AddSecurityDefinition(
"oauth2",
new OpenApiSecurityScheme
{
Type = SecuritySchemeType.OAuth2,
Flows = new OpenApiOAuthFlows
{
AuthorizationCode = new OpenApiOAuthFlow
{
AuthorizationUrl = new Uri("https://lvh.me:4201/connect/authorize"),
TokenUrl = new Uri("https://lvh.me:4201/connect/token"),
Scopes = new Dictionary<string, string> {
{ "combitimeapi", "Demo API" }
}
}
}
});
c.OperationFilter<AuthorizeOperationFilter>();
c.AddSecurityRequirement(
new OpenApiSecurityRequirement
{
{
new OpenApiSecurityScheme{
Reference = new OpenApiReference{
Id = "oauth2", //The name of the previously defined security scheme.
Type = ReferenceType.SecurityScheme
}
},
new List<string>()
}
});
});
return services;
}
public static IApplicationBuilder UseSwaggerDocumentation(this IApplicationBuilder app)
{
app.UseSwagger();
app.UseSwaggerUI(c =>
{
c.SwaggerEndpoint("/swagger/v1/swagger.json", "Versioned API v1.0");
c.DocumentTitle = "Title Documentation";
c.DocExpansion(DocExpansion.None);
c.RoutePrefix = string.Empty;
c.OAuthClientId("combitimeapi_swagger");
c.OAuthAppName("Combitime API");
c.OAuthScopeSeparator(",");
c.OAuthUsePkce();
});
return app;
}
}
Please, pay attention to the AuthorizationUrl property and to the TokenUrl property. The AuthorizationUrl property should be pointed to our OAuth2 server authorization endpoint. Please, keep in mind that authorization endpoint and logon page are different endpoints. We could get all-known endpoints for our frontend application by visiting the url: https://lvh.me:4201/.well-known/openid-configuration in case our application uses ASP.NET Core with IdentityServer.
Next, Startup.cs of our API application should contain:
public void ConfigureServices(IServiceCollection services)
{
// ... some your code ...
services.AddSwaggerDocumentation();
services.AddAuthentication("Bearer")
.AddIdentityServerAuthentication("Bearer", options =>
{
options.ApiName = "combitimeapi";
options.Authority = "https://lvh.me:4201";
});
// ... some your code ...
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
// ... some your code ...
app.UseSwaggerDocumentation();
app.UseRouting();
app.UseAuthorization();
// ... some your code ...
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
Please, do not forget to add attribute [Authorize] to all your controllers, because your AuthorizeOperationFilter assumes that's done.
Let's look for required changes for our frontend & authorize part. You should configure some certain things, like:
CORS policy
Awailable API clients (one is your Angular UI and another one is API application)
Awailable API resources
Authentication & authorization methods
The class Startup.cs should contain:
public void ConfigureServices(IServiceCollection services)
{
// ... some your code ...
services.AddCors(policies => {
policies.AddDefaultPolicy(builder => {
builder.AllowAnyHeader().AllowAnyMethod().AllowAnyOrigin();
});
});
services.AddIdentityServer()
.AddApiAuthorization<ApplicationUser, ApplicationDbContext>(options => {
options.Clients.AddIdentityServerSPA("forntend", cfg => {});
options.Clients.AddNativeApp("combitimeapi_swagger", cfg => {
cfg
.WithRedirectUri("https://lvh.me:5001/oauth2-redirect.html")
.WithScopes("combitimeapi");
});
options.ApiResources.AddApiResource("combitimeapi", cfg => {
cfg.WithScopes("combitimeapi");
});
})
.AddApiResources();
services
.AddAuthentication(
x =>
{
x.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
x.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
})
.AddIdentityServerJwt();
// ... some your code ...
}
I use here .AddIdentityServerJwt() instead of your's .AddJwtBearer(...) because I don't have your keys and other specific options.
The frontend application is configured to use ports 4201 for HTTPS and 4200 for HTTP, the API application is configured to use ports 5001 for HTTPS and 5000 for HTTP.
Now you can run both applications and go to the page https://lvh.me:5001/index.html and press the button 'Authorize' to get a dialog like:
Enter you secret, mark scope and press 'Authorize' and, after you authenticate yourself you will get:
If you do not get a successful result, please check log of the frontend application, usually it contains error that could help you to find out a problem.
Hope text above will help you.
If you look at the OAuth Web-site the case is described as Per-Request Customization
Per-Request Customization
Often times a developer will think that they need to be able to use a
different redirect URL on each authorization request, and will try to
change the query string parameters per request. This is not the
intended use of the redirect URL, and should not be allowed by the
authorization server. The server should reject any authorization
requests with redirect URLs that are not an exact match of a
registered URL.
If a client wishes to include request-specific data in the redirect URL, it can > instead use the “state” parameter to store data that will be included after the > user is redirected. It can either encode the data in the state parameter itself, or use the state parameter as a session ID to store the state on the server.
I hope that helps you in your quest.
Source: https://www.oauth.com/oauth2-servers/redirect-uris/redirect-uri-registration/
There may be more than one problem with the Startup code, more properly in the AddSwaggerGen.
Configuration of the Identity Provider:
Independently of the redirect, are you able to get an access token, or are you getting some kind of error, for example in the request or in the Identity Provider itself?
Please note that the client configuration that you provide in Swagger must match the configuration in the Identity Provider. You seem to be following Scott Brady's example; we can observe that all his Swagger's startup configuration follows the information he has in the Identity Server (here).
Set the token in the calls to the API:
Moreover, even if you are getting the token, I think you are not setting it in the subsequent calls from Swagger to the API itself.
The AddSecurityDefinition and the AddSecurityRequirement or the AuthorizeOperationFilter typically mention at least one scheme with the same identifier, since the first method defines the way that Swagger is authenticating and the second/third define the way that the calls to the API are authenticated (so, they must reference each other). However, you are using different IDs in all the three methods - "OAuth2", "Bearer" and "oauth2" -, so none of them is linked.
I don't fully know your application, but I believe you could actually be using only one of the AddSecurityRequirement or the AuthorizeOperationFilter, since they are both specifying security requirements. The most important would be to reference the ID of the SecurityDefinition (in your case, "OAuth2").
Scott's example, in fact, only uses the AuthorizeCheckOperationFilter and uses the same ID for the OpenApiSecurityScheme that was previously registered in the AddSecurityDefinition - in his case, "oauth2", but any name/string could be used.

No Webpage was found for the address Azurewebsite

I'm getting this
No web page was found for the web address: https://limbu.azurewebsites.net/
Everything worked fine in development but after publishing I get this error.
I can navigate to Views by typing in url but not the index page. I'm also not able to perform Register and login functions from controller in published website.
My Startup
public void ConfigureServices(IServiceCollection services)
{
services.AddRazorPages();
if (Environment.GetEnvironmentVariable("ASPNETCORE_ENVIRONMENT") == "Production")
{
services.Configure<IServiceProvider>(options => {
options.GetService<AppDbContext>().Database.Migrate();
}).AddDbContextPool<AppDbContext>(options => {
options.UseSqlServer(_config.GetConnectionString("AzureSqlConnection"));
//options.EnableSensitiveDataLogging(true);
});
}
else
{
services.AddDbContextPool<AppDbContext>(options => options.UseSqlServer(_config.GetConnectionString("Connection")));
}
services.AddIdentity<ApplicationUser, IdentityRole>(options =>
{
options.SignIn.RequireConfirmedEmail = true;
}).AddEntityFrameworkStores<AppDbContext>()
.AddDefaultTokenProviders();
services.AddTransient<IMailService, SendGridMailService>();
services.AddHostedService<TimedHostedServices>();
services.AddScoped<IGetGlobalCovidData, GetGlobalCovidData>();
services.AddMvc(config =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
}).AddJsonOptions(options => options.JsonSerializerOptions.PropertyNamingPolicy = null);
services.AddAntiforgery(o => o.HeaderName = "XSRF-TOKEN");
//ServicePointManager.ServerCertificateValidationCallback += //This code is security risk as it validates all certificates
// (sender, certificate, chain, errors) => //Not to be used for production and used this instance as I trust the
// { //The site I'm pulling data from
// return errors == SslPolicyErrors.None;
// };
//services.AddLogging(loggingBuilder => { //This code is security risk as it displays all sensitive data
// loggingBuilder.AddConsole() //Not recommended for production
// .AddFilter(DbLoggerCategory.Database.Command.Name, LogLevel.Information);
// loggingBuilder.AddDebug();
//});
}
// 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();
}
else
{
app.UseExceptionHandler("/Error");
}
app.UseStaticFiles();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapRazorPages();
endpoints.MapDefaultControllerRoute();
});
}
Please advise me on what I should provide for debugging.
my .cshtml build action is -content and donotcopy
.cs build action is -c# compiler and donotcopy
For the users who are getting the same issue, There could be different root causes for 404 error messages. Sub-status codes will help you understand the issue better.
You may check whether the site files are deployed correctly or not through Kudu Console. Also, suggest you Enable diagnostics logging for web apps in Azure App Service incase if you haven’t enabled earlier to check the complete error details and root cause.

The OpenIddict ASP.NET Core server cannot be used as the default scheme handler

I'm trying OpenIddict 3.0. I followed the steps in the documentation, created an Authorize controller, and added a test application. When I try to run I get this exception:
The OpenIddict ASP.NET Core server cannot be used as the default
scheme handler. Make sure that neither DefaultAuthenticateScheme,
DefaultChallengeScheme, DefaultForbidScheme, DefaultSignInScheme,
DefaultSignOutScheme nor DefaultScheme point to an instance of the
OpenIddict ASP.NET Core server handler
I cannot find what I'm doing wrong.
Here is my Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddDbContext<ApplicationDbContext>(options =>
{
// Configure the context to use Microsoft SQL Server.
options.UseInMemoryDatabase("Identity");
// Register the entity sets needed by OpenIddict.
// Note: use the generic overload if you need
// to replace the default OpenIddict entities.
options.UseOpenIddict<Guid>();
});
AddIdentityCoreServices(services);
services.AddOpenIddict()
// Register the OpenIddict core components.
.AddCore(options =>
{
// Configure OpenIddict to use the Entity Framework Core stores and models.
options.UseEntityFrameworkCore()
.UseDbContext<ApplicationDbContext>()
.ReplaceDefaultEntities<Guid>();
})
// Register the OpenIddict server components.
.AddServer(options =>
{
// Enable the token endpoint (required to use the password flow).
options.SetTokenEndpointUris("/connect/token");
// Allow client applications to use the grant_type=password flow.
options.AllowPasswordFlow();
// Mark the "email", "profile" and "roles" scopes as supported scopes.
//options.RegisterScopes(OpenIddictConstants.Scopes.Email,
// OpenIddictConstants.Scopes.Profile,
// OpenIddictConstants.Scopes.Roles);
// Accept requests sent by unknown clients (i.e that don't send a client_id).
// When this option is not used, a client registration must be
// created for each client using IOpenIddictApplicationManager.
options.AcceptAnonymousClients();
// Register the signing and encryption credentials.
options.AddDevelopmentEncryptionCertificate()
.AddDevelopmentSigningCertificate();
// Register the ASP.NET Core host and configure the ASP.NET Core-specific options.
options.UseAspNetCore()
.EnableAuthorizationEndpointPassthrough() // Add this line.
.EnableTokenEndpointPassthrough()
.DisableTransportSecurityRequirement(); // During development, you can disable the HTTPS requirement.
})
// Register the OpenIddict validation components.
.AddValidation(options =>
{
// Import the configuration from the local OpenIddict server instance.
options.UseLocalServer();
// Register the ASP.NET Core host.
options.UseAspNetCore();
});
// ASP.NET Core Identity should use the same claim names as OpenIddict
services.Configure<IdentityOptions>(options =>
{
options.ClaimsIdentity.UserNameClaimType = OpenIdConnectConstants.Claims.Name;
options.ClaimsIdentity.UserIdClaimType = OpenIdConnectConstants.Claims.Subject;
options.ClaimsIdentity.RoleClaimType = OpenIdConnectConstants.Claims.Role;
});
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = OpenIddictServerAspNetCoreDefaults.AuthenticationScheme;
});
services.AddControllers();
}
// 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.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication();
app.UseAuthorization();
app.UseEndpoints(endpoints =>
{
endpoints.MapControllers();
});
}
private static void AddIdentityCoreServices(IServiceCollection services)
{
var builder = services.AddIdentityCore<ApplicationUser>();
builder = new IdentityBuilder(
builder.UserType,
typeof(ApplicationRole),
builder.Services);
builder.AddRoles<ApplicationRole>()
.AddEntityFrameworkStores<ApplicationDbContext>()
.AddDefaultTokenProviders()
.AddSignInManager<SignInManager<ApplicationUser>>();
}
Please assist me on what I'm doing wrong.
I finally figured out where I went wrong. #Train Thanks for pointing me in the right direction.
changing the services.AddAuthentication(...) from
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = OpenIddictServerAspNetCoreDefaults.AuthenticationScheme;
});
to
services.AddAuthentication(options =>
{
options.DefaultScheme = OpenIddict.Validation.AspNetCore.OpenIddictValidationAspNetCoreDefaults.AuthenticationScheme;
});
What's you're method of authentication? Cookie? JWT?
You need to change this line of code. You can't set OpenIddictServerAspNetCoreDefaults.AuthenticationScheme; as the default scheme
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = OpenIddictServerAspNetCoreDefaults.AuthenticationScheme;
});
Default Authentication Scheme
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme);
or overload
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
});
Here are the docs on Authentication with a lot more to read up on.

Categories

Resources