Connection context and UserNamePasswordValidator - c#

I´am using UserNamePasswordValidator for WCF. My code is similar. I use database.
Does any know how can I get information about current connection context in this method.
I want to log ip address of unsuccessful calls
public class MyCustomUserNameValidator : UserNamePasswordValidator
{
// This method validates users. It allows two users, test1 and test2
// with passwords 1tset and 2tset respectively.
// This code is for illustration purposes only and
// MUST NOT be used in a production environment because it is NOT secure.
public override void Validate(string userName, string password)
{
if (null == userName || null == password)
{
throw new ArgumentNullException();
}
if (!(userName == "test1" && password == "1tset") && !(userName == "test2" && password == "2tset"))
{
throw new SecurityTokenException("Unknown Username or Password");
}
}
}

Check this
Getting the IP address of server in ASP.NET?
in your exception you can get ip address.

Related

Read username in WCF service after authenticate

I have a custom validator that validate the incoming username and password in a webservice .
Once the validation is done, i need to use that user name and password inside the webservice .
Here is my CustomValidator
public class ServiceAuthenticator : UserNamePasswordValidator
{
private static readonly ILog _log = LogManager.GetLogger("ServiceAuthenticator");
public override void Validate(String userName, string password)
{
_log.InfoFormat("-------------{0}/{1}------------------------------", userName, password);
if (userName == null || password == null)
{
_log.WarnFormat(" Missing User-name / Password {0}/{1}", userName, password);
throw new FaultException("Incorrect User name or Password");
}
}
}
Now i have a webservice where i am trying to get the above user name and password
[WebInvoke(Method = "POST", UriTemplate = "Uplooc")]
[WebMethod(Description = "Save documents ")]
public void UploadDocGen(RemoteFileInfo remoteFileInfo)
{
// string UserName = ""; --- How i get the username
// sting Password = ""; -- How to get the password into this
}
We could use the ServiceSecurityContext to obtain the username value, while we could not get the password after the credential is authenticated to pass.
public string SayHello()
{
OperationContext oc = OperationContext.Current;
var username1=oc.ServiceSecurityContext.PrimaryIdentity.Name;
Console.WriteLine(username1);
var username2 = ServiceSecurityContext.Current.PrimaryIdentity.Name;
Console.WriteLine(username2);
return $"Hello Buddy,{DateTime.Now.ToLongTimeString()}";
}
Result.
The security token based on the SAML, we only can obtain the claim sets. It is a complex topic, which I don’t know much.
Here are some related documents, wish it is useful to you.
https://learn.microsoft.com/en-us/dotnet/framework/wcf/how-to-examine-the-security-context
https://learn.microsoft.com/en-us/dotnet/api/system.servicemodel.servicesecuritycontext?view=netframework-4.8
Feel free to let me know if there is anything I can help with.
 

Getting client ip address on WCF CustomUserNameValidator

We are using CustomUserNameValidator on our WCF project. I need to get client ip adresses when I'm authenticating. OperationContext is null, I cannot get it. ı don't want to turn on Asp.Net Compability mode on.
public class CustomUserNameValidator : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
if (null == userName || null == password)
{
throw new ArgumentNullException();
}
try
{
int userId = Common.AuthenticateUser(userName, password); //need ip adresses here
}
catch (Exception)
{
throw new SecurityTokenException("Unauthorized");
}
}
}
Is there any resolve about this issue?

ServiceStack Authentication with Existing Database

