I am using Identity Server 4 and implemented the following two interfaces:
IResourceOwnerPasswordValidator: for the purpose of validating user credentials against my custom DB
public class ResourceOwnerPasswordValidator : IResourceOwnerPasswordValidator
{
private IUserRepository _userRepository;
public ResourceOwnerPasswordValidator(IUserRepository userRepository)
{
this._userRepository = userRepository;
}
public Task ValidateAsync(ResourceOwnerPasswordValidationContext context)
{
var isAuthenticated = _userRepository.ValidatePassword(context.UserName, context.Password);
//code is omitted for simplicity
}
}
IProfileService: for getting necessary claims
public class ProfileService : IdentityServer4.Services.IProfileService
{
public IUserRepository _userRepository;
public ProfileService(IUserRepository userRepository)
{
_userRepository = userRepository;
}
public Task GetProfileDataAsync(ProfileDataRequestContext context)
{
//code is ommitted
}
Then added then necessary Interfaces and dependencies to the services in Startup class. I have also modified the Account Controller by injecting the login service which has the following implementation:
public class LoginService
{
private readonly IUserRepository _userRepository;
public LoginService( IUserRepository userRepository)
{
_userRepository = userRepository;
}
public bool ValidateCredentials(string username, string password)
{
return _userRepository.ValidatePassword(username, password);
}
The AccountController "Login" Action validate for the credentials by calling:
if (_loginService.ValidateCredentials(model.Username, model.Password))
{
var user = _loginService.FindByUsername(model.Username);
await HttpContext.Authentication.SignInAsync(user.Subject, user.Username);
//other code is omitted
When debugging the project the Login Action of AccountContoller is called, then my login service. After validating the user credentials, the consent page is displayed, which trigger the ProfileService GetProfileDataAsync Method.
However, the ResourceOwnerPasswordValidator was never called. Am I Implementing the LoginService in the correct manner or should I replace the IUserRepository injected by IResourceOwnerPasswordValidation then call the "ValidateAsync" method in Login Service ValidateCredentails?
And if that was the case, what is the advantage of passing that to LoginService, since I am already validating users in this way?
I ran into the same problem. The solution was to do just as you have done, but then needed to modify Startup.cs to see it.
// Adds IdentityServer
services.AddIdentityServer()
.AddResourceOwnerValidator<**PasswordAuthentication**>()
NOTE: Make sure you don't add it to the AddIdentity which also has the same .AddResourceOwnerValidator function. It took me awhile. Hope this helps someone.
According to docs:
If you want to use the OAuth 2.0 resource owner password credential
grant (aka password), you need to implement and register the
IResourceOwnerPasswordValidator interface:
Since you don't use password grant, expected behaviour is to not call ResourceOwnerPasswordValidator. For your case you don't need ResourceOwnerPasswordValidator implementation.
Related
I'm testing some custom authorization without the default Entity Framework stuff.
I have created an "ASP.NET Core Web App (Model-View-Controller)" project using "Authentication type" = "Individual Accounts".
In Program.cs I have:
builder.Services.AddTransient<IUserStore<CustomIdentityUser>, CustomUserStore>();
builder.Services
.AddDefaultIdentity<CustomIdentityUser>()
.AddUserStore<CustomUserStore>();
For the moment CustomIdentityUser is just an empty class.
CustomUserStore looks like this:
public class CustomUserStore : IUserStore<CustomIdentityUser>
{
public void Dispose()
{
GC.SuppressFinalize(this);
}
public Task<CustomIdentityUser> FindByNameAsync(string normalizedUserName, CancellationToken cancellationToken)
{
// Use dummy user for now
//return new Task<CustomIdentityUser>(() => new CustomIdentityUser());
return new Task<CustomIdentityUser>(() => { throw new Exception("THIS DOES NOT HAPPEN!"); });
}
...
(All other methods from IUserStore currently throws NotImplementedException.)
If I start the application, go the the login page, enter some credentials and click login I can see that FindByNameAsync in CustomUserStore is called. Good, it seems the application actually uses my custom user store to look for the user whose name I just entered.
But that's where my luck ends. The user interface seems to be waiting for the login to complete. The Task returned from FindByNameAsync doesn't seem to be started at all...why? I think that the caller should get CustomIdentityUser instance from it (and then probably call GetPasswordHashAsync in CustomUserStore).
why do you return new task in FindByNameAsync , also CustomUserStore
should be for extending identity properties. in my opinion, creating a service for identity functionality and using dependency injection
create an interface with the name IIdentity.cs
public Task<IdentityResult> UpdateUserAsync(string id , UpdateUserVm updatedUser);
then the implementation class IdentityService
public class IdentityService : IIdentity
{
...
public async Task<IdentityResult> UpdateUserAsync(string id, UpdateUserVm updatedUser)
{
var currentUser = await _userManager.FindByIdAsync(id);
currentUser.PhoneNumber = updatedUser.PhoneNumber;
return currentUser != null ? await _userManager.UpdateAsync(currentUser) : IdentityResult.Failed();
}
}
then register the service
service.AddScoped<IIdentity, IdentityService>();
in controller
public class AccountController : Controller
{
private readonly IIdentity _identityService;
public AccountController(IIdentity identityService) =>
(_identityService) = (identityService);
[HttpPost]
public async Task<IActionResult> UpdateProfile(params)
{
...
await _identityService.UpdateUserAsync(params)
}
}
I have web API with custom policies and authorization handlers.
I wanted to reuse authorization handlers but HttpContext is null when attribute is used on signalr's hub.
For example this is my controller.
[Authorize]
public sealed class ChatsController : ControllerBase
{
[HttpPost("{chatId}/messages/send")]
[Authorize(Policy = PolicyNames.ChatParticipant)]
public Task SendMessage() => Task.CompletedTask;
}
And this my my authorization handler. I can extract "chatId" from HttpContext and then use my custom logic to authorize user.
internal sealed class ChatParticipantRequirementHandler : AuthorizationHandler<ChatParticipantRequirement>
{
private readonly IHttpContextAccessor _httpContextAccessor;
public ChatParticipantRequirementHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ChatParticipantRequirement requirement)
{
if(_httpContextAccessor.HttpContext != null)
{
// Logic
}
return Task.CompletedTask;
}
}
However this won't work with Azure SignalR because I don't have access to HttpContext. I know that I can provide custom IUserIdProvider but I have no idea how to access "chatId" from "Join" method in my custom authorization handler.
[Authorize]
public sealed class ChatHub : Hub<IChatClient>
{
[Authorize(Policy = PolicyNames.ChatParticipant)]
public async Task Join(Guid chatId)
{
await Groups.AddToGroupAsync(Context.ConnectionId, chatId.ToString());
}
Is it possible to reuse my authorization handlers?
I would like to avoid copypasting my code.
One solution is to extract my authorization code to separate services but then I have to manually call those from my hubs and abandon [Authorize] way.
Your chat is a resource, and you want to use resource based authorization. In this case declarative authorization with an attribute is not enough, because chat id is known at runtime only. So you have to use imperative authorization with IAuthorizationService.
Now in your hub:
[Authorize]
public sealed class ChatHub : Hub<IChatClient>
{
private readonly IAuthorizationService authService;
public ChatHub(IAuthorizationService authService)
{
this.authService = authService;
}
public async Task Join(Guid chatId)
{
// Get claims principal from authorized hub context
var user = this.Context.User;
// Get chat from DB or wherever you store it, or optionally just pass the ID to the authorization service
var chat = myDb.GetChatById(chatId);
var validationResult = await this.authService.AuthorizeAsync(user, chat, PolicyNames.ChatParticipant);
if (validationResult.Succeeded)
{
await Groups.AddToGroupAsync(Context.ConnectionId, chatId.ToString());
}
}
}
Your authorization handler should look different, because it needs the chat resource in its signature to do this kind of evaluation:
internal sealed class ChatParticipantRequirementHandler : AuthorizationHandler<ChatParticipantRequirement, Chat>
{
private readonly IHttpContextAccessor _httpContextAccessor;
public ChatParticipantRequirementHandler(IHttpContextAccessor httpContextAccessor)
{
_httpContextAccessor = httpContextAccessor;
}
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ChatParticipantRequirement requirement, Chat chat)
{
// You have both user and chat now
var user = context.User;
if (this.IsMyUserAuthorizedToUseThisChat(user, chat))
{
context.Succeed(requirement);
}
else
{
context.Fail();
}
return Task.CompletedTask;
}
}
Edit: there is actually another option I didn't know about
You can make use of HubInvocationContext that SignalR Hub provides for authorized methods. This can be automatically injected into your AuthorizationHandler, which should look like this:
public class ChatParticipantRequirementHandler : AuthorizationHandler<ChatParticipantRequirement, HubInvocationContext>
{
protected override Task HandleRequirementAsync(AuthorizationHandlerContext context, ChatParticipantRequirement requirement, HubInvocationContext hubContext)
{
var chatId = Guid.Parse((string)hubContext.HubMethodArguments[0]);
}
}
Hub method will be decorated normally with [Authorize(Policy = PolicyNames.ChatParticipant)]
You still will have two authorization handlers, AuthorizationHandler<ChatParticipantRequirement> and AuthorizationHandler<ChatParticipantRequirement, HubInvocationContext>, no way around it. As for code dublication, you can however just get the Chat ID in the handler, either from HttpContext or HubInvocationContext, and than pass it to you custom written MyAuthorizer that you could inject into both handlers:
public class MyAuthorizer : IMyAuthorizer
{
public bool CanUserChat(Guid userId, Guid chatId);
}
I'm trying to create a custom identity provider to use in my asp.net core app that uses my legacy code to validate username and password.
This article https://learn.microsoft.com/en-us/aspnet/core/security/authentication/identity-custom-storage-providers?view=aspnetcore-3.1 explains the architecture and suggests to rewrite only the "store" and "data" layer, moreover, implementing only the interfaces you need.
But these intefaces don't have any of login/logout methods.
Maybe this operation must be performed in the upper layer, the "IdentityManager" layer? (UserManager)
I need (for now) only the login/logout functionality but i can't understand where to implement this methods.
Any idea or suggestion?
[EDITED]
as Fei Han has suggested me i'm trying to implement a custom SignInManager.
I did the following with success:
a custom SignInManager:
public class MySignInManager : SignInManager<IdentityUser>
{
private readonly UserManager<IdentityUser> _userManager;
private readonly ApplicationDbContext _db;
private readonly IHttpContextAccessor _contextAccessor;
public MySignInManager(
UserManager<IdentityUser> userManager,
IHttpContextAccessor contextAccessor,
IUserClaimsPrincipalFactory<IdentityUser> claimsFactory,
IOptions<IdentityOptions> optionsAccessor,
ILogger<SignInManager<IdentityUser>> logger,
ApplicationDbContext dbContext,
IAuthenticationSchemeProvider schemeProvider,
IUserConfirmation<IdentityUser> userConfirmation
)
: base(userManager, contextAccessor, claimsFactory, optionsAccessor, logger, schemeProvider, userConfirmation)
{
_userManager = userManager ?? throw new ArgumentNullException(nameof(userManager));
_contextAccessor = contextAccessor ?? throw new ArgumentNullException(nameof(contextAccessor));
_db = dbContext ?? throw new ArgumentNullException(nameof(dbContext));
}
public override async Task<SignInResult> PasswordSignInAsync(string userName, string password, bool rememberMe, bool shouldLockout)
{
SignInResult result;
if (userName == "pippo" && password == "pippo")
{
return SignInResult.Success;
}
else
{
return SignInResult.Failed;
}
}
}
then i registered it in the startrup.cs
public void ConfigureServices(IServiceCollection services)
{
services. ... .AddSignInManager<MySignInManager>();
this mechanism WORKS!
When i signin, the method PasswordSignInAsync on my custom SignInManager is fired!
But, on the subsequentially calls to the api, the HttpContext.User.Identity is null.
so the first question is:
1) should i set the identity in the context on my own(explicitly)? and if yes, where?
I've explored the source code in the official .net core repository https://github.com/dotnet/aspnetcore/tree/master/src/Identity and i've noticed that:
the implementation of the SignInManager https://github.com/dotnet/aspnetcore/blob/master/src/Identity/Core/src/SignInManager.cs behaves more or less like mine (to be more precise, it doesn't set the HttpContext.User.Identity inside itself)
So, at firts, i though that HttpContext.User.Identity was setted by the caller of SignInManager.PasswordSignInAsync (just after, in case of success login). For the "caller" i mean in the login page.
but i've notice that in the login page of the .net core official repository https://github.com/dotnet/aspnetcore/blob/master/src/Identity/UI/src/Areas/Identity/Pages/V4/Account/Login.cshtml.cs nothing is setted after the call to the SignInManager.PasswordSignInAsync.
so the next question is:
2) what is the relationship between SignInManager and HttpContext.User.Identity? probably, i miss a piece.
I need (for now) only the login/logout functionality but i can't understand where to implement this methods. Any idea or suggestion?
SignInManager in ASP.NET Core Identity provides the APIs for user sign in and sign out, you can try to implement a customer SignInManager and override corresponding methods to customize login/logout functionality based on your actual requirement.
In order to have user information in HttpContext.User.Identity
You can update like this, but this will check the user from from the database of applicationDbContex
if (userName == "pippo" && password == "pippo")
{
//return SignInResult.Success;
return await base.PasswordSignInAsync(userName, password, rememberMe,
shouldLockout);
}
Or if you just signInAsync
if (userName == "pippo" && password == "pippo")
{
var user = new ApplicationUser { UserName = userName, Email = userName};
await base.SignInAsync(user, isPersistent: false);
return SignInResult.Success;
}
Hope this helps
I've got an ASP.NET core application which implements a custom UserClaimsPrincipalFactory , including the following method:
protected override async Task<ClaimsIdentity> GenerateClaimsAsync(TUser user)
This works well. I can check attributes of the user and include claims dynamically.
But I'd like to add a separate "admin" login endpoint, so that users can decide if they'd rather have the admin experience when they log in. Can I pass additional information about the current session (such as the login URL or any form parameters) to the GenerateClaimsAsync method?
For accessing request information from UserClaimsPrincipalFactory, you could register IHttpContextAccessor like below:
public class CustomUserClaimsPrincipalFactory<TUser> : UserClaimsPrincipalFactory<TUser> where TUser : class
{
private readonly HttpContext _httpContext;
public CustomUserClaimsPrincipalFactory(UserManager<TUser> userManager
, IOptions<IdentityOptions> optionsAccessor
,IHttpContextAccessor httpContextAccessor)
: base(userManager, optionsAccessor)
{
_httpContext = httpContextAccessor.HttpContext;
}
protected override async Task<ClaimsIdentity> GenerateClaimsAsync(TUser user)
{
var ci = await base.GenerateClaimsAsync(user);
ci.AddClaim(new Claim("RequestPath", _httpContext.Request.Path.Value));
return ci;
}
}
I am using OAuth bearer authentication, configured like this in Startup.cs:
OAuthBearerAuthenticationOptions oAuthBearerOptions =
new OAuthBearerAuthenticationOptions
{
AccessTokenProvider = new AccessTokenProvider(),
AuthenticationMode = AuthenticationMode.Active
};
app.UseOAuthBearerAuthentication(oAuthBearerOptions);
... where AccessTokenProvider is implemented as:
public class AccessTokenProvider : AuthenticationTokenProvider
{
public override async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
// Internal logic to get data needed for building identity...
// Create claims identity
ClaimsIdentity identity = new ClaimsIdentity(identityName);
identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, nameIdentifier));
// Add other claims
// Set claims identity
context.SetTicket(new AuthenticationTicket(identity, new AuthenticationProperties()));
}
}
If I set a breakpoint at the end of ReceiveAsync, I can verify that the identity is built correctly (has claims) and that SetTicket is reached.
But when I try to access the identity from a Web API controller:
public abstract class BaseStorageController : ApiController
{
protected IStorageService StorageService;
protected BaseStorageController(IStorageServiceFactory storageServiceFactory)
{
StorageService = storageServiceFactory.CreateStorageService(User.Identity as ClaimsIdentity);
}
}
... the list of claims on the identity is empty!
What can be causing this?
Side note: I don't know if this is related, but I am using Castle Windsor as an IOC container to inject dependencies into my controllers (in the above case, IStorageServiceFactory). The above seemed to work (claims were not empty) before I added that. However, I'm not using CW to manage anything related to authentication. Here is my CW installer for api controllers:
public class ApiControllerInstaller : IWindsorInstaller
{
public void Install(IWindsorContainer container, IConfigurationStore store)
{
container.Register(Classes.FromThisAssembly().BasedOn<ApiController>().LifestylePerWebRequest());
}
}
I found the answer. It was not related to dependency injection/inversion of control. I'm not sure how I thought it was working prior to adding that.
The issue is similar to what is described here (but in my case the solution is different): User (IPrincipal) not avaliable on ApiController's constructor using Web Api 2.1 and Owin
Basically IPrincipal is not accessible from the constructor of the api controller, which is why there are no claims (the user is not yet authenticated). User.Identity is only accessible from the controller's actions, not the constructor. I changed my base controller implementation to the following to get around this issue:
public abstract class BaseStorageController : ApiController
{
private readonly IStorageServiceFactory _storageServiceFactory;
private IStorageService _storageService;
protected BaseStorageController(IStorageServiceFactory storageServiceFactory)
{
_storageServiceFactory = storageServiceFactory;
}
protected IStorageService StorageService
{
get
{
if (_storageService == null)
{
_storageService = _storageServiceFactory.CreateStorageService(User.Identity as ClaimsIdentity);
}
return _storageService;
}
}
}
Since StorageService is only accessed from controller actions, User.Identity is authenticated and has claims populated by the time that the StorageService getter gets called.
Hope this helps someone!
protected IStorageService StorageService
{
get
{
if (_storageService == null)
{
_storageService = _storageServiceFactory.CreateStorageService(User.Identity as ClaimsIdentity);
}
return _storageService;
}
}
this is not the best approach for implementing DI
It's much better to use constructor injection.
Check Constructor Injection in C#/Unity?
if you are not familliar with Unity, follow this link, very useful:
https://msdn.microsoft.com/en-us/library/dn223671(v=pandp.30).aspx
Regards