WCF UserNamePasswordValidator Caching - c#

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");
}
}

Related

Offline authentication best practices in Xamarin

I intend to create a Xamarin(.Forms) application, and it has to have authentication: the user should provide a username and password to obtain data.
The problem is that it has to work offline as well (meaning it shouldn't rely on a server for the authentication process).
I considered the following so far:
Storing username and password in Preferences - it is not encrypted, easy to reset, easy to obtain etc.
Storing username and password in database - it could be encrypted (I presume), easy to reset
What are the best practices? Is there any built-in solution for Xamarin?
It depends on your app needs - if you just need to authenticate the user to access some restricted data offline - I suggest you to hash the user name and password, and storing it in the local device (doesn't matter where, it's hashed).
If you need to use the username & password later on for authenticate it with the server, then you should use a secured data like keychain, Xamarin has a built in abstraction for using it, it's called Xamarin.Auth, where you can store your credentials securely.
*note - in jailbroken/rooted devices storing sensitive data is risky, use it with caution.
Edit - I have added a working code sample of how to consume and use these services:
1) In your portable project add this interface:
public interface ISecuredDataProvider
{
void Store(string userId, string providerName, IDictionary<string, string> data);
void Clear(string providerName);
Dictionary<string, string> Retreive(string providerName);
}
2) In your Android project, add this implementation:
public class AndroidSecuredDataProvider : ISecuredDataProvider
{
public void Store(string userId, string providerName, IDictionary<string, string> data)
{
Clear(providerName);
var accountStore = AccountStore.Create(Android.App.Application.Context);
var account = new Account(userId, data);
accountStore.Save(account, providerName);
}
public void Clear(string providerName)
{
var accountStore = AccountStore.Create(Android.App.Application.Context);
var accounts = accountStore.FindAccountsForService(providerName);
foreach (var account in accounts)
{
accountStore.Delete(account, providerName);
}
}
public Dictionary<string, string> Retreive(string providerName)
{
var accountStore = AccountStore.Create(Android.App.Application.Context);
var accounts = accountStore.FindAccountsForService(providerName).FirstOrDefault();
return (accounts != null) ? accounts.Properties : new Dictionary<string, string>();
}
}
3) In your iOS project, add this implementation:
public class IOSSecuredDataProvider : ISecuredDataProvider
{
public void Store(string userId, string providerName, IDictionary<string, string> data)
{
Clear(providerName);
var accountStore = AccountStore.Create();
var account = new Account(userId, data);
accountStore.Save(account, providerName);
}
public void Clear(string providerName)
{
var accountStore = AccountStore.Create();
var accounts = accountStore.FindAccountsForService(providerName);
foreach (var account in accounts)
{
accountStore.Delete(account, providerName);
}
}
public Dictionary<string, string> Retreive(string providerName)
{
var accountStore = AccountStore.Create();
var accounts = accountStore.FindAccountsForService(providerName).FirstOrDefault();
return (accounts != null) ? accounts.Properties : new Dictionary<string, string>();
}
}
4) An example of Usage - after fetching facebook token, we'll store it like this:
securedDataProvider.Store(user.Id, "FacebookAuth", new Dictionary<string, string> { { "FacebookToken", token } });
Note - I added two layers of data in order to have the first layer to indicate whether the authentication was Facebook/UserName-Password/other.
The 2nd layer was the data itself - i.e facebook token / credentials.

ServiceStack API and ASP MVC Authentication in two ways

