I'm developing a web application in asp.net mvc.
My application uses multiple databases.
The database on which working on depends by the logged user.
I manage login on two levels:
Level1 on a "master" database where I have info about the login username/email and the "specific" database to use.
Level2 on the "specific" database where I manage users and roles with Identity2.
Example:
In the "master" database I have a record in User table with:
- username = user1
- databaseToUse = "specificDb1"
In the "specific" database called specificDb1, for the same user, I have a record in User table with all I need to manage user authentication and more.
What I want to achieve is:
Start the website, click on login, insert username and password, click on login.
Search for the username in the master database, if exist get the specific database name associated to the user.
Set here, DYNAMICALLY, the connection string for the "specific" database and perform Identity 2 login operations.
No problems for points 1 and 2. The problem is in point 3.
I use EntityFramework 6 Code First for both (master and specific) databases.
Regarding the configuration part of Identity I see in Startup.Auth.cs:
app.CreatePerOwinContext(ApplicationDbContext.Create);
app.CreatePerOwinContext<ApplicationUserManager>(ApplicationUserManager.Create);
app.CreatePerOwinContext<ApplicationSignInManager>(ApplicationSignInManager.Create);
Should I change something in Identity configuration?
Thanks in advance.
After hours spent in searching here my personal (maybe not the best) working solution.
In the Login action of AccountController, after the first check in "master" database, set the "specific" database informations in Session scope:
//Save the database infos in Session.
Session.Add("SqlServerInstance", masterUser.Company.DatabaseServer);
Session.Add("DbName", masterUser.Company.DatabaseName);
Always in AccountController update the SignInManager and UserManager properties fixing the connection string for the Identity context:
public ApplicationSignInManager SignInManager
{
get
{
//Set manually the right connection string used by the Identity database context.
HttpContext.GetOwinContext().Get<ApplicationDbContext>().Database.Connection.ConnectionString = ApplicationDbContext.GetConnectionString();
return _signInManager ?? HttpContext.GetOwinContext().Get<ApplicationSignInManager>();
}
private set
{
_signInManager = value;
}
}
public ApplicationUserManager UserManager
{
get
{
//Set manually the right connection string used by the Identity database context.
HttpContext.GetOwinContext().Get<ApplicationDbContext>().Database.Connection.ConnectionString = ApplicationDbContext.GetConnectionString();
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
}
private set
{
_userManager = value;
}
}
And finally the method that give us the connection string:
/// <summary>
/// Get the connection string getting SqlServerInstance and DbName from Session.
/// </summary>
public static string GetConnectionString()
{
string sqlServerInstance = DEFAULT_SQLSERVERINSTANCE;
if (HttpContext.Current.Session != null && HttpContext.Current.Session["SqlServerInstance"] != null)
sqlServerInstance = Convert.ToString(HttpContext.Current.Session["SqlServerInstance"]);
string dbName = DEFAULT_DBNAME;
if (HttpContext.Current.Session != null && HttpContext.Current.Session["DbName"] != null)
dbName = Convert.ToString(HttpContext.Current.Session["DbName"]);
return "Data Source=" + sqlServerInstance + ";Initial Catalog=" + dbName + ";Integrated Security=True";
}
Hope this can help.
Related
I am using MVC5, i know that if a user forgets his password, then MVC provides the feature of forgot password and reset password.
My client server is disconnected from internet or mailing, it is behind the firewalls, so i cannot use forgot password, as it might generate a link to reset password, but cannot mail it to the user to facilitate the password reset.
Please suggest if there is any way to decrypt the password(to let user know if he forgets his password) like how it was available in asp.net membership by simply using the GetPassword method of the membership classes.
Thank you
As far I know there is no easy way to do this in MVC5, because Identity (next gen of Membership) is using hash of password rather then encrypted password.
Password is hashed and stored in db as a hash - generally it's one-way operation (it's mean that there is no easy way to get password form hash).
Little bit more about what is hashing and salting you can read here:
How to securely store passwords and beat the hackers
How does hashing work?
This step to Ecrypt and decrypt password in asp.net mvc5.
create class name Hashing, paste this code
private static string GetRandomSalt()
{
return BCrypt.Net.BCrypt.GenerateSalt(12);
}
public static string HashPassword(string password)
{
return BCrypt.Net.BCrypt.HashPassword(password, GetRandomSalt());
}
public static bool ValidatePassword(string password, string correctHash)
{
return BCrypt.Net.BCrypt.Verify(password, correctHash);
}
Create controller login you past this code
using WebBcryptMVC.Models; //
using WebBcryptMVC.Util; // call folder name of Hashing class
namespace WebBcryptMVC.Controllers
{
public class LoginController : Controller
{
private DBLoginEntities db = new DBLoginEntities();
public ActionResult frmLogin()
{
return View("frmLogin", new tblLogin());
}
[HttpPost]
public ActionResult frmLogin(tblLogin account)
{
var currentAccount = db.tblLogins.First(a => a.UserName.Equals(account.UserName));
if ((currentAccount != null))
{
if (Hashing.ValidatePassword(account.Password, currentAccount.Password))
{
Session.Add("UserName", account.UserName);
//return View("~/Views/Home/frmHome.cshtml");
return RedirectToAction("frmHome", "Home");
}
else
{
ViewBag.error = "Invalid";
return View("frmLogin");
}
}
else
{
ViewBag.error = "Invalid";
return View("frmLogin");
}
}
I am currently working with MVC5 application which is using Owin OAuth authentication. I am looking forward to extend the login criteria where I have added couple of tables (Application table (ApplicationId(guid), ApplicationName(nvarchar) and ApplicationUserTable(id, ApplicationId(FK from Application table), UserId(FK column from ASPNetUsers table))) in my security DB.
Could please anyone give me some idea on how to access this ApplicationUserTable in Owin context so that I can verify first if user belong to a particular application? I have looked through quite a few examples but didn't find anything relevant to the particular scenario i am working with.
You could set your ApplicationId field as the ClientId, then you will have a different ClientId for each application.
When the user sends an authentication token request, in the GrantResourceOwnerCredentials method when you check the user credentials, check if the user belongs to the application that received ClientId represents.
In a simple way, it could be something like the following:
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
...
var user = await _userService.GetUserAsync(context.UserName, context.Password, context.ClientId);
if (user == null)
{
context.SetError("invalid_grant", "The user name, password or clientId is incorrect.");
return;
}
...
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
}
And in the user service:
public async Task<User> GetUserAsync(string userName, string password, string clientId = null)
{
var user = await _userManager.FindAsync(userName, password);
if (user == null || (clientId != null && user.ApplicationUsers.Where(au => au.ApplicationId == clientId).Count() == 0))
{
return null;
}
return user;
}
In the ValidateClientAuthentication method you should validate the ClientId-Secret are in your Application table.
I hope it helps.
I work on an ASP.NET MVC4 solution. When the user is logged in, I would like to display his fullname (not the username provided in the login form). His fullname (firstname + lastname actually stored in the user table in my database) should be displayed in the top right corner.
For better performance, I don't want to query the database each time a request is done.
How to proceed?
Keeping the user information (firstname, lastname, ...) in a cookie?
Keeping the user information is a session variable for all the lifecycle of the application?
Keeping the user information in a 'Profile' like explained here: How to assign Profile values? (*)
Something else?
(*) I think this solution a little complex for the use I have.
Thanks.
I would use a cookie. It doesn't hog up any memory on your machine like Session, and it doesn't hit the database like Profile would. Just remember to delete the cookie when the user signs off.
Note that the Profile would hit the database server each time you make a request. As far as I know, Profile data is not cached anywhere on the web server (unless you have a custom profile provider).
Another reason why I like cookie: if you ever want to store any additional user information for fast access, like UserPrimaryKey, or any special user preferences, you can just store them as JSON in the cookie. Here is an example:
Another note: the code below uses Newtonsoft.Json (the JsonConvert lines). It should come out of the box in an MVC4 project, but for an MVC3 project, you can just add it via nuget.
public class UserCacheModel
{
public string FullName { get; set; }
public string Preference1 { get; set; }
public int Preference2 { get; set; }
public bool PreferenceN { get; set; }
}
public static class UserCacheExtensions
{
private const string CookieName = "UserCache";
// put the info in a cookie
public static void UserCache(this HttpResponseBase response, UserCacheModel info)
{
// serialize model to json
var json = JsonConvert.SerializeObject(info);
// create a cookie
var cookie = new HttpCookie(CookieName, json)
{
// I **think** if you omit this property, it will tell the browser
// to delete the cookie when the user closes the browser window
Expires = DateTime.UtcNow.AddDays(60),
};
// write the cookie
response.SetCookie(cookie);
}
// get the info from cookie
public static UserCacheModel UserCache(this HttpRequestBase request)
{
// default user cache is empty
var json = "{}";
// try to get user cache json from cookie
var cookie = request.Cookies.Get(CookieName);
if (cookie != null)
json = cookie.Value ?? json;
// deserialize & return the user cache info from json
var userCache = JsonConvert.DeserializeObject<UserCacheModel>(json);
return userCache;
}
}
With this, you can read / write the cookie info from a controller like this:
// set the info
public ActionResult MyAction()
{
var fullName = MethodToGetFullName();
var userCache = new UserCache { FullName = fullName };
Response.UserCache(userCache);
return Redirect... // you must redirect to set the cookie
}
// get the info
public ActionResult MyOtherAction()
{
var userCache = Request.UserCache();
ViewBag.FullName = userCache.FullName;
return View();
}
We override the basic authentication in an MVC3 application by calling a webservice with the user's credentials and returning a WCF structure that contains the user's ID, a "LogonTicket". This LogonTicket is used to "authenticate the user for each call made to the webservice.
Now, we override by replacing the defaultProvider in the Web.config. All we do in this overridden provider is
to override the ValidateUser() function. That is where we call the web service with their credentials and return
the "LogonTicket".
This is the LogOn() function from our AccountController, essentially the base code from the template:
public ActionResult LogOn(LogOnModel model)
{
string ReturnUrl = "";
if (HttpContext.Request.UrlReferrer.Query.Length > 11)
{
ReturnUrl = Uri.UnescapeDataString(HttpContext.Request.UrlReferrer.Query.Substring(11));
}
if (ModelState.IsValid)
{
if (Membership.ValidateUser(model.UserName, model.Password))
{
FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe);
if (Url.IsLocalUrl(ReturnUrl) && ReturnUrl.Length > 1 && ReturnUrl.StartsWith("/")
&& !ReturnUrl.StartsWith("//") && !ReturnUrl.StartsWith("/\\"))
{
return Redirect(ReturnUrl);
}
else
{
return RedirectToAction("Index", "Home");
}
}
else
{
ModelState.AddModelError("", "The user name or password provided is incorrect.");
}
}
// If we got this far, something failed, redisplay form
ViewBag.MainWebsite = MainWebsite;
return View(model);
}
This is the overridden ValidateUser() function from our new default provider:
public override bool ValidateUser(string username, string password)
{
MyServiceClient mps = new MyServiceClient();
string sha1password = HashCode(password);
LogonInfo logonInfo = mps.GetLogonTicket(username, sha1password);
if (logonInfo.LogonTicket != "" && logonInfo.LogonTicket != "0")
{
// Authenticated so set session variables
HttpContext.Current.Session["LogonTicket"] = logonInfo.LogonTicket;
HttpContext.Current.Session["ParticipantID"] = logonInfo.ParticipantID;
return true;
}
else
{
return false;
}
}
I'm not really sure how to combine the use of the two, so my questions are:
How can I implement OpenID and Facebook logins and keep my current authentication method?
How can we "map" the OpenID user with our current user DB values? We MUST know so we can retrieve their info.
I know we can retrieve their email address but what if their OpenID email is different than the one they use for their record on our site?
Are there any examples of how to do this, anywhere?
Thanks for looking at my question.
I have done a project which required multiple log-on possibilities (custom account, Google and Facebook)
In the end your authentication with ASP.NET is entirely dependant on your configuration. (In your case it is FormsAuthentication) this means that FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe); basicly determines everything in regard to your user and where you set this isn't restricted.
You have now basicly the same implementation as we started out with, using a MembershipProvider to handle your own custom account. You only need to expand now to facilitate the openIds. You would have to expand your Controller with various actions for each login type (Now you have ActionResult LogOn() you can add to that for example: ActionResult LogOnOpenId()). Inside that method you basicly call the same code but instead of Membership.ValidateUser(model.UserName, model.Password) you call the OpenId services.
I have provided below an example of our google implementation using dotnetopenauth. The service method uses formsService.SignIn(userId.Value.ToString(), false); which basicly calls FormsAuthentication.SetAuthCookie(model.UserName, model.RememberMe); (we only do some custom behaviour there in regard to the SecurityPrincipal but this doesn't affect your Authentication process). You can also see that we make a new account when we receive a new user. To solve your question part 2 we have implemented a profile which can be merged if you can provide another login. This allows our users to keep their account consolidated and use whatever login method they like.
For examples in regard to multiple signons I will refer to the answer of Tomas whom referenced StackExchange as a good example. Also I'd advise you to install MVC4 and VS2012 and just do a File > New Project. The newest default template of MVC includes openid implementation alongside a custom login!
Example google openid implementation:
The controller method:
public virtual ActionResult LoginGoogle(string returnUrl, string runAction)
{
using (var openId = new OpenIdRelyingParty())
{
IAuthenticationResponse response = openId.GetResponse();
// If we have no response, start
if (response == null)
{
// Create a request and redirect the user
IAuthenticationRequest req = openId.CreateRequest(WellKnownProviders.Google);
var fetch = new FetchRequest();
fetch.Attributes.AddRequired(WellKnownAttributes.Name.First);
fetch.Attributes.AddRequired(WellKnownAttributes.Name.Last);
fetch.Attributes.AddRequired(WellKnownAttributes.Contact.Email);
fetch.Attributes.AddRequired(WellKnownAttributes.Preferences.Language);
req.AddExtension(fetch);
req.RedirectToProvider();
return null;
}
_service.ConnectViaGoogle(response, TempData);
}
The service method:
public void ConnectViaGoogle(IAuthenticationResponse response, TempDataDictionary tempData)
{
// We got a response - check it's valid and that it's me
if (response.Status == AuthenticationStatus.Authenticated)
{
var claim = response.GetExtension<FetchResponse>();
Identifier googleUserId = response.ClaimedIdentifier;
string email = string.Empty;
string firstName = string.Empty;
string lastName = string.Empty;
string language = string.Empty;
if (claim != null)
{
email = claim.GetAttributeValue(WellKnownAttributes.Contact.Email);
firstName = claim.GetAttributeValue(WellKnownAttributes.Name.First);
lastName = claim.GetAttributeValue(WellKnownAttributes.Name.Last);
language = claim.GetAttributeValue(WellKnownAttributes.Preferences.Language);
}
//Search User with google UserId
int? userId = _userBL.GetUserIdByGoogleSingleSignOnId(googleUserId);
//if not exists -> Create
if (!userId.HasValue)
{
_userBL.CreateGoogleUser(
googleUserId,
firstName,
lastName,
email,
language,
DBConstants.UserStatus.DefaultStatusId,
out userId);
}
if (userId.HasValue)
{
_userBL.UpdateLastLogon(userId.Value);
var formsService = new FormsAuthenticationService();
formsService.SignIn(userId.Value.ToString(), false);
AfterLoginActions(tempData);
}
}
}
Any questions or comments? I'll gladly hear them.
it should be perfectly possible to have multiple authentications methods. All IIS / ASP.net cares about is the FormsAuthentication cookies. So you would have one set of actions for your standard username/password auth, and another for OpenId. This is at least what I have done on one project.
You can't even trust the openId provider to give you an email address! A common solution to this problem is to allow a user to attach multiple OpenId identifiers (URI's) to the his account after logging in. This is e.g. how StackOverflow works. If this is the first time the user visits the system then you can auto create a new account, or force the user through a signup process.
When I added the OpenId support in the system mentioned, it had an existing table used to store username and password(users table). I added a new table with a many to one relationship with the users table, and used this to store the URI's.
As mentioned above StackOverflow it self is a good place to start, also there are a lot of good examples in the http://www.dotnetopenauth.net/ project.
As far as I know the source of SO is not public, and they are using the dotnetopenauth project.
This may be to abstract, but this library is a openId (among other things) for the open source orchard CMS: http://orchardopenauth.codeplex.com/
I hope this helps, but if you have any questions then please expand your question with more details.
I am implementing a custom MembershipProvider and I am trying to get the ValidateUser method to validate against my Profiles table in SQL Server. This table has columns called UserName and Password.
public override bool ValidateUser(string username, string password)
{
??? what to do here???
}
FYI, I am using MVC3 & EF 4.1 Code First.
Thanks
Paul
If you're using EF 4.1, you will have some kind of a DbContext object that contains the DbSet for your Profiles table - right?
So in that case, use this:
public override bool ValidateUser(string username, string password)
{
using(DbContext yourCtx = new DbContext())
{
// from your "Profiles" DbSet, retrieve that single entry which
// matches the username/password being passed in
var profile = (from p in yourCtx.Profiles
where p.UserName == username && p.Password == password
select p).SingleOrDefault();
// if that query returns a "Profile" (is != null), then your
// username/password combo is valid - otherwise, it's not valid
return (profile != null);
}
}