"OnDisconnected(): no suitable method found to override" - SignalR - c#

I've been trying to implement a chat room by following the "Asp.Net SignalR Chat Room" tutorial on CodeProject (http://www.codeproject.com/Articles/562023/Asp-Net-SignalR-Chat-Room). However, I'm getting the error "Hubs.ChatHubs.OnDisconnected(): no suitable method found to override"
ChatHub class:
public class ChatHub : Hub
{
#region Data Members
static List<UserDetail> ConnectedUsers = new List<UserDetail>();
static List<MessageDetail> CurrentMessage = new List<MessageDetail>();
#endregion
#region Methods
public void Connect(string userName)
{
var id = Context.ConnectionId;
if (ConnectedUsers.Count(x => x.ConnectionId == id) == 0)
{
ConnectedUsers.Add(new UserDetail { ConnectionId = id, UserName = userName });
// send to caller
Clients.Caller.onConnected(id, userName, ConnectedUsers, CurrentMessage);
// send to all except caller client
Clients.AllExcept(id).onNewUserConnected(id, userName);
}
}
public void SendMessageToAll(string userName, string message)
{
// store last 100 messages in cache
AddMessageinCache(userName, message);
// Broad cast message
Clients.All.messageReceived(userName, message);
}
public void SendPrivateMessage(string toUserId, string message)
{
string fromUserId = Context.ConnectionId;
var toUser = ConnectedUsers.FirstOrDefault(x => x.ConnectionId == toUserId);
var fromUser = ConnectedUsers.FirstOrDefault(x => x.ConnectionId == fromUserId);
if (toUser != null && fromUser != null)
{
// send to
Clients.Client(toUserId).sendPrivateMessage(fromUserId, fromUser.UserName, message);
// send to caller user
Clients.Caller.sendPrivateMessage(toUserId, fromUser.UserName, message);
}
}
public override System.Threading.Tasks.Task OnDisconnected()
{
var item = ConnectedUsers.FirstOrDefault(x => x.ConnectionId == Context.ConnectionId);
if (item != null)
{
ConnectedUsers.Remove(item);
var id = Context.ConnectionId;
Clients.All.onUserDisconnected(id, item.UserName);
}
return base.OnDisconnected();
}
#endregion
#region private Messages
private void AddMessageinCache(string userName, string message)
{
CurrentMessage.Add(new MessageDetail { UserName = userName, Message = message });
if (CurrentMessage.Count > 100)
CurrentMessage.RemoveAt(0);
}
#endregion
}
Any clues as to why this is happening?

For version 2.1.1+ change
public override Task OnDisconnected()
to
public override Task OnDisconnected(bool stopCalled)

You have a missmatch in your signalR depedency, they changed the signature for OnDisconnected in 2.1.1
So upgrade all projets to 2.1.1 or downgrade all projects to 2.1.0 and it should work

Related

Blazor Server http headers authorisation throws 400 on attempt to start hub connection

