No default authentication scheme has been set - c#

I am in the process of upgrading from IdentityServer4 1.x to IdentityServer4 2.0 which also means that i have upgraded the to .Net core 2.0 I am aware that there are a lot of breaking changes with this upgrade I have done it once before but for some reason I am stuck on this error.
warn: IdentityServer4.Startup[0]
No default authentication scheme has been set. Setting a default scheme is required.
warn: IdentityServer4.Startup[0]
No default authentication scheme has been set. Setting a default scheme is required.
The error appears in Configure after i call app.UseIdentityServer();
Configure method:
public void Configure(IApplicationBuilder app, ILoggerFactory loggerFactory)
{
if (Debugger.IsAttached)
loggerFactory.AddConsole(Configuration);
else
loggerFactory.AddConsoleJson(Configuration);
InitializeDatabase(app);
// Stops microsoft from overwriting claim types to their proprietary ones
JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear();
app.UseAuthentication();
app.UseCors(builder =>
builder.AllowAnyHeader()
.AllowAnyMethod()
.AllowAnyOrigin()
.AllowCredentials()
);
app.UseForwardedHeaders(new ForwardedHeadersOptions
{
ForwardedHeaders = ForwardedHeaders.XForwardedProto
});
app.UseDeveloperExceptionPage();
app.UseIdentityServer();
app.UseStaticFiles();
app.UseMvcWithDefaultRoute();
}
ConfigureServices method
public void ConfigureServices(IServiceCollection services)
{
var settingsSetup = Configuration.GetSection("Settings").Get<Settings>();
settingsSetup.XenaConnectionUrl = Configuration["XenaPath"];
services.AddSingleton(settingsSetup);
var idsConnectionString = Configuration.GetConnectionString("XenaIdentityConnection");
var xenaConnectionString = Configuration.GetConnectionString("XenaConnection");
var migrationsAssembly = typeof(Startup).GetTypeInfo().Assembly.GetName().Name;
services.AddDbContext<UserDbContext>(builder =>
builder.UseSqlServer(xenaConnectionString));
services.AddCors();
services.AddMvc();
services.TryAddScoped<UserManager, UserManager>();
services.TryAddScoped<SignInManager, SignInManager>();
services.TryAddSingleton(new XenaClient(Configuration));
services.AddTransient<Services.IClaimsService, XenaClaimsService>();
services.AddTransient<IProfileService, ProfileService>();
// Sms service setup
services.Configure<SmsOptions>(Configuration.GetSection("SmsOptions"));
services.AddTransient<ISMSService, SMSService>();
services.AddTransient<IResourceOwnerPasswordValidator, PasswordValidator>();
services.AddIdentityServer()
.AddSigningCredential(LoadCertificate())
// this adds the config data from DB (clients, resources)
.AddConfigurationStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(idsConnectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
})
// this adds the operational data from DB (codes, tokens, consents)
.AddOperationalStore(options =>
{
options.ConfigureDbContext = builder =>
builder.UseSqlServer(idsConnectionString,
sql => sql.MigrationsAssembly(migrationsAssembly));
})
.AddProfileService<ProfileService>();
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
});
services.AddAuthorization(options =>
{
options.AddPolicy("Supporter", policy => policy.RequireClaim("supporter"));
});
}
I am setting DefaultAuthenticateScheme so i cant really figure out what the problem is. I have resorted to digging around in the source code and it seams to have something to do with the validation I am obviously not adding something but i cant figure out what it is source

I found the problem. All the following does is initialise it. It doesnt actually add an authentication type.
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
});
The project I was using as reference had the following
services.AddAuthentication(options =>
{
options.DefaultAuthenticateScheme = IdentityConstants.ApplicationScheme;
options.DefaultChallengeScheme = IdentityConstants.ApplicationScheme;
})
.AddGoogle("Google", options =>
{
options.AccessType = "offline";
options.SignInScheme = IdentityConstants.ExternalScheme;
options.ClientId = Configuration.GetSection("Settings:GoogleClientId").Value;
options.ClientSecret = Configuration.GetSection("Settings:GoogleClientSecret").Value;
});
But my current project does not need google login at this time so i just removed that part. Which caused it to fail becouse no authecation type had been added.
I just removed the AddAuthentication part and everything is working now.

Related

Listening on many ports for mTLS ASP.NET Core

