Resolve dependency on token validation and on controller constructor - asp.net-core

I'm trying to share a security object through an application using dependency injection:
services.AddScoped<IRequesterFilter, RequesterFilter>();
The object is populated when a token is validated:
app.UseJwtBearerAuthentication(options =>
{
options.AutomaticAuthenticate = true;
options.Authority = "https://xxxxxx.xxx";
options.TokenValidationParameters.ValidateAudience = false;
options.TokenValidationParameters.ValidIssuer = "https://yyyyyy.yyyy";
options.Events = new JwtBearerEvents
{
OnValidatedToken = context =>
{
var requestFilter = context.HttpContext.ApplicationServices.GetService<IRequesterFilter>();
requestFilter.RequesterLevel = RequesterLevelEnum.Client;
requestFilter.AppId = context.AuthenticationTicket.Principal.FindFirst("appid").Value;
return Task.FromResult(0);
}
};
});
But when I get IRequesterFilter from the controller constructor, the object is not initialized:
public ValuesController(IRequesterFilter requestFilter)
{
var x = requestFilter;
}
I'm using ASP.NET Core 1.0 RC1.

You are using the wrong container.
You are using context.HttpContext.ApplicationServices.GetService<IRequesterFilter>(), which is for application managed/application-wide objects, usually singletons.
For scoped services you have to use the scoped container (called RequestServices), which is would be context.HttpContext.RequestServices.GetService<IRequesterFilter>().
Please note that ApplicationServices will be removed from HttpContext with ASP.NET Core RC2, which will be a breaking change in the way are using it right now.

Related

OpenIddict Console Application - Get All Application Clients - ListSync Fails

I have written a simple console application to get all application clients from OpenIddict server. I tried all the possibilities and getting the syntax error. The code is below. I did not find any example in Github and found some outdated example (2017) is no longer relevant now. Please help
public static async Task<bool> Test()
{
var services = CreateServices();
var provider = services.BuildServiceProvider();
var scope = provider.CreateScope();
var context = scope.ServiceProvider.GetRequiredService<CustomDbContext>();
await context.Database.EnsureCreatedAsync();
var manager = scope.ServiceProvider.GetRequiredService<IOpenIddictApplicationManager>();
var result = await manager.FindByClientIdAsync("TestApp"); // It Works
IQueryable<OpenIddictEntityFrameworkCoreApplication> _applicationsQuery = Enumerable.Empty<OpenIddictEntityFrameworkCoreApplication>().AsQueryable();
_applicationsQuery.Where(apps => apps.ClientId != "");
var clients = manager.ListAsync<Func<OpenIddictEntityFrameworkCoreApplication>>(_applicationsQuery); //Compiler Error
return (result != null);
}
private static IServiceCollection CreateServices()
{
var services = new ServiceCollection();
services.AddDbContext<CustomDbContext>(opts =>
{
opts.UseSqlServer(
ConfigurationManager.ConnectionStrings["DefaultConnection"].ConnectionString,
b => b.MigrationsAssembly("Program"));
opts.UseOpenIddict();
});
services.AddOpenIddict() // Register the OpenIddict core components.
.AddCore(options =>
{
// Configure OpenIddict to use the Entity Framework Core stores and models.
// Note: call ReplaceDefaultEntities() to replace the default OpenIddict entities.
options.UseEntityFrameworkCore()
.UseDbContext<CustomDbContext>();
// Enable Quartz.NET integration.
options.UseQuartz();
});
return services;
}
ListAsync() returns an IAsyncEnumerable<T> collection, so you can use await foreach to iterate the collection:
await foreach (var application in manager.ListAsync())
{
Console.WriteLine(await manager.GetClientIdAsync(application));
}
You can also reference the System.Linq.Async package and use the async LINQ extensions. For instance, here's how you could retrieve all the client identifiers of all existing applications:
var identifiers = await manager.ListAsync()
.SelectAwait(application => manager.GetClientIdAsync(application))
.ToListAsync();

More than one IDP for SAML2 login with Sustainsys using .NET Core