I've been looking at ServiceStack and I'm trying to understand how to use BasicAuthentication on a service with an existing database. I would like to generate a public key (username) and secret key (password) and put that in an existing user record. The user would then pass that to the ServiceStack endpoint along with their request.
What do I need to implement in the ServiceStack stack to get this working?
I have looked at both IUserAuthRepository and CredentialsAuthProvider base class and it looks like I should just implement IUserAuthRepository on top of my existing database tables.
I am also trying to figure out what is the bare minimum I should implement to get authentication working. I will not be using the service to Add or Update user access to the Service, but instead using a separate web application.
Any help and past experiences are greatly appreciated.
Example of authenticating against an existing database (in this case via Umbraco/ASP.NET membership system). 1) Create your AuthProvider (forgive the verbose code, and note you don't have to override TryAuthenticate too, this is done here to check if the user is a member of specific Umbraco application aliases):
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Web.Security;
using ServiceStack.Configuration;
using ServiceStack.Logging;
using ServiceStack.ServiceInterface;
using ServiceStack.ServiceInterface.Auth;
using ServiceStack.WebHost.Endpoints;
using umbraco.BusinessLogic;
using umbraco.providers;
public class UmbracoAuthProvider : CredentialsAuthProvider
{
public UmbracoAuthProvider(IResourceManager appSettings)
{
this.Provider = "umbraco";
}
private UmbracoAuthConfig AuthConfig
{
get
{
return EndpointHost.AppHost.TryResolve<UmbracoAuthConfig>();
}
}
public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IOAuthTokens tokens, Dictionary<string, string> authInfo)
{
ILog log = LogManager.GetLogger(this.GetType());
var membershipProvider = (UsersMembershipProvider)Membership.Providers["UsersMembershipProvider"];
if (membershipProvider == null)
{
log.Error("UmbracoAuthProvider.OnAuthenticated - NullReferenceException - UsersMembershipProvider");
session.IsAuthenticated = false;
return;
}
MembershipUser user = membershipProvider.GetUser(session.UserAuthName, false);
if (user == null)
{
log.ErrorFormat(
"UmbracoAuthProvider.OnAuthenticated - GetMembershipUser failed - {0}", session.UserAuthName);
session.IsAuthenticated = false;
return;
}
if (user.ProviderUserKey == null)
{
log.ErrorFormat(
"UmbracoAuthProvider.OnAuthenticated - ProviderUserKey failed - {0}", session.UserAuthName);
session.IsAuthenticated = false;
return;
}
User umbracoUser = User.GetUser((int)user.ProviderUserKey);
if (umbracoUser == null || umbracoUser.Disabled)
{
log.WarnFormat(
"UmbracoAuthProvider.OnAuthenticated - GetUmbracoUser failed - {0}", session.UserAuthName);
session.IsAuthenticated = false;
return;
}
session.UserAuthId = umbracoUser.Id.ToString(CultureInfo.InvariantCulture);
session.Email = umbracoUser.Email;
session.DisplayName = umbracoUser.Name;
session.IsAuthenticated = true;
session.Roles = new List<string>();
if (umbracoUser.UserType.Name == "Administrators")
{
session.Roles.Add(RoleNames.Admin);
}
authService.SaveSession(session);
base.OnAuthenticated(authService, session, tokens, authInfo);
}
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
ILog log = LogManager.GetLogger(this.GetType());
var membershipProvider = (UsersMembershipProvider)Membership.Providers["UsersMembershipProvider"];
if (membershipProvider == null)
{
log.Error("UmbracoAuthProvider.TryAuthenticate - NullReferenceException - UsersMembershipProvider");
return false;
}
if (!membershipProvider.ValidateUser(userName, password))
{
log.WarnFormat("UmbracoAuthProvider.TryAuthenticate - ValidateUser failed - {0}", userName);
return false;
}
MembershipUser user = membershipProvider.GetUser(userName, false);
if (user == null)
{
log.ErrorFormat("UmbracoAuthProvider.TryAuthenticate - GetMembershipUser failed - {0}", userName);
return false;
}
if (user.ProviderUserKey == null)
{
log.ErrorFormat("UmbracoAuthProvider.TryAuthenticate - ProviderUserKey failed - {0}", userName);
return false;
}
User umbracoUser = User.GetUser((int)user.ProviderUserKey);
if (umbracoUser == null || umbracoUser.Disabled)
{
log.WarnFormat("UmbracoAuthProvider.TryAuthenticate - GetUmbracoUser failed - {0}", userName);
return false;
}
if (umbracoUser.UserType.Name == "Administrators"
|| umbracoUser.GetApplications()
.Any(app => this.AuthConfig.AllowedApplicationAliases.Any(s => s == app.alias)))
{
return true;
}
log.WarnFormat("UmbracoAuthProvider.TryAuthenticate - AllowedApplicationAliases failed - {0}", userName);
return false;
}
}
public class UmbracoAuthConfig
{
public UmbracoAuthConfig(IResourceManager appSettings)
{
this.AllowedApplicationAliases = appSettings.GetList("UmbracoAuthConfig.AllowedApplicationAliases").ToList();
}
public List<string> AllowedApplicationAliases { get; private set; }
}
2) Register provider via usual AppHost Configure method:
public override void Configure(Container container)
{
// .... some config code omitted....
var appSettings = new AppSettings();
AppConfig = new AppConfig(appSettings);
container.Register(AppConfig);
container.Register<ICacheClient>(new MemoryCacheClient());
container.Register<ISessionFactory>(c => new SessionFactory(c.Resolve<ICacheClient>()));
this.Plugins.Add(
new AuthFeature(
// using a custom AuthUserSession here as other checks performed here, e.g. validating Google Apps domain if oAuth enabled/plugged in.
() => new CustomAuthSession(),
new IAuthProvider[] { new UmbracoAuthProvider(appSettings)
}) {
HtmlRedirect = "/api/login"
});
}
3) Can now authenticate against existing Umbraco database # yourapidomain/auth/umbraco, using Umbraco to manage users/access to API. No need to implement extra user keys/secrets or BasicAuthentication, unless you really want to....
I'm just starting with ServiceStack and I needed exactly the same thing - and I managed to get it to work today.
The absolute bare minimum for logging in users via Basic Auth is this:
using ServiceStack.ServiceInterface;
using ServiceStack.ServiceInterface.Auth;
public class CustomBasicAuthProvider : BasicAuthProvider
{
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
// here, you can get the user data from your database instead
if (userName == "MyUser" && password == "123")
{
return true;
}
return false;
}
}
...and register it in the AppHost:
Plugins.Add(new AuthFeature(() => new CustomUserSession(),
new IAuthProvider[] {
new CustomBasicAuthProvider()
}) { HtmlRedirect = null });
That's all!
Another possible solution would be to use the default BasicAuthProvider and provide an own implementation of IUserAuthRepository instead.
I can show you an example of this as well, if you're interested.
EDIT:
Here's the bare minimum IUserAuthRepository - just inherit from InMemoryAuthRepository and override TryAuthenticate:
using ServiceStack.ServiceInterface.Auth;
public class CustomAuthRepository : InMemoryAuthRepository
{
public override bool TryAuthenticate(string userName, string password, out UserAuth userAuth)
{
userAuth = null;
if (userName == "MyUser" && password == "123")
{
userAuth = new UserAuth();
return true;
}
return false;
}
}
...and register it in the AppHost:
container.Register<IUserAuthRepository>(r => new CustomAuthRepository());
Of course, you need to register one of the default AuthProviders (Basic, Credentials, whatever) as well.