How can I setup listening multiple ports? On first port I want to have default app with https, on another I want to use HTTPS and require SSL based authentication with client certificates. How to do it? This is my current Startup.cs code:
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(kestrelOptions =>
{
kestrelOptions.ConfigureHttpsDefaults(httpOptions =>
{
httpOptions.ClientCertificateMode = ClientCertificateMode.AllowCertificate;
});
});
var services = builder.Services;
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, cfg =>
{
cfg.ReturnUrlParameter = "returnUrl";
cfg.LoginPath = "/account/login";
cfg.LogoutPath = "/account/logout";
})
.AddCertificate(CertificateAuthenticationDefaults.AuthenticationScheme, cfg =>
{
cfg.AllowedCertificateTypes = CertificateTypes.All;
cfg.RevocationMode = X509RevocationMode.Online;
});
services.AddControllersWithViews();
var app = builder.Build();
app.UseHttpsRedirection();
app.UseAuthentication();
app.UseAuthorization();
app.MapControllers();
app.Run();
My goal is to use Certificate authentication on some endpoints (and don't display certificate request e.g. for web explorer users) and not use delayed certificates.
I did it with kestrelOptions.ListenLocalhost:
var builder = WebApplication.CreateBuilder(args);
builder.WebHost.ConfigureKestrel(kestrelOptions =>
{
kestrelOptions.ListenLocalhost(8080, cfg =>
{
cfg.UseHttps(httpOptions =>
{
httpOptions.ClientCertificateMode = ClientCertificateMode.RequireCertificate;
});
});
kestrelOptions.ListenLocalhost(8081, cfg =>
{
cfg.UseHttps(httpOptions =>
{
httpOptions.ClientCertificateMode = ClientCertificateMode.NoCertificate;
});
});
});
Now one port is for mTLS (8080) and another don't require certificate! Works really nice.

Active Directory redirect_uri shows IP instead of DNS name. How to provide absolute CallbackPath in code while authenticating with Azure AD?

appSettings.json:
"CallbackPath": "/platform/signin-oidc",
This is what I get after deployment:
I think it is showing the Kubernetes IP where code is deployed. The application uses Azure front door to route requests (in case that helps). How do I fix this? Can I pass DNS name? Full callback path instead of relative path?
I found a similar post here
But unfortunately there aren't enough details in the answer posted by the OP.
startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options =>
{
options.AddPolicy("CorsPolicy",
builder => builder.AllowAnyOrigin()
.AllowAnyMethod()
.AllowAnyHeader());
});
services.Configure<OpenIdConnectOptions>(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.Events.OnRedirectToIdentityProviderForSignOut = async context =>
{
Console.WriteLine("intercepted");
};
});
var azureAd = new AzureAd();
Configuration.GetSection("AzureAd").Bind(azureAd);
services.AddControllersWithViews();
services.AddAuthentication(options =>
{
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = OpenIdConnectDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, o =>
{
o.Cookie.SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None;
})
.AddOpenIdConnect(OpenIdConnectDefaults.AuthenticationScheme, options =>
{
options.SignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.Authority = $"https://login.microsoftonline.com/{azureAd.TenantId}";
options.ClientId = azureAd.ClientId;
options.ResponseType = OpenIdConnectResponseType.Code;
options.ResponseType = OpenIdConnectResponseType.IdToken;
options.SaveTokens = true;
options.Scope.Add("profile");
options.Scope.Add("openid");
options.Scope.Add("offline_access");
options.ClientSecret = azureAd.ClientSecret;
options.CallbackPath = azureAd.CallbackPath; // POINT OF INTEREST
});
}
You can override the redirect uri before redirecting to the IdP like that:
options.Events.OnRedirectToIdentityProvider = (context) =>
{
context.ProtocolMessage.RedirectUri = "full redirect url with front door domain";
return Task.FromResult(0);
};

Authenticating tokens from multiple sources (e.g Cognito and Azure)

