ASP.NET Membership: Where is Current User ID Stored? - asp.net

Using ASP.NET membership, if I want to get information for the current user, I can call MembershipUser.GetUser()
So, somehow the system must know the ID of the current user.
If this is correct, and all I want is the ID of the current user, is there a way to get it without returning all the user information from the database?
I know I can get the username of the current user using User.Identity.Name, but I need the ID.

The short answer is no you can't get only userID without retrieve whole user info when you use built-in membership provider, only by this
MembershipUser user = Membership.GetUser();
string UserID = user.ProviderUserKey.ToString();
But if you want to have method or property which retrieve only userID, you must re-implement your own membership provider or(it's simply) to implement IPrincipal interface

To return the UserId, use the command bellow in your controller:
User.Identity.GetUserId();
To return the UserName, use:
User.Identity.Name;
To return the user:
var user = db.Users.Find(User.Identity.GetUserId());
Please refer to the post: How to get current user, and how to use User class in MVC5?

As you've guessed, the Membership API doesn't support what you want out of the box. In the past, I've used a helper class instead of creating my own provider. In this case it's pretty simple, maybe something like this:
public static object GetUserId() {
return GetUserId(HttpContext.Current.User.Identity.Name, true);
}
public static object GetUserId(string userName) {
return GetUserId(userName, true);
}
public static object GetUserId(string userName, bool UpdateLastActivity) {
using (SqlConnection c = new SqlConnection(CONNECTION_STRING)) {
string sql = #"
DECLARE #UserId uniqueidentifier
SELECT #UserId=u.UserId
FROM dbo.aspnet_Applications AS a
,dbo.aspnet_Users AS u
,dbo.aspnet_Membership AS m
WHERE
a.LoweredApplicationName=LOWER(#ApplicationName)
AND u.ApplicationId=a.ApplicationId
AND u.LoweredUserName=LOWER(#UserName)
AND u.UserId=m.UserId;
IF #UserId IS NOT NULL AND #UpdateLastActivity=1
UPDATE dbo.aspnet_Users
SET LastActivityDate=#CurrentTimeUtc
WHERE UserId=#UserId;
SELECT #UserId
";
using (SqlCommand cmd = new SqlCommand(sql, c)) {
cmd.Parameters.AddWithValue("#ApplicationName", Roles.ApplicationName);
cmd.Parameters.AddWithValue("#UserName", userName);
cmd.Parameters.AddWithValue("#UpdateLastActivity", UpdateLastActivity);
cmd.Parameters.AddWithValue("#CurrentTimeUtc", DateTime.UtcNow);
object id = null;
c.Open();
id = cmd.ExecuteScalar();
return id != DBNull.Value ? id : null;
}
}
}
Above is pretty similar to what's done in the Membership API when calling GetUser()

You can use MembershipUser.UserName to get the user id or try calling Membership.GetUser(User.Identity.Name) and see if that works for you.

After looking into this further, it seems that the ASP.NET Membership API does not track the user ID after all. It must track just the user name (User.Identity.Name). The ID is not required because Membership.GetUser() can find a user from an ID or user name.
In fact, Membership.GetUser() must simply translate to Membership.GetUser(User.Identity.Name). Since it can obtain the current user from the user name, there is no longer any reason to assume that the current user ID is cached anywhere.
So it appears the ID is not loaded into memory, and the only way to obtain the ID is to load the data from the database (which means loading the entire user record when using the ASP.NET Membership API).

Consider
int userId = WebSecurity.CurrentUserId;
Credit: https://stackoverflow.com/a/15382691/1268910

Related

Login authentication to asp.net and postgresql

I have tried to implement login page in ASP.NET Core MVC and using postgresql as database.
It should check whether user exits in the database table of postgresql and verify, so what is the query to search for user in database and made them sign in?
I have written my code like this:
public IActionResult Login(string seller_email, string seller_password)
{
using var connection = new NpgsqlConnection(connString);
connection.Open();
string main_query = String.Format(#"select exists(select 1 from public.""sellers"" where ""seller_email""='{0}')", seller_email);
using var command_main = new NpgsqlCommand(main_query, connection);
int result_main = command_main.ExecuteNonQuery();
if (result_main < 0)
{
return View(nameof(Create));
}
else
{
return View(nameof(Sign));
}
}
There is a seller table in the database, so just have to check seller exists or not - if exists the have to create a view for it
You are doing a select query, so you must use a command.ExecuteReader.
The ExecuteNonQuery is to be used with statements that update/insert/delete records.
BUT, most importantly, don't concatenate user submitted values into the query string, as it opens the door to SQL injections. Instead, used a parameter.
See the getting started doc for a simple example.

How to get IdentityUser by Username

I have previously worked with Membership through "System.Web.Security.Membership"
Here, you can do the following:
var currentUser = Membership.GetUser();
var otherUser = Membership.GetUser(username);
...giving you a MembershipUser.
Now, with Identity, I can find a load of ways to get the current logged in user.
But no way to get another user.
I can use:
var userStore = new UserStore<IdentityUser>();
var userManager = new UserManager<IdentityUser>(userStore);
var user = userManager.Find(username, password);
But that takes both username and password, with no overload for just username.
How do i get the IdentityUser from only a username?
Almost every answer I find is connected to MVC.
This is for a WCF service, where authorization is made using Identity. And in some cases the user is getting to the site from an other site with a generated "token" - an encrypted string, containing the username. From here, user is logged in and a session-cookie is set, depending on users settings.
Also, is there a shorter way to get UserInformation?
"var currentUser = Membership.GetUser(username);"
is much more convenient than
"var user2 = (new UserManager((new UserStore()))).Find(username, password);"
UserManager has UserManager<TUser>.FindByNameAsync method. You can try using it to find user by name.

Asp.net Identity 2.0 update user

Having a problem with saving user changes to the database, like changing the person's name. I'm using the IdentityModel that is automatically created in a new VS2013 web project using individual authentication. Sadly, the template doesn't allow you to change any user information, other than changing roles. I'm looking around via google, I haven't found much. Anyone implement updating using the base identity code?
This is the closest thing I found:
Updating user data - Asp.net Identity
I haven't been successful at incorporating default template. I've just started using Identity this week, so it might be my lack of understanding that's the problem.
var updatedUser = new ApplicationUser
{
Id = model.UserId,
UserName = model.UserName,
CustomerId = model.CustomerId,
Email = model.EmailAddress,
FirstName = model.FirstName,
LastName = model.LastName,
PhoneNumber = model.PhoneNumber,
};
...
var result = await UserManager.UpdateAsync(updatedUser);
My UserManager is created like this:
return _userManager ?? HttpContext.GetOwinContext().GetUserManager<ApplicationUserManager>();
I get the following error in the browser:
Attaching an entity of type 'ApplicationUser' failed because another entity of the same type already has the same primary key value. This can happen when using the 'Attach' method or setting the state of an entity to 'Unchanged' or 'Modified' if any entities in the graph have conflicting key values. This may be because some entities are new and have not yet received database-generated key values. In this case use the 'Add' method or the 'Added' entity state to track the graph and then set the state of non-new entities to 'Unchanged' or 'Modified' as appropriate
Thanks
The problem I had was I creating an ApplicationUser and saved the object to the database. Since Identity uses Entity Framework under the covers, the entity state of the "updatedUser" object is Added. So Entity Framework tried to INSERT in to the Identity database, causing the conflict. So, you have to get the user and update the returned user object for Entity Framework to know that the entity state is Modified. Here's the working code:
var user = await UserManager.FindByIdAsync(model.UserId);
user.Email = model.EmailAddress;
user.CustomerId = model.CustomerId;
user.FirstName = model.FirstName;
user.PhoneNumber = model.PhoneNumber;
user.LastName = model.LastName;
var result = await UserManager.UpdateAsync(user);
You can also use the AuthContext and update the state to EntityState.Modified. Below is an example. This will allow you to only make one call to the DB instead of two.
AuthContext authContext = new AuthContext();
authContext.Entry(updatedUser).State = EntityState.Modified;

ASP Membership / Role Provider -> how to report the number of users currently logged in?

I'm using the ASP.NET Membership and Role provider. My question is about if there is a built in way to report the number of users who are currently logged in. The question is not get the information about the user who is logged in but from a high level view of everyone who is logged in.
I would like to create a user management dashboard and this metric would be great. also showing the usernames of users who are currently logged in would be useful.
thank you for any help you can provide.
Yes there's a built-in way, see Membership.GetNumberOfUsersOnline(). You can change the "window" for what's considered online, see Membership.UserIsOnlineTimeWindow. (you set the threshold in web.config)
UPDATE:
In response to your comment about getting a list of online usernames...
The Membership API is lacking what you want, so you have to roll your own. You can use the following as starter code, it's similar to what I've done in the past:
public static List<string> GetUsersOnline() {
List<string> l = new List<string>();
string CS = WebConfigurationManager
.ConnectionStrings[YOUR_WEB_CONFIG_KEY]
.ConnectionString
;
string sql = #"
SELECT UserName,LastActivityDate
FROM aspnet_Users
WHERE LastActivityDate > #window
ORDER BY LastActivityDate DESC"
;
using (SqlConnection c = new SqlConnection(CS) ) {
using (SqlCommand cmd = new SqlCommand(sql, c) ) {
DateTime window = DateTime.UtcNow.AddMinutes(
-Membership.UserIsOnlineTimeWindow
);
cmd.Parameters.AddWithValue("#window", window);
c.Open();
using (SqlDataReader r = cmd.ExecuteReader() ) {
while ( r.Read() ) {
l.Add(r.GetString(0));
}
}
}
}
return l;
}
A couple of notes:
Replace YOUR_WEB_CONFIG_KEY above with the key in your web.config <connectionStrings> section.
The LastActivityDate field in the aspnet_Users table (aspnetdb database) is stored as a GMT/UTC Datetime value, so that's why DateTime.UtcNow is used to calculate the window.
Not sure how your Membership database permissions are setup, but you may need to make permission changes, since above code is directly querying the database.

How to Get List of User/Profile from Membership Provider?

I am utilising this Membership Provider. I am stuck with getting the User List + Profile (FirstName, LastName, Title etc etc)
I know that there is a method for Membership.GetAllUsers() but I don't know how to combine this with FirstName, LastName that I stored in Profile Provider.
Thanks
Membership.GetAllUsers() returns a MembershipUserCollection, which you can use to access individual MembershipUser. Example:
MembershipUserCollection users = Membership.GetAllUsers();
string email = users["some_username"].Email;
You can also retrieve ProfileInfo in the similar way:
ProfileInfoCollection profiles = ProfileManager.GetAllProfiles(ProfileAuthenticationOption.All);
DateTime lastActivity = profiles["some_username"].LastActivityDate;
However there are no FirstName and LastName properties by default, unless you manually specified them in your profile provider.
Check out MembershipUser class and ProfileInfo class for more details. You might also wanna check out SqlProfileProvider class as an example of profile provider, unless you already have implemented one.
First when you create a user, create a profile with the same username using:
// Create an empty Profile for the new User
ProfileCommon p = (ProfileCommon) ProfileCommon.Create("username", true);
Then to retrieve it next time..
// Retrieve a particular profile
ProfileCommon userProfile = Profile.GetProfile("username");
Thanks.

Resources