I have a site (MVC5) that require a user to login. However I have no control over the user database, instead I have a web services with a method of something like this.
public bool LogIn(string username, string password);
I need to login the user if it returns true, any idea how to achieve this?
I've read this article
http://www.asp.net/identity/overview/extensibility/overview-of-custom-storage-providers-for-aspnet-identity but have no idea how to create my own context, stuck in this part since the author doesn't provide downloadable solution example.
public void ConfigureAuth(IAppBuilder app)
{
app.CreatePerOwinContext(ExampleStorageContext.Create);
app.CreatePerOwinContext(ApplicationUserManager.Create);
...
Any help will be appreciated and sorry for bad english.
In this case you can simply use Session to save state of user.
Related
Background
I've created a working bot in C# but I'm failing to expand it to be a multi-tenant bot. I have created multiple bots in the Microsoft portal using this technique to identify themselves from the messaging endpoint:
https://example.com/api/messages/bot1
https://example.com/api/messages/bot2
https://example.com/api/messages/bot3
I can grab the LastSegment from the URL while in the MessagesController and store it in PrivateConversationData so I know which bot is talking in the current conversation. I intended use this stored 'bot id' in order to retrieve the Microsoft AppId & Password from the web.config (the bot's credentials are stored as a series of custom entries and not the standard appSettings as that only works for a single bot).
Credentials Problem
The authentication works well (nearly) as described here except when using async code with .ConfigureAwait(false) I can't get the HttpContext.Current as it becomes null when running on a different thread. This means I can't get the authenticated user's credentials either by looking them up in the web.config or by calling GetCredentialsFromClaims() since I've lost the authenticated user. If I use .ConfigureAwait(true) I just get deadlocks all over the place.
I have the credentials in the web.config but they are stored per bot and I need the 'bot id' from the URL above in order to get the credentials.
Question
The crux of the problem is: I need the URL to get the 'bot id' and I need the 'bot id' to get the credentials from the web.config but I can never reliably get access to the URL once I've passed a .ConfigureAwait(false) in the code. On the flip side, I can't get the 'bot id' from the PrivateConversationData since I need the bot's credentials in order to load it. A bit chicken and egg :-(
If anyone has any ideas of what I may be doing wrong or has an alternative approach to know which 'bot id' is currently executing I'd very much appreciate it.
Thanks
Please find below given the sample code.
public class StartUp {
public void Configuration(IAppBuilder app) {
var builder = new ContainerBuilder();
//Note: Initialize / register the Metadata Service that can bring the tenant details from the corresponding store
builder.RegisterType<TenantMetadataService>().As<ITenantMetadataService>();
//Note: This helps you in accessing the TenantMetadata from any constructor going forward after the below registry
builder.Register(ti => TenantMetadata.GetTenantMetadataFromRequest()).InstancePerRequest();
//TODO: Register the various services / controllers etc which may require the tenant details here
}
}
public class TenantMetadata {
public Guid TenantId { get;set; }
public Uri TenantUrl { get;set; }
public string TenantName { get;set; }
public static TenantMetadata GetTenantMetadataFromRequest() {
var context = HttpContext.Current;
//TODO: If you have any header like TenantId coming from the request, you can read and use it
var tenantIdFromRequestHeader = "";
//TODO: There will be a lazy cache that keeps building the data as new tenant's login or use the application
if(TenantCache.Contains(...))return TenantCache[Key];
//TODO: Do a look-up from the above step and then construct the metadata
var tenantMetadata = metadataSvc.GetTenantMetadata(...);
//TODO: If the data match does not happen from the Step2, build the cache and then return the value.
TenantCache.Add(key,tenantMetadata);
return tenantMetadata;
}
}
Note
The above code snippet uses the various service placeholders, cache and the other methods which will require to be used based on the designed application services. If you wish not to cache the tenant metadata, if it may contain some sensitive data, you can remove the caching implementation parts.
This implementation can be spread across all your web facing portals like your Web UI, Web Api and WebJobs etc so that it is same across all apps and it is easy to test and consume.
HTH.
I am creating a login system and I want a way to sort of cache information without retrieving the same information from the database.
for example I would have a static class called tokenData. token data would be a private class to store login token, username, expireDate, etc. So every time I visit another page it would check the static class for the data. The token is then stored in session / cookie to produce the lookup. If the data is not in the token static class (e.g. application pool restart) then it would check the database for the record when the user logs in and creates another based on the data in the token table.
Can someone offer me any advice is this is acceptable practice or offer me anything to improve and issues that can arise?
an exmaple is
public class userToken
{
private string name;
private string tokenId;
private static List<userToken> userData = new List<userToken>();
public void add(userToken);
public userToken Find(string tokenId);
}
Never ever ever use static for user or session specific data. static is shared across ALL sessions! You might end up with user sessions sharing confidential data.
Use HttpContext.Session or HttpContext.Cache.
Your solution can introduce errors when run on more than a single server with a single user. The cache you are building is not thread safe. It will also introduce errors when your app is run across 2+ servers in a cluster (load balanced).
I would look into using a proper caching toolset (memcached, etc.)
I am moving all of the Authentication and Security concerns into my Data Access Layer on an ASP.NET MVC4 Internet Application. Everything is fine for logging in, logging out, creating users etc but I am hitting a stumbling block with adding users to roles.
After Creating a user account I want to add them to some default roles. The method for doing so looks like this
public static string CreateUserAccount(string username, string password)
{
WebSecurity.CreateUserAndAccount(username, password);
var roleProvider = new SimpleRoleProvider();
roleProvider.AddUsersToRoles(new[] {username}, new[] {"MeterInfo", "SiteInfo", "AMRInfo", "InstallImages"});
return username + " Account Created";
}
The call to WebSecurity for creating the account is OK, but my use of SimpleRoleProvider causes this error
You must call the "WebSecurity.InitializeDatabaseConnection" method before you call any other method of the "WebSecurity" class. This call should be placed in an _AppStart.cshtml file in the root of your site.
The InitializeDatabaseConnection is already handled in the AuthConfig which is called on startup by global.asax.
AssetRegisterDataLayer.DataAccess.Security.InitializeSecurity();
The method being called on my DataAccess layer looks like this
public static void InitializeSecurity()
{
WebSecurity.InitializeDatabaseConnection("AssetRegisterDb","UserProfile","UserId","UserName", false);
}
I have seen this issue happen when people use the out of the box config for MVC4 where the Accounts controller is decorated with the [InitializeSimpleMembership] attribute instead of calling the WebSecurity initializer at application start, but that is not the case here. Anyone know why all the WebSecurity works except roles?
Thanks very much
I have found my mistake, I will answer my own question in case someone else has a similar issue.
The error in the code shown in my question was instantiating a new SimpleRoleProvider. I should have done this
public static string CreateUserAccount(string username, string password)
{
WebSecurity.CreateUserAndAccount(username, password);
var roleProvider = (SimpleRoleProvider)Roles.Provider;
roleProvider.AddUsersToRoles(new[] {username}, new[] {"MeterInfo", "SiteInfo", "AMRInfo", "InstallImages"});
return username + " Account Created";
}
Once authenticatated I use HttpContext.Current.User.Identity.Name; to ensure user is authorized to view a part of my site.
When I access certain parts of my site I need to get the User and get which context (organization they are logged into), url would be something like settings/supercompany/profile. where supercompany is the current context.
For each user I would need to check if they are admin in that company or a general user, if a general user then they cannot see certain things.
public class SettingsApi
{
private readonly string _userId;
private readonly string _contextId;
public SettingsApi(string userId, string contextId)
{
_userId = userId;
_contextId = contextId;
}
}
If I instantiate the class above from a controller (post or get), would caching somehow mess things up? Users role changed and I don't pick it up? Would something like the below work well?
var settings = new SettingsApi(HttpContext.Current.User.Identity.Name, currentContextId);
settings.IsAdmin();
Note: I would have used attributes to authorize but my requirements are I need to pick out the currentContext from the URL plus I need to use the class above elsewhere in my code.
Update
AuthorizeAttribute works well with caching, but the method used to authorize i.e.
protected override bool AuthorizeCore(HttpContextBase httpContext)
Will not hand me back an instance of the class I need...
Update 2 I don't want this class or an instance of this class to be cached in anyway, everytime I ask for a new instance I don't mind fetching one from the DB...
My Question - is the way I am coding ok? Will my user and his permissions NOT be cached?
It is possible, if you're not careful, to let MVC cache the output of the first request by an authenticated user. I use VaryByCustom and the current identity's name.
[OutputCache(VaryByCustom="user")]
public class SomeController : Controller
{
// etc.
}
In my Global.asax.cs I define:
public override string GetVaryByCustomString(HttpContext context, string custom)
{
if (custom.Equals("user", StringComparison.OrdinalIgnoreCase))
{
return context.User.Identity.IsAuthenticated ? context.User.Identity.Name : string.Empty;
}
return base.GetVaryByCustomString(context, custom);
}
If you are proposing to add instances of the SettingsApi to the cache then it definitely will not work as caching is app wide and so all users will end up sharing the same SettingsApi. Using the OutputCache should be fine (as long as you dont do something like put userid in a hidden field and use [OutputCache(VaryByCustom="user")] or similar).
If you are looking to cache the SettingsApi you should do so through SessionState which is per user/session and wont affect the authentication.
I'm very curious as to why I need to create a custom MembershipProvider and not supposed to do something like this to login a potential user.
[HttpPost]
public ActionResult Login(string username, string password)
{
UserUnitOfWork unitOfWork = new UserUnitOfWork();
User user = unitOfWork.UserRepository.GetByUsername(username);
if (user != null)
{
SaltedHashHelper saltHelper = new SaltedHashHelper();
if (saltHelper.VerifyHashString(username, user.Password, user.Salt))
FormsAuthentication.SetAuthCookie(user.Username, false);
}
else
{
// User cannot be verified.
}
return View();
}
If I create a custom MembershipProvider then I will have to create a custom MembershipUser because I am not using asp.net membership tables. I feel like that is more of a headache when your not using the aspnet membership tables. Maybe I am wrong.
Does anybody see anything wrong with my approach above? I'm curious.
Your approach is fine so long properly salt and hash the passwords and properly protect yourself from SQL injection.
The built in providers will be better tested than your custom built authentication provider, and may be more secure depending on your implementation.
You don't need to create a custom membership provider, you can use what ASP.NET gives you right out of the box. In fact implementing your own authentication scheme is always inadvisable if there's any other way out of it whatsoever. Worst case, just override the provider methods that you really need to behave differently.
Have a look at OWASP Top 10 for .NET developers part 7: Insecure Cryptographic Storage for some more background on why this is a bad idea.