Implement multiple authorization in .net 6 web API - c#

I have auth0 authentication implemented for my webAPIs. But now due to some requirement change few of APIs need to be authorized with another scheme. So I need below specified different authorization schemes to authorize my API
Auth0 scheme (already authorizing api's)
Azure AD B2C
I have implemented Azure AD B2C and which is working fine when used alone but when I am trying to add it enable it with a previous scheme it is causing issues.
public static IServiceCollection AddSecurityPolicy(this IServiceCollection services, ConfigurationManager config)
{
const string ClientPortalScheme = "ClientPortalBearerScheme";
//from https://auth0.com/blog/securing-aspnet-minimal-webapis-with-auth0/
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = JwtBearerDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = JwtBearerDefaults.AuthenticationScheme;
}).AddJwtBearer(options =>
{
options.Authority = config["AuthenticationSettings:Domain"];
options.Audience = config["AuthenticationSettings:Audience"];
}).AddJwtBearer(ClientPortalScheme, ClientPortalScheme, options =>
{
options.Authority = config["AzureADB2CSettings:Domain"];
options.Audience = config["AzureADB2CSettings:Tenant"];
});
//services.AddMicrosoftIdentityWebApiAuthentication(config, "AzureADB2CSettings");
//By default, require an authenticated user
//Only one JWT bearer authentication is registered with the default authentication scheme JwtBearerDefaults.AuthenticationScheme.
//Additional authentication has to be registered with a unique authentication scheme.
//see https://docs.microsoft.com/en-us/aspnet/core/security/authorization/limitingidentitybyscheme?view=aspnetcore-6.0
services.AddAuthorization(options =>
{
var defaultAuthorizationPolicyBuilder = new AuthorizationPolicyBuilder(
JwtBearerDefaults.AuthenticationScheme,
ClientPortalScheme);
defaultAuthorizationPolicyBuilder =
defaultAuthorizationPolicyBuilder.RequireAuthenticatedUser();
options.DefaultPolicy = defaultAuthorizationPolicyBuilder.Build();
});
return services;
}
This is how my code looks like.
Issue is when I am calling my endpoint it says
Unable to obtain configuration from: 'https://mydomain.auth0.com/.well-known/openid-configuration'.
---> System.IO.IOException: IDX20804: Unable to retrieve document from: 'https://mydomain.auth0.com/.well-known/openid-configuration'.
Please let me know if any information is required int this regard

I tried to reproduce the issue in my environment.
It occurs when app config is not receiving OpenIDmeta data properly
The issue was due to TLS configuration in my case as TLS 1.1 or TLS 1.0 are depreciated.
Please make sure to set TLS to 1.2 or greater.
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12;
Startup.cs
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
IdentityModelEventSource.ShowPII = true;
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
......
System.Net.ServicePointManager.SecurityProtocol = System.Net.SecurityProtocolType.Tls12 | SecurityProtocolType.Ssl3;;
....
app.UseAuthentication();
app.UseAuthorization();
....
}
Please make sure your backend API refers to /.well-known/openid-configuration
Or check this way.
app.UseOpenIdConnectAuthentication(new OpenIdConnectAuthenticationOptions
{
MetadataAddress = "http[s]://{IdentityServer}/.well-known/openid-configuration"
...
});
In AzureAdB2c make sure to set the Authority or (Domain and instance ) property correctly .
Authority being the combination of Instance and domain
Appsettings.json
Authority : https://[yourb2ctenant}.b2clogin.com/{Configuration["AzureAdB2C:Tenant"]}/{Configuration["AzureAdB2C:Policy"]}/v2.0
Also see if Instance can be https://<tenant>.b2clogin.com/tfp/
Domain : <b2ctenant>.onmicrosoft.com
Then the authentication can be carried on successfully:

Related

Trouble authorizing .NET 5 Web API reference tokens to IdentityServer3 using IdentityServer4.AccessTokenValidation package