We are using Sustainsys middleware with .NET Core to connect to an SAML2 IDP. It works well.
However, when we add more than one IDP in the Startup.cs we get in trouble. The user will select which IDP to login to and then the code should send a challenge to that IDP.
How to we specify which IDP in the code?
Using standard .NET Framework it is straight forward:
Context.GetOwinContext().Environment.Add("saml2.idp", new Entity(IDP2EntityId));
but there is no such construct in the .NET Core middleware.
Here is my code. Basically I add two IDPs during startup but I don't know how to specify which one during login/challenge? With this code IDP-1 is always selected because it was the first one added.
STARTUP.CS
public void ConfigureServices(IServiceCollection services)
{
var authenticationBuilder = GetAuthenticationBuilder(services);
string authenticationScheme = "saml2.idp"
authenticationBuilder.AddSaml2(authenticationScheme, options =>
{
options.SPOptions = GetSPOptions();
// Add IDP-1
options.IdentityProviders.Add(
new IdentityProvider(new EntityId(IDPEntityUrl1), options.SPOptions)
{
MetadataLocation = IDPMetadataUrl1
});
// Add IDP-2
options.IdentityProviders.Add(
new IdentityProvider(new EntityId(IDPEntityUrl2), options.SPOptions)
{
MetadataLocation = IDPMetadataUrl2
});
}
}
LOGINCONTROLLER.CS
string saml2AuthenticationScheme = "saml2.idp";
var props = new AuthenticationProperties
{
RedirectUri = returnUrl,
Items = { { "scheme", saml2AuthenticationScheme } }
};
return Challenge(properties: props, saml2AuthenticationScheme);
How do I specify which IDP to use in the LoginController?
I found the solution. We studied the Sustainsys code and found the undocumented (?) feature to specify the IDP in the AuthenticationProperties.Items with an "idp" item. Like this:
LoginController.cs
string saml2AuthenticationScheme = "saml2.idp";
var props = new AuthenticationProperties
{
RedirectUri = returnUrl,
Items = { { "scheme", saml2AuthenticationScheme }, { "idp", theSelectedIDPIdentityId } }
};
return Challenge(properties: props, saml2AuthenticationScheme);

Sustainsys Saml2 Handler AuthenticateAsync() method operation is not implemented

I'm trying a simple implementation in my Asp net Core application of Saml2 to integrate with an Ad FS server. I can't figure why I am getting this error. I downloaded the samples from the gitHub and tried to adapt it in my application.
NotImplementedException: The method or operation is not implemented.
Sustainsys.Saml2.AspNetCore2.Saml2Handler.AuthenticateAsync()
Here's my implementation, my application is running on Asp Net Core
On StartUp
services
.AddAuthentication(sharedOptions =>
{
sharedOptions.DefaultSignInScheme = CookieAuthenticationDefaults.AuthenticationScheme;
sharedOptions.DefaultChallengeScheme = Saml2Defaults.Scheme;
})
.AddSaml2(options =>
{
options.SPOptions.EntityId = new EntityId("http://myAdfsServer.myDomain.com/adfs/services/trust");
options.SPOptions.ReturnUrl = new Uri("https://localhost:5000");
options.IdentityProviders.Add(
new IdentityProvider(new EntityId("http://myAdfsServer.myDomain.com/adfs/services/trust"), options.SPOptions)
{
LoadMetadata = true,
MetadataLocation = "https://myAdfsServer.myDomain.com/FederationMetadata/2007-06/FederationMetadata.xml"
//MetadataLocation = "FederationMetadata.xml"
});
//options.SPOptions.ServiceCertificates.Add(new X509Certificate2(certificate.ToString()));
})
.AddCookie();
On my Controller
trying something similar to Sustainsys SAML2 Sample for ASP.NET Core WebAPI without Identity
[Authorize(AuthenticationSchemes = Saml2Defaults.Scheme)]
public class AuthenticationController : Controller
{
public AuthenticationController()
{
}
[AllowAnonymous]
public async Task LoginAdfs()
{
string redirectUri = string.Concat("https://localhost:5000", "/verifyAdfs");
try
{
new ChallengeResult(
Saml2Defaults.Scheme,
new AuthenticationProperties
{
RedirectUri = Url.Action(nameof(LoginCallback), new { redirectUri })
});
}catch(Exception e)
{
}
}
[AllowAnonymous]
public async Task<IActionResult> LoginCallback(string returnUrl)
{
var authenticateResult = await HttpContext.AuthenticateAsync(Saml2Defaults.Scheme);
//_log.Information("Authenticate result: {#authenticateResult}", authenticateResult);
// I get false here and no information on claims etc.
if (!authenticateResult.Succeeded)
{
return Unauthorized();
}
var claimsIdentity = new ClaimsIdentity("Email");
claimsIdentity.AddClaim(authenticateResult.Principal.FindFirst(ClaimTypes.NameIdentifier));
// _log.Information("Logged in user with following claims: {#Claims}", authenticateResult.Principal.Claims);
await HttpContext.SignInAsync("Email", new ClaimsPrincipal(claimsIdentity));
return LocalRedirect(returnUrl);
}
}
note: I've got a client that won't expose his MetaData in a URL, so I'll need to adapt it and set manually the metadata parameters
I'm stuck in this error, I does not even hit my method LoginAdfs.
The Saml2 handler cannot be used as an authencation scheme, it is a challenge scheme.
I guess that the LoginAdfs() method works fine, but that it's the LoginCallback that fails. The reason should be the call to HttpContext.AuthenticationAsync(Saml2Defaults.Scheme).
You should instead authenticate with the cookie scheme - because that's what keeps the session. Internally when the challenge is completed, the Saml2 handler will use the DefaultSignInScheme to preserve the result in a session (through a cookie, as that's the default sign in scheme).