Disconnect not firing sometimes in signalR

I currently keep my users in a table called OnlineUsers. When a person connects or disconnects it adds the userid and his connectionid to the table, but for some reason (i believe when multiple browser windows are open) the Disconnect function does not fire sometimes, leaving users in the table and making them appear "online" when they really aren't. Has anyone ran into this problem before and what would be a good way to fix this issue?
UPDATE** (sorry about not putting code, I should have done it in the first place)
Here are my db functions to add and remove from the table:
public bool ConnectUser(Guid UserId, String ConnectionId)
{
if (!Ent.OnlineUsers.Any(x => x.UserId == UserId && x.ConnectionId == ConnectionId))
{
Ent.OnlineUsers.AddObject(new OnlineUser { UserId = UserId, ConnectionId = ConnectionId });
Ent.SaveChanges();
return true;
}
else
return false;
}
public void DisconnectUser(Guid UserId, String ConnectionId)
{
if (Ent.OnlineUsers.Any(x => x.UserId == UserId && x.ConnectionId == ConnectionId))
{
Ent.OnlineUsers.DeleteObject(Ent.OnlineUsers.First(x => x.UserId == UserId && x.ConnectionId == ConnectionId));
Ent.SaveChanges();
}
}
Here is my hub class connect and disconnect task:
public Task Disconnect()
{
disconnectUser();
return null;
}
public Task Reconnect(IEnumerable<string> connections)
{
connectUser();
return null;
}
public Task Connect()
{
connectUser();
return null;
}
private void connectUser()
{
if (onlineUserRepository.ConnectUser(MainProfile.UserId, Context.ConnectionId))
{
Groups.Add(Context.ConnectionId, Convert.ToString(MainProfile.ChatId));
}
}
private void disconnectUser()
{
onlineUserRepository.DisconnectUser(MainProfile.UserId, Context.ConnectionId);
Groups.Remove(Context.ConnectionId, Convert.ToString(MainProfile.ChatId));
}
I have checked that I am on the latest version of signalR (0.5.3) and this seems to happen when I have multiple browser windows open and I close them all at once, the users will get stuck in the database.
In case this is needed, this is my Connection Id Generator class:
public class MyConnectionFactory : IConnectionIdGenerator
{
public string GenerateConnectionId(IRequest request)
{
if (request.Cookies["srconnectionid"] != null)
{
return request.Cookies["srconnectionid"].ToString();
}
return Guid.NewGuid().ToString();
}
}
I think your connection factory is indeed the problem. If the case that you do not find a cookie, you go ahead and generate a new guid, but by that time it's already too late.
My understanding is that the connection id is established by the client (the client side hub) during initialization and cannot be changed at the server; it can only be read. In effect when you are returning a new Guid when you don't find the cookie you are changing the client id.
In my connection factory if the cookie is not found I throw. In the controller action that opens the page that is using signalr I make sure the cookie is planted.
Here is my connection factory:
public class ConnectionFactory : IConnectionIdGenerator
{
public string GenerateConnectionId(IRequest request)
{
if (request.Cookies["UserGuid"] != null)
return request.Cookies["UserGuid"].Value;
throw new ApplicationException("No User Id cookie was found on this browser; you must have cookies enabled to enter.");
}
}