We're working on an API that allows users authenticating through a number of different providers. The individual providers are not an issue, but using them together is proving to be a challenge.
It seems that adding more than 1 provider throws a InvalidOperationException with "Scheme already exists: Bearer" when the application starts up.
Below is the ConfigureServices function from Startup.cs
public void ConfigureServices(IServiceCollection services)
{
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer(options =>
{
options.Authority = "Value";
options.Audience = "Value";
})
.AddMicrosoftIdentityWebApi(options =>
{
Configuration.Bind("AzureAdB2C", options);
options.TokenValidationParameters.NameClaimType = "name";
},
options => { Configuration.Bind("AzureAdB2C", options); });
services.AddControllers();
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder(
JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build();
});
}
I'm using the Microsoft example for authenticating with Azure AD as a starting point. Removing either the AddJwtBearer or AddMicrosoftIdentityWebApi calls works fine, but I need to configure both providers for our use-case.
Is there a way to do this with .NET Core 3.1 or up?
We can't register 2 authentications under same scheme name. So we need to register the 2 authentication schemes with different name(or one with default and another with a scheme name)
In my case I am registering 2 authentication schemes:
My own JWT scheme with our app name "MyAppName",
Azure AD authentication with JWT default scheme JwtBearerDefaults.AuthenticationScheme, as I was not able to add it with custom scheme name.
I was able to make it work with the following configuration:
services.AddAuthentication(JwtBearerDefaults.AuthenticationScheme)
.AddJwtBearer("MyAppName",options =>
{
options.Authority = "Value";
options.Audience = "Value";
})
.AddMicrosoftIdentityWebApi(Configuration, "AzureAd");
and Authorization configuration:
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder(
"MyAppName",
JwtBearerDefaults.AuthenticationScheme)
.RequireAuthenticatedUser()
.Build();
});

HttpContext.SignInAsync() doesn't authenticate the user

I have been trying to create a custom login feature in ASP.NET Core 2.1. However, it doesn't seem the work and I have no idea why.
This is run in the controller:
var claims = new List<Claim>
{
new Claim(ClaimTypes.Email, email),
new Claim(ClaimTypes.Role, loginResult.User.RoleName)
};
ClaimsIdentity identity = new ClaimsIdentity(claims, CookieAuthenticationDefaults.AuthenticationScheme);
ClaimsPrincipal principal = new ClaimsPrincipal(identity);
var timespanExpiry = new TimeSpan(0, 0, 30, 0, 0);
await httpContextAccessor.HttpContext.SignInAsync(CookieAuthenticationDefaults.AuthenticationScheme
, principal
, new AuthenticationProperties { ExpiresUtc = new DateTimeOffset(timespanExpiry.Ticks, timespanExpiry) });
This is what I have in my Startup.cs:
public void ConfigureServices(IServiceCollection services)
{
services.AddMemoryCache();
services.AddSingleton<IConfiguration>(Configuration);
services.AddSingleton<IHttpContextAccessor, HttpContextAccessor>();
services.Configure<CookiePolicyOptions>(options =>
{
// This lambda determines whether user consent for non-essential cookies is needed for a given request.
options.CheckConsentNeeded = context => true;
options.MinimumSameSitePolicy = SameSiteMode.None;
});
//services
services.AddSingleton<ITableStorageService, TableStorageService>();
services.AddAuthorization(options =>
{
options.AddPolicy("ConfirmUser",
policy => policy.Requirements.Add(new AuthorizationsRequirement(AuthorizationKeyConstants.AUTH_CONFIRM_USER)));
options.AddPolicy("GetUser",
policy => policy.Requirements.Add(new AuthorizationsRequirement(AuthorizationKeyConstants.AUTH_GET_USER)));
options.AddPolicy("RemoveUser",
policy => policy.Requirements.Add(new AuthorizationsRequirement(AuthorizationKeyConstants.AUTH_DELETE_USER)));
options.AddPolicy("GetListUser",
policy => policy.Requirements.Add(new AuthorizationsRequirement(AuthorizationKeyConstants.AUTH_GETLIST_USER)));
});
services.AddAuthentication(options =>
{
options.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultAuthenticateScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultChallengeScheme = CookieAuthenticationDefaults.AuthenticationScheme;
options.DefaultScheme = CookieAuthenticationDefaults.AuthenticationScheme;
})
.AddCookie(CookieAuthenticationDefaults.AuthenticationScheme, options =>
{
options.LoginPath = new PathString("/User/Login");
options.AccessDeniedPath = new PathString("/error?unauth");
});
services.AddSingleton<IAuthorizationHandler, AuthorizationsHandler>();
services.AddMvc().SetCompatibilityVersion(CompatibilityVersion.Version_2_1);
}
// This method gets called by the runtime. Use this method to configure the HTTP request pipeline.
public void Configure(IApplicationBuilder app, IHostingEnvironment env)
{
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseDatabaseErrorPage();
}
else
{
app.UseExceptionHandler("/Home/Error");
app.UseHsts();
}
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseCookiePolicy();
app.UseAuthentication();
app.UseMvc(routes =>
{
routes.MapRoute(
name: "default",
template: "{controller=Home}/{action=Index}/{id?}");
});
}
No errors are occurring, but when I use the code below in Razor or in my controller to check if the user is authenticated, it returns a false. I have checked other questions and answers as well, none of it helps and helped me out with this.
httpContextAccessor.HttpContext.User.Identity.IsAuthenticated
Is there another way to do this, or am I doing something wrong?
EDIT:
I have included my whole Startup.cs but excluded some dependency injections and database related information on purpose.
I know this was asked a long time ago, but I recently encountered this same issue. So,here I am sharing my finding.
app.UseHttpsRedirection();
app.UseStaticFiles();
var cookiePolicyOptions = new CookiePolicyOptions
{
MinimumSameSitePolicy = SameSiteMode.Strict,
HttpOnly = Microsoft.AspNetCore.CookiePolicy.HttpOnlyPolicy.Always,
Secure = CookieSecurePolicy.None,
};
app.UseCookiePolicy(cookiePolicyOptions);
app.UseRouting();
app.UseAuthorization();
app.UseAuthentication();
For some reason when I use the above code in startup.cs ,It always redirecting to /login
But the below code is working fine.
app.UseHttpsRedirection();
app.UseStaticFiles();
app.UseRouting();
var cookiePolicyOptions = new CookiePolicyOptions
{
MinimumSameSitePolicy = SameSiteMode.Strict,
HttpOnly = Microsoft.AspNetCore.CookiePolicy.HttpOnlyPolicy.Always,
Secure = CookieSecurePolicy.None,
};
app.UseCookiePolicy(cookiePolicyOptions);
app.UseAuthentication();
app.UseAuthorization();
It seems position of UseRouting() impacts cookie authentication, any comments on this will be appreciated.
When adding the cookie you need to pass in the AuthenticationScheme.
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(
CookieAuthenticationDefaults.AuthenticationScheme,
options => {
options.LoginPath = "/Login";
}
);
Try one of the following process:
Clear browser cache.
Use a different browser.

