I created one database and tables to store the user login values and credentials.
asp.net is providing aspnet_regsql tool to create a database for the membership related activities. But I dont want to use it. Thats why I created another database. Now I want to connect this database to my project. I changed in web.config file for the connectionstring parameter to my newly created database. But I am unable to login. It is giving following error message.
Could not find stored procedure 'dbo.aspnet_CheckSchemaVersion'
How to work with this. Is there any step by step procedures are there!! If so please provide.
Is there any thing to change rather than the connection string in the web.config file?
You need to create a membership provider to connect to your custom tables for authentication. MSDN has some documentation on the subject. You can also view a video on the subject at ASP.NET. Here are the links.
http://msdn.microsoft.com/en-us/library/f1kyba5e(v=vs.100).aspx
http://www.asp.net/web-forms/videos/how-do-i/how-do-i-create-a-custom-membership-provider
The main method for validation is going to be the ValidateUser method, you will override this method to provide authentication.
public sealed class CustomMembershipProvider : MembershipProvider
{
// implement other methods
public override bool ValidateUser(string username, string password)
{
try
{
var user = // GET USER OBJECT HERE
if (user != null)
{
string name = // set username
// Set your forms authentication ticket
FormsAuthenticationTicket ticket = new FormsAuthenticationTicket(1, user.ID.ToString(), DateTime.Now, DateTime.Now.AddMinutes(30), false, name, FormsAuthentication.FormsCookiePath);
HttpCookie authCookie = new HttpCookie(FormsAuthentication.FormsCookieName, FormsAuthentication.Encrypt(ticket));
HttpContext.Current.Response.Cookies.Add(authCookie);
return true;
}
}
catch
{
}
return false;
}
// Other implementations
}
If you have roles in your application you may also want to implement a custom role provider:
http://msdn.microsoft.com/en-us/library/8fw7xh74(v=vs.100).aspx
Related
I have inherited an application with database. The database has following tables related to authentication and authorization.
User Table
UserName
Password
UserTypeId
UserType Table
UserTypeId
UserTypeDesc
The User Type table stores the roles for the user e.g. Admin, Editor, etc.
If I want to implement authorization like below
[Authorize(Roles="Admin, Editor")]
public IHttpActionResult GetOrders()
{
//Code here
}
Where and what should I code so that the roles are available to the authorize attribute ?
Edit
I already have a database. So I cannot use the AspNetUserRoles or AspNetRoles tables. I need to set the roles using my custom tables.
Edit2
As asked by #Nkosi, here is code snippet of how authentication is implemented. The actual implementation calls the business layer service and performs encryption and other stuff but I have simplified the snippet
public HttpResponseMessage Authenticate(User user)
{
var isValid = myRepository.Exists(a => a.UserName == user.UserName && a.Password == user.Password);
if(isValid)
{
FormsAuthentication.SetAuthCookie(user.UserName,false);
}
}
This method is called from the login page where user enters the UserName and Password
Using these Answers for reference
Having Trouble with Forms Authentication Roles
FormsAuthentication Roles without Membership
After having set the auth cookie on login like you did originally,
you do the following in your Global.asax.cs
protected void Application_AuthenticateRequest(object sender, EventArgs e)
{
var authCookie = HttpContext.Current.Request.Cookies[FormsAuthentication.FormsCookieName];
if (authCookie != null)
{
var ticket = FormsAuthentication.Decrypt(authCookie.Value);
FormsIdentity formsIdentity = new FormsIdentity(ticket);
ClaimsIdentity claimsIdentity = new ClaimsIdentity(formsIdentity);
//get the user from your custom tables/repository
var user = myUserRepository.GetUserByEmail(ticket.Name);
if(user!=null){
var userTypeId = user.UserTypeId;
var role = myUserTypeRepository.GetUserTypeById(userTypeId);
if(role != null) {
//Assuming the roles for the user e.g. Admin, Editor, etc.
// is in the UserTypeDesc property
claimsIdentity.AddClaim(new Claim(ClaimTypes.Role, role.UserTypeDesc));
}
}
ClaimsPrincipal claimsPrincipal = new ClaimsPrincipal(claimsIdentity);
System.Threading.Thread.CurrentPrincipal = claimsPrincipal ;
if (System.Web.HttpContext.Current != null) {
System.Web.HttpContext.Current.User = claimsPrincipal ;
}
}
}
The nice thing about how they implemented it is that it handles Claims based roles using the ClaimsIdentity and ClaimsPrincipal objects, without putting the roles in the user's cookie. It also handles authentication in the Global.asax.cs file without having to resort to putting in custom authorize attributes.
Your question was very easy. You just need to sync these 2 tables with AspNetUserRoles and AspNetRoles tables respectively. Actually, Authorize attribute by default checks these two tables. So your roles need to reflect in them. These tables are made by default by EF if you select MVC template project.
I'm migrating a SaaS app from Classic ASP to .NET MVC5 and will use EF6 Database First. The login form for end users is customisable by each tenant (on their own subdomain but pointing to the same web application). We wish to use the existing database schema and the new authentication & authorization filters.
For example, a user on one tenant may login by entering their first name, surname and a code generated by our system. A user on another tenant may login by entering their email address and a password. Additionally, each tenant has a separate administrator login which uses a username and password. Another tenant may use LDAP authentication against a remote AD server.
Is there a definitive best practice way of doing custom authentication?
Almost every article appears to suggest different ways of accomplishing this: simply setting FormsAuthentication.SetAuthCookie, using a custom OWIN provider, override AuthorizeAttribute, etc.
In Classic ASP, we queried the database to find out the type of login for that tenant, displayed the appropriate fields on the login screen and then on post back, checked the fields match what's in the database and then set the session variables appropriately which were checked on each page request.
Thanks
I find that Identity framework is very flexible in terms of authentication options. Have a look on this bit of authentication code:
var identity = await this.CreateIdentityAsync(applicationUser, DefaultAuthenticationTypes.ApplicationCookie);
authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
This is pretty standard run of the mill authentication part in Identity, you'll find this in every Identity sample on the web. If you look closely it is very flexible - all you need for authentication is ApplicationUser object that framework does not care how you get.
So in theory you can do things like this (pseudocode, I did not try to compile this):
// get user object from the database with whatever conditions you like
// this can be AuthCode which was pre-set on the user object in the db-table
// or some other property
var user = dbContext.Users.Where(u => u.Username == "BillyJoe" && u.Tenant == "ExpensiveClient" && u.AuthCode == "654")
// check user for null
// check if the password is correct - don't have to do that if you are doing
// super-custom auth.
var isCorrectPassword = await userManager.CheckPasswordAsync(user, "enteredPassword");
if (isCorrectPassword)
{
// password is correct, time to login
// this creates ClaimsIdentity object from the ApplicationUser object
var identity = await this.CreateIdentityAsync(user, DefaultAuthenticationTypes.ApplicationCookie);
// now we can set claims on the identity. Claims are stored in cookie and available without
// querying database
identity.AddClaim(new Claim("MyApp:TenantName", "ExpensiveClient"));
identity.AddClaim(new Claim("MyApp:LoginType", "AuthCode"));
identity.AddClaim(new Claim("MyApp:CanViewProducts", "true"));
// this tells OWIN that it can set auth cookie when it is time to send
// a reply back to the client
authenticationManager.SignIn(new AuthenticationProperties() { IsPersistent = isPersistent }, identity);
}
Using this authentication, you have set a few claims on the user - they are stored in the cookie and available everywhere via ClaimsPrincipal.Current.Claims. Claims are essentially a collection of key-value pairs of strings and you can store there anything you like.
I usually access claims from the user via extension method:
public static String GetTenantName(this ClaimsPrincipal principal)
{
var tenantClaim = principal.Claims.FirstOrDefault(c => c.Type == "MyApp:TenantName");
if (tenantClaim != null)
{
return tenantClaim.Value;
}
throw new ApplicationException("Tenant name is not set. Can not proceed");
}
public static String CanViewProducts(this ClaimsPrincipal principal)
{
var productClaim = principal.Claims.FirstOrDefault(c => c.Type == "MyApp:CanViewProducts");
if (productClaim == null)
{
return false;
}
return productClaim.Value == "true";
}
So in your controller/view/business layer you can always call to ClaimsPrincipal.Current.GetTenantName() and in this case you'd get "ExpensiveClient" back.
Or if you need to check if a specific feature is enabled for the user, you do
if(ClaimsPrincipal.Current.CanViewProducts())
{
// display products
}
It is up to you how you store your user properties, but as long as you set them as claims on the cookie, they will be available.
Alternatively you can add claims into the database for every user:
await userManager.AddClaimAsync(user.Id, new Claim("MyApp:TenantName", "ExpensiveClient"));
And this will persist the claim into the database. And by default, Identity framework adds this claim to the user when they login without you needing to add it manually.
But beware, you can't set too many claims on a cookie. Cookies have 4K limit set by browsers. And the way Identity cookie encryption works it increases encoded text by about 1.1, so you can have roughly 3.6K of text representing claims. I've run into this issue here
Update
To control access to controllers via claims you can use the following filter on the controller:
public class ClaimsAuthorizeAttribute : AuthorizeAttribute
{
public string Name { get; private set; }
public ClaimsAuthorizeAttribute(string name)
{
Name = name;
}
public override void OnAuthorization(AuthorizationContext filterContext)
{
var user = HttpContext.Current.User as ClaimsPrincipal;
if (user.HasClaim(Name, Name))
{
base.OnAuthorization(filterContext);
}
else
{
filterContext.Result = new RedirectToRouteResult(new RouteValueDictionary()
{
{"controller", "errors"},
{"action", "Unauthorised"}
});
}
}
}
and then use this attribute on controllers or separate actions like this:
[ClaimsAuthorize("Creating Something")]
public ActionResult CreateSomething()
{
return View();
}
User will require "Create Something" claim on them to access this action, otherwise they will be redirected to "Unauthenticated" page.
Recently I've played with claims authentication and made a prototype application similar to your requirement. Please have a look on the simple version: https://github.com/trailmax/ClaimsAuthorisation/tree/SimpleClaims where claims are stored individually for each user. Or there is more complex solution where claims belong to a role and when users login, role claims assigned to the user: https://github.com/trailmax/ClaimsAuthorisation/tree/master
There's two components you need. The authentication itself and the strategy each user gets for authentication.
The first is easy and is accomplished with these two lines...
var identity = await UserManager.CreateIdentityAsync(user,
DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties()
{ IsPersistent = isPersistent }, identity);
When a user is Signed In, they get an identity which contains the user's claims on roles and who they are. These are given to the user as a cookie. After this point you just decorate controllers with [Authorize] to make sure only authenticated users can log in. Pretty standard here.
The only complicated part in the problem is the second part; The strategy for how each user gets authenticated set by the admin.
Some pseudocode for how this could work in actions is this...
// GET: /Account/Login
[AllowAnonymous]
public ActionResult Login(int tenantId)
{
var tenant = DB.GetTenant(tenantId);
return View(tenant);
}
In your view you would output the authentication strategy for the tenant. That may be email and password, a code and email, or whatever your requirements.
When the user enters their info and clicks to login, you then have to determine what strategy they were using, and check to see if their information matches.
//
// POST: /Account/Login
[HttpPost]
[AllowAnonymous]
[ValidateAntiForgeryToken]
public async Task<ActionResult> Login(LoginViewModel model)
{
var tenant = DB.GetTenant(model.tenantId);
//If user info matches what is expected for the tenants strategy
if(AuthenticateUserInfo(tenant, model.UserInputs))
{
//Sign the user in
var identity = await UserManager.CreateIdentityAsync(user,
DefaultAuthenticationTypes.ApplicationCookie);
AuthenticationManager.SignIn(new AuthenticationProperties()
{ IsPersistent = isPersistent }, identity);
}
}
I did a lot of hand-waving in the second part because of the complicated nature of how dynamic it is. Overall you should use the same strategies you used in your legacy application to generate the right inputs and such. Nothing has changed there, only the way you sign in is going to be different.
Using Visual Studio 2013 Update 3 you can create a new Web Application that comes with MVC5, EF6 and Identity already installed. Here is how to select Identity when you create a new Application:
With MVC Template selected, click Change Authentication and the highlighted window will pop up. Individual User Accounts = Identity. Click ok and continue.
Having done that, you have created an application with Identity. You can now customize your login and registration as follows.
You want to look at your AccountController.cs in the Controllers folder. Here you will find the script for Registration and Login.
If you look at the
public async Task<ActionResult> Register(RegisterViewModel model)
function, you'll notice it contains:
IdentityResult result = await UserManager.CreateAsync(new ApplicationUser() { UserName = newUser.UserName }, newUser.Password);
This is where the user gets created. If you want to use Identity, you should save the users username and password. You can use an e-mail as the username if you want. etc.
After doing that, I add the user a specified role (I find the user and then add it to the role):
ApplicationUser userIDN = UserManager.FindByName(newUser.UserName);
result = await UserManager.AddToRoleAsync(userIDN.Id, "Admin");
In my scenario, I have created an additional extended table where I hold their address, phone number, etc. In that table, you can hold any additional login information. You can add these new entries before or after creating the users account in Identity. I would create the extended information and then create the Identity account just to be sure.
IMPORTANT: For any scenarios where a user is logging in with something that is not a username or e-mail address that isn't saved into via Identity, you will have to do a custom solution.
Example: User types in their first name, surname and the code. You could do two things: Save the first name and surname into the username field of identity and the code into the password and verify the login that way
OR
you would check your custom table for those properties and make sure they match, if and when they do you could call this little beauty:
await SignInAsync(new ApplicationUser() { UserName = model.UserName }, isPersistent: false);
Once you call that SignInAsync function, you can go ahead and direct them to your protected page.
NOTE: I'm creating the ApplicationUser on the function call but if you use it more than once it would be ideal for you to declare the ApplicationUser as follows:
ApplicationUser user = new ApplicationUser() { UserName = model.UserName };
NOTE #2: If you don't want to user Async methods, those functions all have non-async versions of them.
Note #3: At the very top of any page using UserManagement, it is being declared. Make sure if you are creating your own controller that wasn't generated by Visual Studio to use Identity, you include the UserManagement declaration script at the top inside of the class:
namespace NameOfProject.Controllers
{
[Authorize]
public class AccountController : Controller
{
public AccountController() : this(new UserManager<ApplicationUser>(new UserStore<ApplicationUser>(new ApplicationDbContext()))) { }
public AccountController(UserManager<ApplicationUser> userManager) { UserManager = userManager; }
public UserManager<ApplicationUser> UserManager { get; private set; }
Please let me know if you have any questions and I hope this helps.
I want to allow people by registering with their email id, password and confirming password in my asp.net 4.0 application. How can I send the confirmation link to their entered email, and there by showing the profile strength in their individual profiles? with the backend as sql server 2010
You have to send the confirmation link with unique confirmation id as query string parameter in link. When user clicks on that link you can validate that link for -
Is link already used?
Is link has valid confirmation number.
Then if it is valid link then allow user to confirm there email id. The unique confirmation id needs to be stored in the database for given email id. So that you can validate for it. Also in the same database table you can have additional field where it will record the time stamp when that id is generated and email send to user.
Introduction
Authentication and authorization as fundamental part of a web application was tremendously simplified with arrival of .NET 2.0. Basic authentication and authorization can be performed using different DBMS systems. There are numerous examples implementing authentication using Access database. One such example can be found in the article ASP.NET 2.0 Authentication using Access Database. In this article the example implements bare minimum to achieve desired goal, namely authentication and authorization using access database. However, authentication and authorization is only first part of the user management system. Second fundamental part of user management is allowing users to create an account at will. In such instances, users can sign up and become members of the community without administrator intervention. Furthermore, the user should be able to provide contact information (email) so that he/she can be contacted if necessary. Contact information must be verified. This article focuses on very simple ways to achieve such goal.
CreateUserWizard
As already suspected, .NET 2.0 provides necessary tools for quick and effective implementation of user registration. The component of interest is CreateUserWizard control that can be found under Login components. CreateUserWizard component provides preformatted set of components that provide the user with opportunity to enter necessary information. By default, CreateUserWizard provides user with option to enter name, password, email, security question, and answer to security question. In addition, CreateUserWizard provides the user with function to reenter the password to confirm user intentions.
In our example, we will focus on minimalistic approach to implement user signup and email conformation. As such we will not implement security question, but rather focus on username, password, and email to be confirmed. This article is continuation of ASP.NET 2.0 authorization using Access Database and it reuses Role and Membership provider code as presented in that article. Here, I’ll only present additions to the above mentioned article. In addition, full source code is included and demonstration can be found in demo section.
Extending Roles and Membership Providers
As already mentioned, I’ll focus on additional implementation necessary for CreateUserWizard implementation. Methods implemented in ASP.NET 2.0 authentication and authorization using Access Database are explained in mentioned article. However, there are some modifications that will be stressed at relevant time.
Looking at AccessMembershipProvider, I quickly realized that there are multiple methods that must be implemented to correctly use CreateUserWizardcontrol. First we need to be able to eliminate security question option. In order to eliminate security question, membership provider must return false for RequiresQuestionAndAnswer property of membership provider. Implementation of this property is similar to implementation of connection string property. We simply add variable of type bool that retrieves its value from web configuration file. Web configuration file must contain requiresQuestionAndAnswer as shown below:
<membership defaultProvider="AccessMembershipProvider">
<providers>
<clear/>
<add name="AccessMembershipProvider"
type="AccessProvider.AccessMembershipProvider"
connectionStringName="UsersDB"
requiresQuestionAndAnswer="false"/>
</providers>
</membership>
And the setting is parsed within Initialize method as following:
public override void Initialize(string name,
System.Collections.Specialized.NameValueCollection config)
{
base.Initialize(name, config);
m_strDBConnection =
ConfigurationManager.ConnectionStrings[config["connectionStringName"]].ConnectionString;
m_bRequiresQuestionAndAnswer =
(config["requiresQuestionAndAnswer"].ToLower().CompareTo("true") == 0);
}
As we can see, the code above simply extracts the setting from the web.config file and applies it to appropriate property. CreateUserWizard control reads the same property and displays security question according to the property value. Now that security question does not have to be entered, we can look at what happens when user enters the data and tries to create a user. Through experiment, I realized that the AccessMembershipProvider method CreateUser is called (obviously). Intuitively, I concluded that this method is responsible for the code that adds the user to the database. Following is method implementation:
public override MembershipUser CreateUser(string username, string password, string email,
string passwordQuestion, string passwordAnswer,
bool isApproved, object providerUserKey,
out MembershipCreateStatus status)
{
MembershipUser user = null;
using (OleDbConnection conn = new OleDbConnection(m_strDBConnection))
{
try
{
OleDbCommand command = new OleDbCommand("INSERT INTO Users " +
"(UUSERNAME, UPASSWORD, EMAIL, PROVIDER_KEY) "+
"VALUES (#Param1, #Param2, #Param3, #Param4)",
conn);
Guid guid = Guid.NewGuid();
command.Parameters.AddWithValue("#Param1", username);
command.Parameters.AddWithValue("#Param2", password);
command.Parameters.AddWithValue("#Param3", email);
command.Parameters.AddWithValue("#Param4", guid.ToString());
conn.Open();
command.ExecuteNonQuery();
string[] users = {username};
Roles.AddUsersToRole(users, "User");
conn.Close();
user = new MembershipUser("AccessMembershipProvider",
username, guid, email, null, null, false, false,
DateTime.Now, DateTime.Now, DateTime.Now,
DateTime.Now, DateTime.Now);
status = MembershipCreateStatus.Success;
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.Message);
status = MembershipCreateStatus.UserRejected;
}
}
return user;
}
CreateUser method simply adds the user to the database and returns MembershipUser object filled with the information as entered in CreateUserWizard. My first implementation of the method did not include adding the user to the “User” group using role provider. However, later investigation showed that the user was simply added without group assignment. Realizing that the user must be in a group, I added the following implementation to AccessRoleProvider code:
public override void AddUsersToRoles(string[] usernames, string[] roleNames)
{
using (OleDbConnection conn = new OleDbConnection(m_strDBConnection))
{
try
{
for (int i = 0; i < usernames.Length; ++i)
{
OleDbCommand command = new OleDbCommand("INSERT INTO UsersInRoles "+
"(ROLE_NAME, UUSERNAME) "+
"VALUES (#Param1, #Param2)",
conn);
command.Parameters.AddWithValue("#Param1", roleNames[i]);
command.Parameters.AddWithValue("#Param2", usernames[i]);
conn.Open();
command.ExecuteNonQuery();
conn.Close();
}
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.Message);
}
}
}
Now that the user is added to the database and assigned to appropriate group, we need to implement code to confirm user information, particularly email. Searching through MSDN documentation I found that CreateUserWizard provides method for automatic email notification. However, such approach was not appropriate because of lack of information provided within the event. Our conformation will utilize GUID that is written to DB when the user is created. The user will be activated only when the activation with such GUID is performed. Finding out GUID was impractical using send email event and easier solution was found, namely CreatedUser event of CreateUserWizard component.
Email Notification
As already mentioned, the user should be able to use the account only after successful email conformation. For conformation, the user should receive an email containing the link that will automatically activate the account. Once the account is activated, the account is ready to be used. Following is CreatedUser event implementation:
protected void CreateUserWizard1_CreatedUser(object sender, EventArgs e)
{
CreateUserWizard wizard = (CreateUserWizard)sender;
MembershipUser user = Membership.GetUser(wizard.UserName);
if (user != null)
{
try
{
SmtpClient smtpClient = new SmtpClient("smtpServer");
smtpClient.UseDefaultCredentials = true;
smtpClient.Send("yourEmail", user.Email, "Account Conformation Email",
"hst/Confirm.aspx?id="+user.ProviderUserKey.ToString());
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.Message);
}
}
}
Implementation of email notification is very simple. This is due to the fact that .NET library provides very simple means to communicate with any SMTP server. It is important to note the line smtpClient.UseDefaultCredentials = true. In this way, smtpClient object looks into web.config for SMTP server settings, i.e. username, password, and the host to be used. Following section is expected by SmtpClient object to be present in web.config file:
<system.net>
<mailSettings>
<smtp>
<network
host="smtp.hst.com "
userName="username"
password="password" />
</smtp>
</mailSettings>
</system.net>
Settings are self explanatory. The smtpClient simply needs the host name, username, and password to be used. Once the user is created, CreatedUser event is raised and email is sent. The content of the email is simply a link to Confirm.aspx page with an id parameter. The id parameter is GUID assigned specifically for that user. It is important to note that username could be used too, but GUID provides slightly more security.
Another extension necessary for CreatedUser event to function properly is capability of AccessMembershipProvider object to be able to provide user information based on username. In order to achieve such functionality, AccessMembershipProvider contains method that takes username as parameter and reads user information from database. Following is implementation:
public override MembershipUser GetUser(string username, bool userIsOnline)
{
MembershipUser user = null;
using (OleDbConnection conn = new OleDbConnection(m_strDBConnection))
{
try
{
/* Create command */
OleDbCommand command =
new OleDbCommand("SELECT UUSERNAME, PROVIDER_KEY, EMAIL FROM Users " +
"WHERE UUSERNAME=#Param1", conn);
command.Parameters.AddWithValue("#Param1", username);
/* Open connection */
conn.Open();
/* Run query */
OleDbDataReader reader = command.ExecuteReader();
/* Check if we have something */
bool bResult = reader.HasRows;
if (bResult)
{
reader.Read();
user = new MembershipUser("AccessMembershipProvider", reader.GetString(0),
new Guid(reader.GetString(1)),
reader.GetString(2), "", "", true, false,
DateTime.Now, DateTime.Now, DateTime.Now,
DateTime.Now, DateTime.Now);
}
/* Close connection */
conn.Close();
return user;
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.Message);
}
return user;
}
}
Once again, the implementation is trivial. We simply find info about user with specified username by running query against our users table.
Email Confirmation
Once the email is sent the user receives an email with a link to confirm his/her signup and email. The link is of the form http://"hst"/Confirm.aspx?id=XXXXXXXXXXXXXXXXXX, where XXXXXXXXXXXXXXXX is GUID determined in CreateUser method. Once the user clicks on the email the browser loads Confirm.aspx page with appropriate parameter. As suspected, Confirm.aspx is responsible for account activation. The account activation is performed by modifying ACTIVATED flag within user table. Following is Confirm.aspx implementation:
protected void Page_Load(object sender, EventArgs e)
{
if (Request.QueryString["id"] != null)
{
using (OleDbConnection conn = new OleDbConnection(
ConfigurationManager.ConnectionStrings["UsersDB"].ConnectionString))
{
try
{
OleDbCommand command =
new OleDbCommand("UPDATE Users SET ACTIVATED=#Param1 "+
"WHERE PROVIDER_KEY=#Param2", conn);
command.Parameters.AddWithValue("#Param1", true);
command.Parameters.AddWithValue("#Param2", Request.QueryString["id"]);
conn.Open();
command.ExecuteNonQuery();
conn.Close();
Response.Write("Your account has been activated. "+
"Please log in <a href='Default.aspx'>here</a>");
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.Message);
}
}
}
}
The implementation is self explanatory. The code simply modifies activated flag of the user with specified GUID.
At this point it is important to state somewhat obvious fact. The AccessMembershipProvider’s ValidateUser method must be modified to consider activated flag. In other words, only activated users can be validated and consequently logged in. For completeness, following is the implementation:
public override bool ValidateUser(string username, string password)
{
using (OleDbConnection conn = new OleDbConnection(m_strDBConnection))
{
try
{
/* Create command */
OleDbCommand command = new OleDbCommand("SELECT UUSERNAME, UPASSWORD FROM Users " +
"WHERE UUSERNAME=#Param1 " +
"AND UPASSWORD=#Param2 " +
"AND ACTIVATED=#Param3",
conn);
command.Parameters.AddWithValue("#Param1", username);
command.Parameters.AddWithValue("#Param2", password);
command.Parameters.AddWithValue("#Param3", true);
/* Open connection */
conn.Open();
/* Run query */
OleDbDataReader reader = command.ExecuteReader();
/* Check if we have something */
bool bResult = reader.HasRows;
/* Close connection */
conn.Close();
return bResult;
}
catch (Exception ex)
{
System.Diagnostics.Trace.WriteLine(ex.Message);
}
return false;
}
}
Access Database
Access database utilized is extended version of the database utilized in ASP.NET 2.0 Forms Authentication article with addition of GUID and ACTIVATED fields. GUID is stored in PROVIDER_KEY field. In addition, some fields like username and password had to be renamed because of the conflict with Access DB system.
Source
Click here to demo
I'm trying to customize my own implementation of ExtendedMembershipProvider. I have no idea what the GetUserIDFromOauth method is supposed to do? I see it is throwing an exception by default, and that it is supposed to return the user ID from the open auth provider.
I fail to see how this is supposed to be done, unless this means find if that user exists in the system? Is that it's purpose? I find the lack of documentation confusing...
Thanks.
GetUserIdFromOAuth is a method used by ExtendedMembershipProvider class to find User.Id in your table of users in your web application database based on Provider and ProviderUserId that you get from OAuth or OpenId Provider. After getting Provider and ProviderUserId data for a specified user, you need to save it in your database.
It returns throw new NotImplementedException(); by default. You need to implement this method to return an integer of your User.Id from your application database.
This is a sample implementation:
public override int GetUserIdFromOAuth(string provider, string providerUserId)
{
using (var context = new YourApplicationEntities())
{
// Try to find user with certain Provider and ProviderUserId
var user = context.Users.SingleOrDefault(
q => q.Provider == provider &&
q.ProviderUserId == providerUserId
);
if (user != null)
{
return user.Id;
}
}
return -1;
}
This implementation assumed that you have Provider and ProviderUserId field in your User table. If this information saved in a different table, you just need to modify the LINQ to return the desired result.
I implemented custom authentication/authorization based on this tutorial http://www.mattwrock.com/post/2009/10/14/Implementing-custom-Membership-Provider-and-Role-Provider-for-Authinticating-ASPNET-MVC-Applications.aspx
It works fine. I implemented it because I don't want to have stored procedures in my database and possibility to use different RDBMS.
But I have one issue here. I authenticate user but I don't know how to store UserId somewhere so when I need to get something from database based on UserID to get it. Something like:
List<Product> products = productsRepository.GetProductsByUserId(User.UserID);
How to make this?
BTW Is there any better way to make custom authentication/authorization than this from this tutorial?
Thanks
If you've actually implemented all the methods, and you're populating the built-in MembershipUser, then simply Membership.GetUser().ProviderUserKey will return ther UserId.
in my solution I use
Docent docent = DocentRepo.GetByID(User.Identity.Name);
maybe this can be of use to you
If you're using FormsAuthentification you can encode some custom user data in your cookie / ticket besides UserName. But you have to manually create a FormsAuthenticationTicket and set UserData property to the user's id during login. This way you can have both UserName & UserId.
// during login
var authCookie = FormsAuthentication.GetAuthCookie(userName, createPersistentCookie);
var ticket = FormsAuthentication.Decrypt(authCookie.Value);
// preserve data in your configuration
var ticketWithId = new FormsAuthenticationTicket(
version: ticket.Version,
name: ticket.Name,
issueDate: ticket.IssueDate,
expiration: ticket.Expiration,
isPersistent: ticket.IsPersistent,
userData: userId);
authCookie.Value = FormsAuthentication.Encrypt(ticketWithId);
_context.Response.Cookies.Add(authCookie);
Then you can have an extension method for Controller or HttpContext classes:
public int? GetUserId(this Controller controller) {
var identity = (FormsIdentity)controller.User.Identity;
int id;
if (int.TryParse(identity.Ticket.UserData, out id))
return id;
return null;
}
But if you don't need both UserId & UserName data for your user, than HttpContext.User.Identity.Name or Controller.User.Identity.Name will have the username for your current user