I'm having trouble solving architecture of an ASP MVC application that servers html pages and web services through ServiceStack.
The application lives in the base url eg "http://myapplication.com" and SS lives in "http://myapplication.com/api" because it is the easiest way to configure both.
In general everything works fine, but when I reached the part of the authorization and authentication, is where I'm stuck.
For one, I need the application handle cookies as ASP normally do FormsAuthentication through, and users would go through a login screen and could consume actions and controllers when the attribute "Authorize" is used. This is typical of ASP, so I have no problem with it, such as "http://myapplication.com/PurchaseOrders".
On the other hand, clients of my application will consume my web service api from javascript. Those web services will also be tagged in some cases with the attribute "Authenticate" of ServiceStack. For example "http://myapplication.com/api/purchaseorders/25" would have to validate if the user can view that particular purchase order, otherwise send a 401 Unauthorized so javascript can handle those cases and display the error message.
Last but not least, another group of users will make use of my API by a token, using any external application (probably Java or .NET). So I need to solve two types of authentication, one using username and password, the other by the token and make them persistant so once they are authenticated the first time, the next calls are faster to solve from the API.
This is the code that I have so far, I've put it very simply to make clear the example.
[HttpPost]
public ActionResult Logon(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
JsonServiceClient client = new JsonServiceClient("http://myapplication.com/api/");
var authRequest = new Auth { provider = CredentialsAuthProvider.Name, UserName = model.UserName, Password = model.Password, RememberMe = model.RememberMe };
try
{
var loginResponse = client.Send(authRequest);
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(loginResponse.UserName, false, 60);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket));
Response.Cookies.Add(cookie);
if (Url.IsLocalUrl(returnUrl) && returnUrl.Length > 1 && returnUrl.StartsWith("/") && !returnUrl.StartsWith("//") && !returnUrl.StartsWith("/\\"))
{
return Redirect(returnUrl);
}
else
{
return RedirectToAction("Index", "Test");
}
}
catch (Exception)
{
ModelState.AddModelError("", "Invalid username or password");
}
}
return View();
}
As for the authentication provider I am using this class
public class MyCredentialsAuthProvider : CredentialsAuthProvider
{
public MyCredentialsAuthProvider(AppSettings appSettings)
: base(appSettings)
{
}
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
//Add here your custom auth logic (database calls etc)
//Return true if credentials are valid, otherwise false
if (userName == "testuser" && password == "nevermind")
{
return true;
}
else
{
return false;
}
}
public override void OnAuthenticated(IServiceBase authService, IAuthSession session, IOAuthTokens tokens, Dictionary<string, string> authInfo)
{
//Fill the IAuthSession with data which you want to retrieve in the app eg:
session.FirstName = "some_firstname_from_db";
//...
session.CreatedAt = DateTime.Now;
session.DisplayName = "Mauricio Leyzaola";
session.Email = "mauricio.leyzaola#gmail.com";
session.FirstName = "Mauricio";
session.IsAuthenticated = true;
session.LastName = "Leyzaola";
session.UserName = "mauricio.leyzaola";
session.UserAuthName = session.UserName;
var roles = new List<string>();
roles.AddRange(new[] { "admin", "reader" });
session.Roles = roles;
session.UserAuthId = "uniqueid-from-database";
//base.OnAuthenticated(authService, session, tokens, authInfo);
authService.SaveSession(session, SessionExpiry);
}
}
On the Configure function of AppHost I am setting my custom authentication class to use it as the default. I guess I should create another class and add it here as well, to handle the token scenario.
Plugins.Add(new AuthFeature(() => new CustomUserSession(),
new IAuthProvider[] {
new MyCredentialsAuthProvider(appSettings)
}, htmlRedirect: "~/Account/Logon"));
So far, ServiceStack is working as expected. I can submit a post to /auth/credentials passing username and password and it stores this information, so next call to a service the request is already authorized, great so far!
The question I need to know is how to call (and probably set somewhere in SS) the user that is logging in from my Account controller. If you see the first block of code I am trying to call the web service (looks like I am doing it wrong) and it works, but the next call to any web service looks unauthenticated.
Please don't point me to ServiceStack tutorials, I've been there for the last two days and still cannot figure it out.
Thanks a lot in advance.
Here is what I usually use:
You can replace the "Logon" action method with the code below:
public ActionResult Login(LogOnModel model, string returnUrl)
{
if (ModelState.IsValid)
{
try
{
var authService = AppHostBase.Resolve<AuthService>();
authService.RequestContext = System.Web.HttpContext.Current.ToRequestContext();
var response = authService.Authenticate(new Auth
{
UserName = model.UserName,
Password = model.Password,
RememberMe = model.RememberMe
});
// add ASP.NET auth cookie
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
return RedirectToLocal(returnUrl);
}
catch (HttpError)
{
}
}
// If we got this far, something failed, redisplay form
ModelState.AddModelError("", "The user name or password provided is incorrect.");
return View(model);
}
...and the plugins:
//Default route: /auth/{provider}
Plugins.Add(new AuthFeature(() => new CustomUserSession(),
new IAuthProvider[] {
new CustomCredentialsAuthProvider(),
new CustomBasicAuthProvider()
}));
....the Auth provider classes:
public class CustomCredentialsAuthProvider : CredentialsAuthProvider
{
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
return UserLogUtil.LogUser(authService, userName, password);
}
}
public class CustomBasicAuthProvider : BasicAuthProvider
{
public override bool TryAuthenticate(IServiceBase authService, string userName, string password)
{
return UserLogUtil.LogUser(authService, userName, password);
}
}
...finally, the logging utility class
internal static class UserLogUtil
{
public static bool LogUser(IServiceBase authService, string userName, string password)
{
var userService = new UserService(); //This can be a webservice; or, you can just call your repository from here
var loggingResponse = (UserLogResponse)userService.Post(new LoggingUser { UserName = userName, Password = password });
if (loggingResponse.User != null && loggingResponse.ResponseStatus == null)
{
var session = (CustomUserSession)authService.GetSession(false);
session.DisplayName = loggingResponse.User.FName.ValOrEmpty() + " " + loggingResponse.User.LName.ValOrEmpty();
session.UserAuthId = userName;
session.IsAuthenticated = true;
session.Id = loggingResponse.User.UserID.ToString();
// add roles and permissions
//session.Roles = new List<string>();
//session.Permissions = new List<string>();
//session.Roles.Add("Admin);
//session.Permissions.Add("Admin");
return true;
}
else
return false;
}
}

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.