Server
Using IdentityServer3 for client/application authorization.
Using IdentityAdmin to edit clients/scopes via GUI.
Created a new Client for the API, added a SharedSecret and api scope.
API / Client
Has 2 GET endpoints.
Uses the IdentityServer4.AccessTokenValidation NuGet package.
Configuration should be simple:
Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddControllers(c => {
var policy = ScopePolicy.Create("api");
c.Filters.Add(new AuthorizeFilter(policy));
});
services.AddAuthentication(IdentityServerAuthenticationDefaults.AuthenticationScheme)
.AddIdentityServerAuthentication(options => {
options.Authority = "{base url of identity server}";
options.ApiName = ""; // not sure what this is? client id from identity server?
options.ApiSecret = ""; // should this be the hashed password?
options.LegacyAudienceValidation = true;
});
services.AddSwaggerGen(c => {
c.SwaggerDoc("v1", new OpenApiInfo { Title = "MarvalAPI", Version = "v1" });
});
RegisterServices(services);
}
public void Configure(IApplicationBuilder app, IWebHostEnvironment env)
{
if (env.IsDevelopment()) {
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(c => c.SwaggerEndpoint("/swagger/v1/swagger.json", "MarvalAPI v1"));
}
app.UseHttpsRedirection();
app.UseRouting();
app.UseAuthentication(); //this is added, everything else is by default
app.UseAuthorization();
app.UseEndpoints(endpoints => {
endpoints.MapControllers();
});
}
Testing:
GET client reference token from identity "/connect/token" endpoint
GET API's endpoint with added header "Authorization: Bearer {token}"
Receive 401 Unauthorized
Things I have tried:
Different Startup.cs configurations
Tried validating token via identity "/connect/accesstokenvalidation" endpoint, token is valid.
Different apiname/apisecret values, because not 100% sure what they have to be.
Googled to no avail
I am at a loss here, am I doing something totally wrong? Is this just a compatibility issue? Or am I just not understanding anything at all? Seems like clear documentation is scarce and users have to draw out information.
Sources used
https://github.com/IdentityServer/CrossVersionIntegrationTests/blob/main/src/CoreApiIdSrv3/Startup.cs
https://github.com/IdentityServer/IdentityServer4.AccessTokenValidation
IdentityServer3 documentation
SO / github/identityserver3 threads.
Well, some time after making this post I figured it out.
options.ApiName = "";
options.ApiSecret = "";
ApiName is the name of the scope which the client uses, so it this case the value should be api.
ApiSecret is the PRE-HASHED value of the scope secret.
e.g.
if secret value is "test" and it's SHA256 value is 9f86d081884c7d659a2feaa0c55ad015a3bf4f1b2b0b822cd15d6c15b0f00a08, then ApiSecret value should be test
So, after figuring this out, the above options config should look like this:
options.ApiName = "api";
options.ApiSecret = "test";
Note: SHA512 works as well.
To me this seems like a major naming issue.
I solved this after analysing this VS solution:
https://github.com/IdentityServer/CrossVersionIntegrationTests

Asp.net Blazor server app fails to redirect in kubernetes with OIDC