WCF UserNamePasswordValidator Caching

I have looked across the internet with no luck, I am trying to find a suitable way to cache a username and password token on the service side so each time a connection to the service is made I don't have to create a database connection.
This is what I am trying to achieve:
public class ServiceAuth : UserNamePasswordValidator
{
public override void Validate(string userName, string password)
{
var user = Repository.Authenticate(userName, password);
if (user != null)
{
// Perform some secure caching
}
else
throw new FaultException("Login Failed");
}
}
Is it possible to use caching when validating credentials in C# 4.0 WCF using UserNamePasswordValidator?
If so, can someone give me some clues on how to achieve this?
I would like to request the super users not to delete the answer as that could help others who wants to find the solution for their issues..!
I have implemented the the following CUSTOM security manager using key-value pair Dictionary collection for caching. Hope this helps
public class SecurityManager : UserNamePasswordValidator
{
//cacheCredentials stores username and password
static Dictionary<string, string> cacheCredentials = new Dictionary<string, string>();
//cacheTimes stores username and time that username added to dictionary.
static Dictionary<string, DateTime> cacheTimes = new Dictionary<string, DateTime>();
public override void Validate(string userName, string password)
{
if (userName == null || password == null)
{
throw new ArgumentNullException();
}
if (cacheCredentials.ContainsKey(userName))
{
if ((cacheCredentials[userName] == password) && ((DateTime.Now - cacheTimes[userName]) < TimeSpan.FromSeconds(30)))// && timespan < 30 sec - TODO
return;
else
cacheCredentials.Clear();
}
if (Membership.ValidateUser(userName, password))
{
//cache usename(key) and password(value)
cacheCredentials.Add(userName, password);
//cache username(key), time that username added to dictionary
cacheTimes.Add(userName, DateTime.Now);
return;
}
throw new FaultException("Authentication failed for the user");
}
}

Categories