How can I authenticate against Active Directory in Nancy?

It's an outdated article, but http://msdn.microsoft.com/en-us/library/ff650308.aspx#paght000026_step3 illustrates what I want to do. I've chosen Nancy as my web framework because of it's simplicity and low-ceremony approach. So, I need a way to authenticate against Active Directory using Nancy.
In ASP.NET, it looks like you can just switch between a db-based membership provider and Active Directory just by some settings in your web.config file. I don't need that specifically, but the ability to switch between dev and production would be amazing.
How can this be done?
Really the solution is much simpler than it may seem. Just think of Active Directory as a repository for your users (just like a database). All you need to do is query AD to verify that the username and password entered are valid. SO, just use Nancy's Forms Validation and handle the connetion to AD in your implementation of IUserMapper. Here's what I came up with for my user mapper:
public class ActiveDirectoryUserMapper : IUserMapper, IUserLoginManager
{
static readonly Dictionary<Guid, long> LoggedInUserIds = new Dictionary<Guid, long>();
readonly IAdminUserValidator _adminUserValidator;
readonly IAdminUserFetcher _adminUserFetcher;
readonly ISessionContainer _sessionContainer;
public ActiveDirectoryUserMapper(IAdminUserValidator adminUserValidator, IAdminUserFetcher adminUserFetcher, ISessionContainer sessionContainer)
{
_adminUserValidator = adminUserValidator;
_adminUserFetcher = adminUserFetcher;
_sessionContainer = sessionContainer;
}
public IUserIdentity GetUserFromIdentifier(Guid identifier, NancyContext context)
{
_sessionContainer.OpenSession();
var adminUserId = LoggedInUserIds.First(x => x.Key == identifier).Value;
var adminUser = _adminUserFetcher.GetAdminUser(adminUserId);
return new ApiUserIdentity(adminUser);
}
public Guid Login(string username, string clearTextPassword, string domain)
{
var adminUser = _adminUserValidator.ValidateAndReturnAdminUser(username, clearTextPassword, domain);
var identifier = Guid.NewGuid();
LoggedInUserIds.Add(identifier, adminUser.Id);
return identifier;
}
}
I'm keeping a record in my database to handle roles, so this class handles verifying with AD and fetching the user from the database:
public class AdminUserValidator : IAdminUserValidator
{
readonly IActiveDirectoryUserValidator _activeDirectoryUserValidator;
readonly IAdminUserFetcher _adminUserFetcher;
public AdminUserValidator(IAdminUserFetcher adminUserFetcher,
IActiveDirectoryUserValidator activeDirectoryUserValidator)
{
_adminUserFetcher = adminUserFetcher;
_activeDirectoryUserValidator = activeDirectoryUserValidator;
}
#region IAdminUserValidator Members
public AdminUser ValidateAndReturnAdminUser(string username, string clearTextPassword, string domain)
{
_activeDirectoryUserValidator.Validate(username, clearTextPassword, domain);
return _adminUserFetcher.GetAdminUser(1);
}
#endregion
}
And this class actually verifies that the username/password combination exist in Active Directory:
public class ActiveDirectoryUserValidator : IActiveDirectoryUserValidator
{
public void Validate(string username, string clearTextPassword, string domain)
{
using (var principalContext = new PrincipalContext(ContextType.Domain, domain))
{
// validate the credentials
bool isValid = principalContext.ValidateCredentials(username, clearTextPassword);
if (!isValid)
throw new Exception("Invalid username or password.");
}
}
}

Connection context and UserNamePasswordValidator

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.

Categories