Cookie authentication in ASP.Net core 2.0 Web API

I am trying to use cookie based authentication in ASP.Net Core 2.0 Web API and trying to activate that using the following code. The signin page is hosted inan separate domain than the one the app is hosted. And I have added [Authorize] attribute to the controller.
At startup I can see the service code invoked in debugger.
My expectation is that when my web client use the web api service, the middleware will detect that header does not have the cookie and will redirect the client to the login page. Yet I am able to invoke the controller freely.
public void ConfigureServices(IServiceCollection services)
{
services.AddCors(options => options.AddPolicy("AllowAll",
builder => builder.SetIsOriginAllowed(s => true)
.AllowAnyHeader()
.AllowAnyMethod()
.AllowCredentials()));
services.TryAddTransient<CorsAuthorizationFilter, CorsAuthorizationFilter>();
services.AddSwaggerGen(c =>
{
c.OperationFilter<FileOperationFilter>();
c.SwaggerDoc("v1", new Info
{
Title = "Collateral Management API",
Version = "v1"
});
});
services.AddMvcCore(options =>
{
options.Filters.Add(new CorsAuthorizationFilterFactory("AllowAll"));
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
options.Filters.Add(new AuthorizeFilter(policy));
})
.AddApiExplorer()
.AddJsonFormatters(s => s.NullValueHandling = NullValueHandling.Ignore);
services.AddAuthentication(CookieAuthenticationDefaults.AuthenticationScheme)
.AddCookie(auth =>
{
auth.Cookie.Domain = "xxx.com";
auth.Cookie.Name = "xxx";
auth.LoginPath = "/signin";
auth.AccessDeniedPath = "/signin";
});
services.AddAuthorization(auth =>
{
auth.DefaultPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
});
//...
}
and later ...
app.UseAuthentication()
Try adding:
services.AddAuthorization(options =>
{
options.DefaultPolicy = new AuthorizationPolicyBuilder().RequireAuthenticatedUser().Build();
});
After services.AddMvc()
EDIT
Given the way you are adding MVC can you try:
// requires: using Microsoft.AspNetCore.Authorization;
// using Microsoft.AspNetCore.Mvc.Authorization;
services.AddMvcCore(config =>
{
var policy = new AuthorizationPolicyBuilder()
.RequireAuthenticatedUser()
.Build();
config.Filters.Add(new AuthorizeFilter(policy));
});
AddMvcCore doesn't add the authorization services by default. You will also need to do AddMvcCore(...).AddAuthorization()

Categories