I`ve made a simple Blazor server hub with [Authorize] attribute. Authorisation process is described in specific AuthorizationRequirement and is considered successful only if incoming HTTP request has specified headers.
[Authorize(Policy = EnterpriseServiceBusHub.EsbHubAuthorizationPolicy)]
public class EnterpriseServiceBusHub : Hub
{
public const string EsbHubPath = "/esb-hub";
public const string EsbHubMethod = "InvokeIntegration";
public const string EsbHubAuthorizationPolicy = "EsbHubAuthorizationPolicy";
public const string EsbIntegrationID = "ESB-Integration-ID";
public const string EsbIntegrationName = "ESB-Integration-Channel";
private readonly IEntepriseServiceBus _entepriseServiceBus;
public EnterpriseServiceBusHub(IEntepriseServiceBus entepriseServiceBus)
{
_entepriseServiceBus = entepriseServiceBus;
}
[HubMethodName(EsbHubMethod)]
public async Task InvokeIntegration(string integrationChannel, string encryptedMessage)
{
await Clients.Others.SendAsync(integrationChannel, encryptedMessage);
}
}
public class EsbAuthorizationRequirement : AuthorizationHandler<EsbAuthorizationRequirement>, IAuthorizationRequirement
{
protected override async Task HandleRequirementAsync(AuthorizationHandlerContext context, EsbAuthorizationRequirement requirement)
{
try
{
await EsbAuthorizationChecker.CheckAuthorizationAsync();
context.Succeed(requirement);
}
catch (Exception ex)
{
context.Fail(new AuthorizationFailureReason(this, ex.Message));
}
}
}
static class EsbAuthorizationChecker
{
public static async Task<bool> CheckAuthorizationAsync()
{
var httpContextAccessor = ServicesHolder.GetService<IHttpContextAccessor>();
var requestHeaders = httpContextAccessor.HttpContext.Request.Headers;
if (!requestHeaders.TryGetValue(EnterpriseServiceBusHub.EsbIntegrationID, out var integrationID) ||
!requestHeaders.TryGetValue(EnterpriseServiceBusHub.EsbIntegrationName, out var integrationUrl))
{
var message = $"Headers {EnterpriseServiceBusHub.EsbIntegrationID} or {EnterpriseServiceBusHub.EsbIntegrationName} were not provided";
throw new Exception(message);
}
return await Task.FromResult(true);
}
}
The problem is that even specifying those headers in HubConnectionBuilder, attempt to start hub connection throws 400 (Bad request) exception.
_hubConnection = new HubConnectionBuilder().WithUrl($#"{Url}/{EnterpriseServiceBusHub.EsbHubPath}",
(HttpConnectionOptions options) =>
{
options.Headers.Add(EnterpriseServiceBusHub.EsbIntegrationID, "Value-1");
options.Headers.Add(EnterpriseServiceBusHub.EsbIntegrationName, "Value-2");
}).Build();
await _hubConnection.StartAsync(); // throws 400 Bad request
The specified authorisation policy is included in Program.cs
builder.Services.AddAuthorization(options =>
{
options.AddPolicy(EnterpriseServiceBusHub.EsbHubAuthorizationPolicy, policy =>
{
policy.Requirements.Add(new EsbAuthorizationRequirement());
});
});
builder.Services.AddSignalR(config =>
{
config.EnableDetailedErrors = true;
config.ClientTimeoutInterval = null;
});
What can be the reason for that? Am I specifying headers wrong? Thank you.

Class MessageAppService cannot have multiple base classes 'Hub' and 'AsyncCrudAppService'

I'm using ASP.NET Boilerplate with .NET Core 3.1.
I'm trying to save SignalR chat history to the database. The problem is when I want to create a subclass of AsyncCrudAppService and Hub, an error occurred with below text:
Class MessageAppService cannot have multiple base classes 'Hub' and 'AsyncCrudAppService'
Here is my code:
namespace MyProject.ChatAppService
{
public class MessageAppService : Hub, AsyncCrudAppService<Message, MessageDto, int, PagedAndSortedResultRequestDto, CreateMessageDto, UpdateMessageDto, ReadMessageDto>
{
private readonly IRepository<Message> _repository;
private readonly IDbContextProvider<MyProjectDbContext> _dbContextProvider;
private MyProjectPanelDbContext db => _dbContextProvider.GetDbContext();
public MessageAppService(
IDbContextProvider<MyProjectDbContext> dbContextProvider,
IRepository<Message> repository)
: base(repository)
{
_repository = repository;
_dbContextProvider = dbContextProvider;
}
public List<Dictionary<long, Tuple<string, string>>> InboxChat()
{
// The result will be List<userid, Tuple<username, latest message>>();
List<Dictionary<long, Tuple<string, string>>> result = new List<Dictionary<long, Tuple<string, string>>>();
List<User> listOfAllUsers = db.Set<User>().ToList();
listOfAllUsers.ForEach((user) =>
{
try
{
var dict = new Dictionary<long, Tuple<string, string>>();
var latestMessage = (from msg in db.Set<Message>() select msg)
.Where(msg => msg.CreatorUserId == user.Id && msg.receiverID == AbpSession.UserId)
.OrderByDescending(x => x.CreationTime)
.FirstOrDefault()
.Text.ToString();
dict.Add(user.Id, Tuple.Create(user.UserName, latestMessage));
result.Add(dict);
}
catch (Exception ex)
{
new UserFriendlyException(ex.Message.ToString());
}
});
return result;
}
public List<Message> getMessageHistory(int senderId)
{
return _repository.GetAll()
.Where(x => x.CreatorUserId == senderId && x.receiverID == AbpSession.UserId )
.ToList();
}
}
}
How could I avoid this error?
Update
Here is MyChatHub code that I wanted to combine with the AsyncCrudAppService subclass to become one class (I don't know if this way is correct but this was what came to my mind!).
public class MyChatHub : Hub, ITransientDependency
{
public IAbpSession AbpSession { get; set; }
public ILogger Logger { get; set; }
public MyChatHub()
{
AbpSession = NullAbpSession.Instance;
Logger = NullLogger.Instance;
}
public async Task SendMessage(string message)
{
await Clients.All.SendAsync("getMessage", string.Format("User {0}: {1}", AbpSession.UserId, "the message that has been sent from client is "+message));
}
public async Task ReceiveMessage(string msg, long userId)
{
if (this.Clients != null)
{
await Clients.User(userId.ToString())
.SendAsync("ReceiveMessage", msg, "From Server by userID ", Context.ConnectionId, Clock.Now);
}
else
{
throw new UserFriendlyException("something wrong");
}
}
public override async Task OnConnectedAsync()
{
await base.OnConnectedAsync();
Logger.Debug("A client connected to MyChatHub: " + Context.ConnectionId);
}
public override async Task OnDisconnectedAsync(Exception exception)
{
await base.OnDisconnectedAsync(exception);
Logger.Debug("A client disconnected from MyChatHub: " + Context.ConnectionId);
}
}
Your AsyncCrudAppService subclass can't and shouldn't inherit Hub.
Instead, inject and use IHubContext<MyChatHub> similar to ABP's SignalRRealTimeNotifier.
public MessageAppService(
IHubContext<MyChatHub> hubContext,
IDbContextProvider<MyProjectDbContext> dbContextProvider,
IRepository<Message> repository)
: base(repository)
{
_dbContextProvider = dbContextProvider;
_hubContext = hubContext;
_repository = repository;
}
To send a message to all clients, call _hubContext.Clients.All.SendAsync(...).
References:
https://learn.microsoft.com/en-us/aspnet/core/signalr/hubcontext?view=aspnetcore-3.1
https://aspnetboilerplate.com/Pages/Documents/SignalR-AspNetCore-Integration

Can't receive messages from groups Ng-Chat

I've implemented ng-chat https://github.com/rpaschoal/ng-chat (SignalR).
I have 3 users: User1, User2 and User3
If I send a message from User1 to User2 it works well User2 receives the message, but if I create a group (with User1 I open User2's chat and then Add the User3) a new group is created with Users (User2 and User3).
So, when I send a message from this new chat, the users (User2 and User3) doesn't receive any message
Here is my SingalR Hub:
using AdvansysOficina.Api._Core.Infraestructura;
using AdvansysOficina.Api.Generales.Servicios.UsuarioNs;
using Microsoft.AspNetCore.SignalR;
using NgChatSignalR.Models;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
namespace AdvansysOficina.Api.Desarrollo.Servicios.ConversacionPuntoNs.HubNs
{
public class ConversacionHub : Hub
{
private static List<ParticipantResponseViewModel> AllConnectedParticipants { get; set; } = new List<ParticipantResponseViewModel>();
private static List<ParticipantResponseViewModel> DisconnectedParticipants { get; set; } = new List<ParticipantResponseViewModel>();
private readonly object ParticipantsConnectionLock = new object();
private ISesion _sesion;
private IUsuarioServicio _usuarioServicio;
public ConversacionHub(ISesion sesion, IUsuarioServicio usuarioServicio)
{
_sesion = sesion;
_usuarioServicio = usuarioServicio;
}
public static IEnumerable<ParticipantResponseViewModel> ConnectedParticipants(string currentUserId)
{
return AllConnectedParticipants
.Where(x => x.Participant.Id != currentUserId);
}
public void Join(string userName, dynamic grupo)
{
lock (ParticipantsConnectionLock)
{
AllConnectedParticipants.Add(new ParticipantResponseViewModel()
{
Metadata = new ParticipantMetadataViewModel()
{
TotalUnreadMessages = 0
},
Participant = new ChatParticipantViewModel()
{
DisplayName = userName,
Id = Context.ConnectionId,
}
});
// This will be used as the user's unique ID to be used on ng-chat as the connected user.
// You should most likely use another ID on your application
//Clients.Caller.SendAsync("generatedUserId", Context.ConnectionId);
Clients.Caller.SendAsync("generatedUserId", Context.ConnectionId);
Clients.All.SendAsync("friendsListChanged", AllConnectedParticipants);
}
}
public void SendMessage(MessageViewModel message)
{
var sender = AllConnectedParticipants.Find(x => x.Participant.Id == message.FromId);
if (sender != null)
{
Clients.Client(message.ToId).SendAsync("messageReceived", sender.Participant, message);
}
}
public override Task OnDisconnectedAsync(Exception exception)
{
lock (ParticipantsConnectionLock)
{
var connectionIndex = AllConnectedParticipants.FindIndex(x => x.Participant.Id == Context.ConnectionId);
if (connectionIndex >= 0)
{
var participant = AllConnectedParticipants.ElementAt(connectionIndex);
AllConnectedParticipants.Remove(participant);
DisconnectedParticipants.Add(participant);
Clients.All.SendAsync("friendsListChanged", AllConnectedParticipants);
}
return base.OnDisconnectedAsync(exception);
}
}
public override Task OnConnectedAsync()
{
lock (ParticipantsConnectionLock)
{
var connectionIndex = DisconnectedParticipants.FindIndex(x => x.Participant.Id == Context.ConnectionId);
if (connectionIndex >= 0)
{
var participant = DisconnectedParticipants.ElementAt(connectionIndex);
DisconnectedParticipants.Remove(participant);
AllConnectedParticipants.Add(participant);
Clients.All.SendAsync("friendsListChanged", AllConnectedParticipants);
}
return base.OnConnectedAsync();
}
}
}
}
My signalR Adapter (Angular)
import { ChatAdapter, Message, ParticipantResponse, Group, IChatController } from 'ng-chat';
import { map, catchError } from 'rxjs/operators';
import { HttpClient } from '#angular/common/http';
import * as signalR from '#aspnet/signalr';
import { Observable } from 'rxjs/Observable';
import { of } from 'rxjs/observable/of';
import { AlertasHelper } from '../../../shared/helpers/alertas.helper';
import { PushNotificationHelper } from './notifications/push-notification';
export class SignalRAdapter extends ChatAdapter {
public static serverBaseUrl = 'http://192.168.16.51:5021/'; // if running locally
public userId: string;
private grrupo;
private hubConnection: signalR.HubConnection;
constructor(private username: string, private http: HttpClient, private notification: PushNotificationHelper
) {
super();
this.initializeConnection();
}
private initializeConnection(): void {
this.hubConnection = new signalR.HubConnectionBuilder()
.withUrl(`${SignalRAdapter.serverBaseUrl}chat`, { transport: signalR.HttpTransportType.LongPolling })
.build();
this.hubConnection
.start()
.then(() => {
this.joinRoom();
this.initializeListeners();
})
.catch(err => console.log(`Error while starting SignalR connection: ${err}`));
}
private initializeListeners(): void {
this.hubConnection.on('generatedUserId', (userId) => {
// With the userId set the chat will be rendered
this.userId = userId;
});
this.hubConnection.on('messageReceived', (participant, message) => {
// Handle the received message to ng-chat
console.log(message);
this.notification.notify('Nuevo mensaje de: ' + participant.displayName, message);
this.onMessageReceived(participant, message);
});
this.hubConnection.on('friendsListChanged', (participantsResponse: Array<ParticipantResponse>) => {
// Handle the received response to ng-chat
this.onFriendsListChanged(participantsResponse.filter(x => x.participant.id !== this.userId));
});
}
joinRoom(): void {
if (this.hubConnection && this.hubConnection.state === signalR.HubConnectionState.Connected) {
this.hubConnection.send('join', this.username, '');
}
}
listFriends(): Observable<ParticipantResponse[]> {
// List connected users to show in the friends list
// Sending the userId from the request body as this is just a demo
// return this.http
// .post(`${SignalRAdapter.serverBaseUrl}listFriends`, { currentUserId: this.userId })
// .pipe(
// map((res: any) => res),
// catchError((error: any) => Observable.throw(error.error || 'Server error'))
// );
return of([]);
}
getMessageHistory(destinataryId: any): Observable<Message[]> {
// This could be an API call to your web application that would go to the database
// and retrieve a N amount of history messages between the users.
return of([]);
}
sendMessage(message: Message): void {
if (this.hubConnection && this.hubConnection.state === signalR.HubConnectionState.Connected) {
console.log(message);
this.hubConnection.send('sendMessage', message);
}
}
groupCreated(group: Group): void {
console.log( group);
}
}
Use of component
<ng-chat #chat *ngIf="signalRAdapter && signalRAdapter.userId"
[adapter]="signalRAdapter"
[userId]="signalRAdapter.userId"
[groupAdapter]="signalRAdapter"
(onParticipantChatOpened)="chatOpened($event)"
[historyEnabled]="false">
</ng-chat>
I've downloaded the example of github's creator page, but he doesn't have an example with signalr using groups, I hope you can help me.
ng-chat treats groups as individual participants. You will have to join your room when this event gets invoked:
groupCreated(group: Group): void {
console.log( group);
// Invoke your SignalR hub and send the details of the newly created group
}
ng-chat will generate unique ids every time a group is created so you can track which group is which whenever one gets created from a running ng-chat instance. How you will handle the persistence of these groups is up to your application.
You might want to push a notification to involved users from your SignalR adapter that their friends list has changed (They'll be able to see the group at this stage). You could also decide not to do so and only push a notification if the user who has created the group send an initial message (Once again, up to your application requirements and needs).
You might also want to implement IChatGroupAdapter on your adapter to make the contract more explicit.
Hope this helps!

Send message to specific user in signalr

I have a signalR Server(Console Application) and a client application(Asp.net MVC5)
How I can send message to specific user in OAuth Membership.
Actually I can't resolve sender user from hub request context with.
Context.User.Identity.Name
My Hub
public class UserHub : Hub
{
#region Hub Methods
public void LoggedIn(string userName, string uniqueId, string ip)
{
Clients.All.userLoggedIn(userName, uniqueId, ip);
}
public void LoggedOut(string userName, string uniqueId, string ip)
{
var t = ClaimsPrincipal.Current.Identity.Name;
Clients.All.userLoggedOut(userName, uniqueId, ip);
}
public void SendMessage(string sendFromId, string userId, string sendFromName, string userName, string message)
{
Clients.User(userName).sendMessage(sendFromId, userId, sendFromName, userName, message);
}
#endregion
}
Start hub class(Program.cs)
class Program
{
static void Main(string[] args)
{
string url = string.Format("http://localhost:{0}", ConfigurationManager.AppSettings["SignalRServerPort"]);
using (WebApp.Start(url))
{
Console.WriteLine("Server running on {0}", url);
Console.ReadLine();
}
}
}
Keep connectionId with userName by creating a class as we know that Signalr only have the information of connectionId of each connected peers.
Create a class UserConnection
Class UserConnection{
public string UserName {set;get;}
public string ConnectionID {set;get;}
}
Declare a list
List<UserConnection> uList=new List<UserConnection>();
pass user name as querystring during connecting from client side
$.connection.hub.qs = { 'username' : 'anik' };
Push user with connection to this list on connected mthod
public override Task OnConnected()
{
var us=new UserConnection();
us.UserName = Context.QueryString['username'];
us.ConnectionID =Context.ConnectionId;
uList.Add(us);
return base.OnConnected();
}
From sending message search user name from list then retrive the user connectionid then send
var user = uList.Where(o=>o.UserName ==userName);
if(user.Any()){
Clients.Client(user.First().ConnectionID ).sendMessage(sendFromId, userId, sendFromName, userName, message);
}
DEMO
All of these answers are unnecessarily complex. I simply override "OnConnected()", grab the unique Context.ConnectionId, and then immediately broadcast it back to the client javascript for the client to store and send with subsequent calls to the hub server.
public class MyHub : Hub
{
public override Task OnConnected()
{
signalConnectionId(this.Context.ConnectionId);
return base.OnConnected();
}
private void signalConnectionId(string signalConnectionId)
{
Clients.Client(signalConnectionId).signalConnectionId(signalConnectionId);
}
}
In the javascript:
$(document).ready(function () {
// Reference the auto-generated proxy for the SignalR hub.
var myHub = $.connection.myHub;
// The callback function returning the connection id from the hub
myHub.client.signalConnectionId = function (data) {
signalConnectionId = data;
}
// Start the connection.
$.connection.hub.start().done(function () {
// load event definitions here for sending to the hub
});
});
In order to be able to get "Context.User.identity.Name", you supposed to integrate your authentication into OWIN pipeline.
More info can be found in this SO answer: https://stackoverflow.com/a/52811043/861018
In ChatHub Class Use This for Spacific User
public Task SendMessageToGroup(string groupName, string message)
{
return Clients.Group(groupName).SendAsync("Send", $"{Context.ConnectionId}: {message}");
}
public async Task AddToGroup(string groupName)
{
await Groups.AddToGroupAsync(Context.ConnectionId, groupName);
await Clients.Group(groupName).SendAsync("Send", $"{Context.ConnectionId} has joined the group {groupName}.");
}
public async Task RemoveFromGroup(string groupName)
{
await Groups.RemoveFromGroupAsync(Context.ConnectionId, groupName);
await Clients.Group(groupName).SendAsync("Send", $"{Context.ConnectionId} has left the group {groupName}.");
}

FormatException in custom IUserPasswordStore<Client> implementation

I'm writing custom implementation of IUserPasswordStore<Client> but I got FormatException during login process.
It seems like method GetPasswordHashAsync or someone who call this method calls Microsoft.AspNet.Identity.Crypto.VerifyHashedPassword(String hashedPassword, String password) which cause FormatException with message
Invalid length for a Base-64 char array or string.
Here is my implementation of IUserPasswordStore<Client>
public partial class ClientRepository : IUserStore<Client>, IUserPasswordStore<Client>, IUserLockoutStore<Client, string>
{
public Task CreateAsync(Client user)
{
return Task.Factory.StartNew(() => Create(user));
}
public Task UpdateAsync(Client user)
{
return Task.Factory.StartNew(() => Update(user));
}
public Task DeleteAsync(Client user)
{
return Task.Factory.StartNew(() => Delete(user));
}
public Task<Client> FindByIdAsync(string userId)
{
return Task.FromResult(Find(userId));
}
public Task<Client> FindByNameAsync(string userName)
{
return Task.FromResult(FetchOne(new ClientByUsername(userName)));
}
public Task SetPasswordHashAsync(Client user, string passwordHash)
{
return Task.Factory.StartNew(() => user.Password = passwordHash);
}
public Task<string> GetPasswordHashAsync(Client user)
{
return Task.FromResult(user.Password);
}
public Task<bool> HasPasswordAsync(Client user)
{
return Task.Factory.StartNew(() => !string.IsNullOrEmpty(user.Password));
}
public Task<DateTimeOffset> GetLockoutEndDateAsync(Client user)
{
return Task.FromResult<DateTimeOffset>(user.LockoutTo ?? DateTime.Now);
}
public Task SetLockoutEndDateAsync(Client user, DateTimeOffset lockoutEnd)
{
return Task.Factory.StartNew(() => user.LockoutTo = lockoutEnd.DateTime);
}
public Task<int> IncrementAccessFailedCountAsync(Client user)
{
return Task.FromResult(++user.LoginAttempts);
}
public Task ResetAccessFailedCountAsync(Client user)
{
return Task.Factory.StartNew(() => user.LoginAttempts = 0);
}
public Task<int> GetAccessFailedCountAsync(Client user)
{
return Task.Factory.StartNew(() => user.LoginAttempts);
}
public Task<bool> GetLockoutEnabledAsync(Client user)
{
return Task.FromResult(false);
}
public Task SetLockoutEnabledAsync(Client user, bool enabled)
{
return Task.FromResult(0);
}
public void Dispose()
{
database = null;
Manager = null;
context = null;
}
}
And my question: Where is called Microsoft.AspNet.Identity.Crypto.VerifyHashedPassword(String hashedPassword, String password) and what should I return in GetPasswordHashAsync?
For start I'm using password in plain text...
Edit: I thought, maybe I should provide some kind of hash service, so I add dummy implementation of IPasswordHasher for my purpose to use plain text
public class CustomPasswordHasher : IPasswordHasher
{
public string HashPassword(string password)
{
return password; //return password as is
}
public PasswordVerificationResult VerifyHashedPassword(string hashedPassword, string providedPassword)
{
if (hashedPassword.Equals(providedPassword))
{
return PasswordVerificationResult.Success;
}
return PasswordVerificationResult.Failed;
}
}
// and use it
public class ApplicationUserManager : UserManager<Client>
{
public ApplicationUserManager(IUserStore<Client> store, IPasswordHasher hasher)
: base(store)
{
// ...
PasswordHasher = hasher;
}
}
but breakpoints inside CustomPasswordHasher weren't hit, so propably I'm missing something...
Fixed. I was right with to use CustomPasswordHasher, but I had bug in Unity composition root. lol

Categories

Resources