Unable to resolve service for type Microsoft.AspNetCore.Identity.RoleManager for ASP.Net Core 2.0

How do I inject RoleManager in ASP.Net Core 2.0?
I'm getting the following error:
Unable to resolve service for type
Microsoft.AspNetCore.Identity.RoleManager for ASP.Net Core 2.0
I have the following code:
// add identity
var builder = services.AddIdentityCore<AppUser>(o =>
{
// configure identity options
o.Password.RequireDigit = false;
o.Password.RequireLowercase = false;
o.Password.RequireUppercase = false;
o.Password.RequireNonAlphanumeric = false;
o.Password.RequiredLength = 6;
});
builder = new IdentityBuilder(builder.UserType, typeof(AppRole), builder.Services);
builder.AddSignInManager<SignInManager<AppUser>>();
AppUser and AppRole are derived from IdentityUser and IdentityRole, respectively.
I'm not sure why you are using AddIdentityCore(). Have you tried the conventional AddIdentity():
services.AddIdentity<AppUser, AppRole>(options =>
{
...
});

Asp Net Core Identity UserManager change context

I have implemented the microsoft identity framework, I'm using the UserManager via dependecy injection, since it is a multi-tenancy project I would need to extend the UserManager class to pass the desired context, I searched on google but I could not find or better adapt anything For my case.
_userManagerRepository = new UserManagerRepository(new PortaleContext(tenantContext.Tenant))
_userMgr = new UserManager<ApplicationUser>(userStore,null,null,null,null,null,null,null,null);
But when I run this method:
var passwordResetToken = await _userMgr.GeneratePasswordResetTokenAsync(user)
I get the following error:
<div class="titleerror">NotSupportedException: No IUserTokenProvider named 'Default' is registered.</div>
<p class="location">Microsoft.AspNetCore.Identity.UserManager+<VerifyUserTokenAsync>d__122.MoveNext()</p>
And this is a part of my startup class:
services.AddIdentity<ApplicationUser, IdentityRole>(config =>
{
config.User.RequireUniqueEmail = true;
config.Password.RequiredLength = 8;
// config.Cookies.ApplicationCookie.LoginPath = "/App/Login";
config.SignIn.RequireConfirmedEmail = false;
config.SignIn.RequireConfirmedPhoneNumber = false;
})
.AddEntityFrameworkStores<AdminContext>()
.AddDefaultTokenProviders();
services.AddIdentity<ApplicationUser, IdentityRole>(config =>
{
config.User.RequireUniqueEmail = true;
config.Password.RequiredLength = 8;
config.Password.RequireNonAlphanumeric = false;
config.Password.RequireUppercase = false;
// config.Cookies.ApplicationCookie.LoginPath = "/App/Login";
config.SignIn.RequireConfirmedEmail = false;
config.SignIn.RequireConfirmedPhoneNumber = false;
})
.AddEntityFrameworkStores<PortaleContext>()
.AddDefaultTokenProviders();
In a nutshell, depending on which address the request arrives, the usermanager must update to its context
This article references an error when calling AddDefaultTokenProviders() Twice. This may be your issue, I am looking for a similar solution but this one didn't work for me, but maybe you haven't seen it yet and it works for you.
https://github.com/aspnet/Identity/issues/972