We have a .NET 5 (Blazor Server) app running in Azure Kubernetes that uses OpenID Connect to authenticate with a 3rd party. The app is running behind Ingress. Ingress uses https. The app is only http. After we authenticate with OIDC and get redirected back to /signin-oidc, we get a .NET error that we haven't been able to solve.
warn: Microsoft.AspNetCore.Http.ResponseCookies[1]
The cookie '.AspNetCore.OpenIdConnect.Nonce.CfDJ8EYehvsxFBVNtGDsitGDhE8K9FHQZVQwqqr1YO-zVntEtRgpfb_0cHpxfZp77AdGnS35iGRKYV54DTgx2O6ZO_3gq98pbP_XcbHnJmBDtZg2g5hhPakTrRirxDb-Qab0diaLMFKdmDrNTqGkVmqiGWpQkSxcnmxzVGGE0Cg_l930hk6TYgU0qmkzSO9WS16UBOYiub32GF4I9_qPwIiYlCq5dMTtUJaMxGlo8AdAqknxTzYz4UsrrPBi_RiWUKaF6heQitbOD4V-auHmdXQm4LE' has set 'SameSite=None' and must also set 'Secure'.
warn: Microsoft.AspNetCore.Http.ResponseCookies[1]
The cookie '.AspNetCore.Correlation.MMrYZ2WKyYiV4hMC6bhQbGZozpubcF2tYsKq748YH44' has set 'SameSite=None' and must also set 'Secure'.
warn: Microsoft.AspNetCore.Authentication.OpenIdConnect.OpenIdConnectHandler[15]
'.AspNetCore.Correlation.MMrYZ2WKyYiV4hMC6bhQbGZozpubcF2tYsKq748YH44' cookie not found.
fail: Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware[1]
An unhandled exception has occurred while executing the request.
System.Exception: An error was encountered while handling the remote login.
---> System.Exception: Correlation failed.
--- End of inner exception stack trace ---
at Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler`1.HandleRequestAsync()
at Microsoft.AspNetCore.Authentication.AuthenticationMiddleware.Invoke(HttpContext context)
at Microsoft.AspNetCore.Diagnostics.ExceptionHandlerMiddleware.<Invoke>g__Awaited|6_0(ExceptionHandlerMiddleware middleware, HttpContext context, Task task)
public class Startup
{
private static readonly object refreshLock = new object();
private IConfiguration Configuration;
private readonly IWebHostEnvironment Env;
public Startup(IConfiguration configuration, IWebHostEnvironment env)
{
Console.WriteLine($"LogQAApp Version: {Assembly.GetExecutingAssembly().GetName().Version}");
// We apparently need to set a CultureInfo or some of the Razor pages dealing with DateTimes, like LogErrorCountByTime fails with JavaScript errors.
// I wanted to set it to CultureInvariant, but that wouldn't take. Didn't complain, but wouldn't actually set it.
CultureInfo.DefaultThreadCurrentCulture = new CultureInfo("en-US");
CultureInfo.DefaultThreadCurrentUICulture = new CultureInfo("en-US");
Configuration = configuration;
Env = env;
}
// 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.Configure<ForwardedHeadersOptions>(options =>
{
options.ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto;
});
Encoding.RegisterProvider(CodePagesEncodingProvider.Instance); // Needed for 1252 code page encoding.
Syncfusion.Licensing.SyncfusionLicenseProvider.RegisterLicense("");
services.AddSignalR(e =>
{
e.MaximumReceiveMessageSize = 102400000;
});
services.AddBlazoredSessionStorage();
services.AddCors();
services.AddSyncfusionBlazor();
services.AddRazorPages();
services.AddServerSideBlazor();
services.AddHttpContextAccessor();
ServiceConfigurations.LoadFromConfiguration(Configuration);
#region Authentication
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(
options =>
{
options.Events = GetCookieAuthenticationEvents();
}
)
.AddOpenIdConnect("SlbOIDC", options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = Configuration["SlbOIDC:Authority"];
if (Env.IsDevelopment())
{
options.ClientId = Configuration["SlbOIDC:ClientID"];
options.ClientSecret = Configuration["SlbOIDC:ClientSecret"];
}
else
{
options.ClientId = Configuration.GetValue<string>("slbclientid");
options.ClientSecret = Configuration.GetValue<string>("slbclientsecret");
}
options.ResponseType = OpenIdConnectResponseType.Code;
options.UsePkce = true;
options.SaveTokens = true;
options.ClaimsIssuer = "SlbOIDC";
// Azure is communicating to us over http, but we need to tell SLB to respond back to us on https.
options.Events = new OpenIdConnectEvents()
{
OnRedirectToIdentityProvider = context =>
{
Console.WriteLine($"Before: {context.ProtocolMessage.RedirectUri}");
context.ProtocolMessage.RedirectUri = context.ProtocolMessage.RedirectUri.Replace("http://", "https://");
Console.WriteLine($"After: {context.ProtocolMessage.RedirectUri}");
return Task.FromResult(0);
}
};
});
services.AddSession(options =>
{
options.Cookie.SameSite = SameSiteMode.None;
options.Cookie.SecurePolicy = CookieSecurePolicy.Always;
options.Cookie.IsEssential = true;
});
#endregion
services.AddScoped<BrowserService>();
services.AddSingleton<ConcurrentSessionStatesSingleton>();
services.AddSingleton<URLConfiguration>();
services.AddScoped<CircuitHandler>((sp) => new CircuitHandlerScoped(sp.GetRequiredService<ConcurrentSessionStatesSingleton>(), sp.GetRequiredService<BrowserService>(), sp.GetRequiredService<IJSRuntime>()));
services.AddScoped<SessionServiceScoped>();
services.AddScoped<LogEditorScoped>();
services.AddSingleton<ModalService>();
services.AddFlexor();
services.AddScoped<ResizeListener>();
services.AddScoped<ApplicationLogSingleton>();
services.AddScoped<LogChartsSingleton>();
services.AddScoped<CurveNameClassificationSingleton>();
services.AddScoped<HubClientSingleton>();
services.AddScoped((sp) => new LogAquisitionScopedService(
sp.GetRequiredService<URLConfiguration>(),
sp.GetRequiredService<HubClientSingleton>(),
sp.GetRequiredService<ApplicationLogSingleton>(),
sp.GetRequiredService<IConfiguration>(),
sp.GetRequiredService<SessionServiceScoped>(),
sp.GetRequiredService<AuthenticationStateProvider>(),
sp.GetRequiredService<IHttpContextAccessor>(),
sp.GetRequiredService<IJSRuntime>()
)
);
services.AddScoped<UnitSingleton>();
services.AddServerSideBlazor().AddCircuitOptions(options => { options.DetailedErrors = true; });
services.AddScoped<TimeZoneService>();
services.AddHostedService<ExcelBackgroundService>();
services.AddHostedService<LogEditorBackgroundService>();
}
// 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.UseHsts();
}
//app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
app.UseCors();
app.UseAuthorization();
app.UseCookiePolicy();
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedFor | ForwardedHeaders.XForwardedProto
});
app.UseAuthentication();
if (!Env.IsDevelopment())
{
app.UseTrafficManager();
}
app.UseEndpoints(endpoints =>
{
endpoints.MapDefaultControllerRoute();
endpoints.MapBlazorHub();
endpoints.MapFallbackToPage("/_Host");
});
}
private CookieAuthenticationEvents GetCookieAuthenticationEvents()
{
return new CookieAuthenticationEvents()
{
OnValidatePrincipal = context =>
{
lock (refreshLock)
{
if (context.Properties.Items.ContainsKey(".Token.expires_at"))
{
DateTime expire = DateTime.Parse(context.Properties.Items[".Token.expires_at"]);
if (expire.AddMinutes(-20) < DateTime.Now)
{
try
{
CloudAuthentication cloudAuthentication = new CloudAuthentication();
TokenResponse tokenResponse = cloudAuthentication.GetRefreshToken(context.Properties.Items[".Token.refresh_token"]);
context.Properties.Items[".Token.access_token"] = tokenResponse.access_token;
context.Properties.Items[".Token.refresh_token"] = tokenResponse.refresh_token;
context.Properties.Items[".Token.expires_at"] = DateTime.Now.AddSeconds(tokenResponse.expires_in).ToString();
context.ShouldRenew = true;
}
catch (Exception ex)
{
context.RejectPrincipal();
}
}
}
return Task.FromResult(0);
}
}
};
}
}
It's a good question - there are a couple of interesting points here that I've expanded on since they are related to SameSite cookies.
REVERSE PROXY SETUP
By default the Microsoft stack requires you to run on HTTPS if using cookies that require an SSL connection. However, you are providing SSL via a Kubernetes ingress, which is a form of reverse proxy.
The Microsoft .Net Core Reverse Proxy Docs may provide a solution. The doc suggests that you can inform the runtime that there is an SSL context, even though you are listening on HTTP:
app.Use((context, next) =>
{
context.Request.Scheme = "https";
return next();
});
I would be surprised if Microsoft did not support your setup, since it is a pretty mainstream hosting option. If this does not work then you can try:
Further searching around Blazor and 'reverse proxy hosting'
Worst case you may have to use SSL inside the cluster for this particular component, as Johan indicates
WIDER INFO - API DRIVEN OAUTH
Many companies want to develop Single Page Apps, but use a website based back end in order to manage the OAuth security. Combining serving of web content with OAuth security adds complexity. It is often not understood that the OAuth SPA security works better if developed in an API driven manner.
The below resources show how the SPA code can be simplified and in this example the API will issue cookies however it is configured. This would enable it to listen over HTTP inside the cluster (if needed) but to also issue secure cookies:
API driven OpenID Connect code
Curity Blog Post
WIDER INFO: SAMESITE COOKIES
It is recommended to use SameSite=strict as the most secure option, rather than SameSite=none. There are sometimes usability problems with the strict option however, which can cause cookies to be dropped after redirects or navigation from email links.
This can result in companies downgrading their web security to a less secure SameSite option. These problems do not occur when an API driven solution is used, and you can then use the strongest SameSite=strict option.

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.

OWIN how to configure /signout-oidc to remove cookies

I'm creating applications that are authorized by a personal run OIDC server.
The server is using Openiddict library, and the applications are using OWIN for the configuration. this is because the OIDC server is running on .Net core and the applications on .Net framework.
When trying to log out from these applications I redirect to the OIDC server /Account/Logout, this will in turn get all the logged in applications and open an iframe with the front channel logout url (/signout-oidc).
When logging out, it will give a 404 not found, meaning that the url "example.com/signout-oidc" has not been created.
The used libraries for the application are:
Microsoft.Owin.Security
Microsoft.Owin.Security.Cookies
Microsoft.Owin.Security.OpenIdConnect
app.SetDefaultSignInAsAuthenticationType(CookieAuthenticationDefaults.AuthenticationType);
app.Use(async (Context, next) =>
{
Debug.WriteLine("1 ==>request, before cookie auth");
await next.Invoke();
Debug.WriteLine("6 <==response, after cookie auth");
});
app.UseCookieAuthentication(new CookieAuthenticationOptions());
app.Use(async (Context, next) =>
{
Debug.WriteLine("2 ==>after cookie, before OIDC");
await next.Invoke();
Debug.WriteLine("5 <==after OIDC");
});
app.UseOpenIdConnectAuthentication(
new OpenIdConnectAuthenticationOptions()
{
ClientId = "application-client-id",
ClientSecret = "application-client-secret",
Scope = "openid profile email",
Authority = "personal-oidc-link",
AuthenticationMode = AuthenticationMode.Active,
ResponseType = OpenIdConnectResponseType.IdToken,
RedirectUri = "https://example.com/signin-oidc",
TokenValidationParameters = new TokenValidationParameters
{
NameClaimType = ClaimTypes.NameIdentifier
},
});
app.Use(async (Context, next) =>
{
Debug.WriteLine("3 ==>after OIDC, before leaving the pipeline");
await next.Invoke();
Debug.WriteLine("4 <==after entering the pipeline, before OIDC");
});
The server configuration:
services.AddOpenIddict()
.AddCore(options =>
{
// Configure OpenIddict to use the Entity Framework Core stores and entities.
options.UseEntityFrameworkCore()
.UseDbContext<DataContext>()
.ReplaceDefaultEntities<CompanyApplication, CompanyAuthorization, CompanyScope, CompanyToken, Guid>();
})
.AddServer(options =>
{
options.UseMvc();
options.UseJsonWebTokens();
options.AddEphemeralSigningKey("RS512");
// options.AddDevelopmentSigningCertificate();
if (this.environment.IsDevelopment())
{
options.DisableHttpsRequirement();
}
// Enable the authorization, logout, token and userinfo endpoints.
options
.EnableAuthorizationEndpoint("/oidc/authorize")
.EnableLogoutEndpoint("/Account/Logout")
.EnableTokenEndpoint("/oidc/token")
.EnableUserinfoEndpoint("/oidc/userinfo");
options
.AllowAuthorizationCodeFlow()
.AllowImplicitFlow()
.AllowRefreshTokenFlow();
options.RegisterClaims(
CompanyClaims.FriendlyName,
CompanyClaims.Email,
CompanyClaims.EmailVerified,
CompanyClaims.Sub,
CompanyClaims.Group,
CompanyClaims.GivenName,
CompanyClaims.MiddleName,
CompanyClaims.FamilyName);
// Mark the "email", "profile" and "roles" scopes as supported scopes.
options.RegisterScopes(
OpenIddictConstants.Scopes.Email,
OpenIddictConstants.Scopes.Profile,
OpenIddictConstants.Scopes.Roles);
});
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(
CookieAuthenticationDefaults.AuthenticationScheme,
o =>
{
o.AccessDeniedPath = "/Home/Denied";
o.LoginPath = "/Account/Login";
o.LogoutPath = "/Account/Logout";
o.Cookie.SameSite = SameSiteMode.Strict;
o.Cookie.Name = "session";
o.Cookie.Expiration = TimeSpan.FromHours(24);
o.ExpireTimeSpan = TimeSpan.FromHours(24);
});
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseAuthentication();
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseResponseCaching();
app.UseMvcWithDefaultRoute();
}
I expect that OWIN generates the /signout-oidc route, and when called it deletes the authentication cookie.
Edit: Added some more configuration files.
You need to use
PostLogoutRedirectUri = ""
I've no experience with the openiddict library here. But I'm using the IdentityServer library.
I've learned that in the OpenId Connect flow to remove the cookies using the FrontChannel logout you need to:
o.Cookie.SameSite = SameSiteMode.None;
By doing this, the GET request to /signout-oidc, initiated by your OpenId server will contain the authentication cookie of the user currently logging out. So, the middleware handling the /signout-oidc request knows which user is doing the logout and is able to logout that user.
You can use the Developer Tools in your browser (don't forget to enable 'Preserve log' when using Chrome) to find the GET /signout-oidc request and see if it contains the authentication cookie.
A second option is to choose using a backchannel logout flow, don't know if the openiddict supports this.

Getting Correlation failed exception during OIDC auth

Right now I am developing asp net core 2.0 web site and I am adding authorization.
I have existing auth server build using identity server 4. I added new client with Implicit grant type. When I run locally I am successfully redirected to identity server and than after login back to we site. But when I deployed web site I am getting
An unhandled exception occurred while processing the request.
Exception: Correlation failed.
Microsoft.AspNetCore.Authentication.RemoteAuthenticationHandler+<HandleRequestAsync>d__12.MoveNext()
when redirected back after login. What can case the issue ?
Btw, here is my startup
public void ConfigureServices(IServiceCollection services)
{
services.AddMvc();
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = "oidc";
})
.AddCookie(options =>
{
options.ExpireTimeSpan = TimeSpan.FromMinutes(60);
options.Cookie.Name = "mvcimplicit";
})
.AddOpenIdConnect("oidc", options =>
{
options.Authority = "https://identity.************.com/";
options.RequireHttpsMetadata = false;
options.ClientId = "mvc.client";
options.SaveTokens = true;
});
}
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
app.UseDeveloperExceptionPage();
app.UseStaticFiles();
app.UseAuthentication();
app.UseMvcWithDefaultRoute();
}
Do you have any ideas ?
May be you have some sort of cluster without exlicity defined data protection? Its potentially can produce behaviour like you describe.

Categories

Resources