How admin access link without logging in - asp.net-mvc

Can anyone give me solution for this issue (or article, keyword ):
One user create a product, and back-end will be send mail for admin to approve the product, this mail has contained the link:
http://localhost:11260/#/productHandle/116796
I want admin don't need login in system, and can access this link.
because the current code in Global.asax check cookies:
protected void Application_PostAuthenticateRequest(Object sender, EventArgs e)
{
HttpCookie ck = Request.Cookies[FormsAuthentication.FormsCookieName];
if (ck != null && !String.IsNullOrEmpty(ck.Value))
{
FormsAuthenticationTicket fat = FormsAuthentication.Decrypt(ck.Value);
UserProfile profile = JsonConvert.DeserializeObject<UserProfile>(fat.UserData);
MyPrincipal myPrincipal = new MyPrincipal(profile.Username);
myPrincipal.UsrProfile = profile;
HttpContext.Current.User = myPrincipal;
}
}
---after access successfully, i intended to encrypt this link. but step above don't work..
thank for help me!

We have a similar application that involves a user requesting access to our document imaging system, and then an email is sent to our application admins for approval. The general idea looks like this:
[HttpPost]
public ActionResult RegisterNewUser(NewUser model)
{
if (ModelState.IsValid)
{
var pwSalt = repository.CreateSalt();
var newUser = User();
newUser.Username = model.Username;
newUser.Email = model.Email;
newUser.PasswordSalt = pwSalt;
newUser.Password = repository.CreatePasswordHash(model.Password, pwSalt);
newUser.IsApproved = false;
newUser.RegisterDate = DateTime.Now;
db.Users.Add(newUser);
db.SubmitChanges();
ConfirmationEmail(model.Username);
return RedirectToAction("RegistrationSuccess");
}
}
The above code is the post-action for a new user who's just registered for our application. It adds the user to the db table. The model contains fields like name, username, email, etc (I also have a function "CreatePasswordHash" which uses the user's password and a generated SALT to created an encrypted password). One thing to note is that the Users table contains a column "IsApproved", which sets to "false" by default. Without this value changing to "true", the user won't be able to use the application. Within this action is another function named "ConfirmationEmail" which is where we send an email to one of the admins for approval.
public void ConfirmationEmail(string username)
{
var user = db.Users.Single(u => u.Username == username);
string mailBody = "A new user requires approval for document imaging.\n\n"
+ "Name: " + user.Name + "\n\n"
+ "\n\n"
+ "If you approve, follow the link below: \n\n"
+ "http://example.com/Imaging/Account/Approval/" + user.UserId;
MailMessage msg = new MailMessage();
msg.Priority = MailPriority.High;
msg.To.Add(admin#example.com);
msg.Subject = "New User registration approval";
msg.Body = mailBody;
msg.IsBodyHtml = false;
SmtpClient smtp = new SmtpClient();
smtp.Send(msg);
msg.Dispose();
}
The above function takes a username param from the original registration action, which we use to find the user record (note: Username in the Users table is a UNIQUE column, so it will fail validation if it's not unique). I create the body of the email using plain text, and attach the URL of the approval action for the admin to confirm approval. Everything else is standard mail code (SMTP settings are stored in my web.config file).
Account controller -- Approval action:
public ActionResult Approval(int id)
{
var user = db.Users.Find(id);
user.IsApproved = true;
db.SubmitChanges();
EmailApproval(id);
return View(user);
}
Simple action that changes the "IsApproved" flag to true, which grants the user access to the application. The View that is returned contains basic user information displayed in a nice confirmation window. Lastly, there's a function "EmailApproval" which sends an email to the user about being approved. It uses the same emailing practices as the other function above.
Hope this helps.

You can make action insecure ([AllowAnonymous] will help you) and implement security for this action via IP address.
In this case you need to implement you own Authorization filter (read there how to do it) and check that it is called from predefined admin's IP and call is local (use HttpRequest.IsLocal for checking this).
Frankly, I never will do something insecure in my projects so you have to think twice before doing something similar for this.
From other hand you can extend your session timeout.

Related

Register and Login by Phone Number in Asp.net Core Identity

Is it possible to don't use Email to register and login in asp.net core identity?
Users just register by mobile number and login by SMS verification.
One possible way is to setup identity as two factor authentication. Instead of email use username to store the mobile number. To do this set RequireUniqueEmail = false in ApplicationUserManager.Create.
You'll need to add your own code to retrieve the number and validate it. Make sure it always has the same format as it should match the username.
In the code where the username is verified skip the password check (since password is null), but do check the number. Send an SMS with code and continue with the flow.
You can add your own logic to create and verify the code and how long it is valid.
If you don't rely in Username in your application, (Like you don't show it to user ever), then you can set Username to a guid and skip the email part.
For registration,
var identityUser = new User
{
Username= Guid.NewGuid(),
PhoneNumber= phoneNumber
};
var result = await _signInManager.UserManager.CreateAsync(identityUser, user.Password);
For Login,
var user = await _signInManager.UserManager.Users
.SingleOrDefaultAsync(x =>
x.NormalizedEmail == usernameParam
|| x.PhoneNumber== usernameParam);
if (user == null)
{
return Forbid();
}
var result = await _signInManager.CheckPasswordSignInAsync(user, passwordParam, false);
if (result.Succeeded)
{
// return new TokenResponse(token, refreshToken);
}

Associate a new Google or Facebook login with an existing account

I have just followed instructions in this article to add Google as a login provider to my MVC 5 app. All seems to work OK, but when I log in via Google, it wants me to register the email/username provided by Google as a new account in my app. If I leave the email as is and click the 'Register' button, it tells me that address is already taken, as I have earlier registered on my app's own login provider.
How can I tweak the default code generated by the MVC project template to allow me to associate the Google login with an existing local account?
P.S. I have exactly the same problem with Facebook.
I totally second the points raised by #Chris Pratt
However i'm not sure the code used is enough to do what the OP asked.
adding this code in the default block inside ExternalLoginCallback should do the job
ApplicationUser user = await UserManager.FindByNameAsync(loginInfo.Email);
if (user != null)
{
var addLoginResult = await UserManager.AddLoginAsync(user.Id, loginInfo.Login);
if (addLoginResult.Succeeded)
{
await SignInManager.SignInAsync(user, isPersistent: false, rememberBrowser: false);
return RedirectToLocal(returnUrl);
}
}
I think Identity handles it the way it does on purpose, since there's no real way to verify the identity of the user by email alone coming from a third party. While the risk may be relatively low, it is possible that someone could create an account with a third party like Facebook, using an email address that does not belong to them, and then use that third-party login to impersonate an account at another website attached to the same email.
As a result, Identity only lets you create a new account with an external login when signing in, not attach to an existing one. However, once the user is authenticated by other means, methods are provided to associate additional logins.
If you're not concerned by the relatively mild security risk associated with just assuming that if the email matches it's the same person, then you need only modify ExternalLoginCallback in AccountController.cs to attempt to find the user by email:
var user = await UserManager.FindByEmailAsync(loginInfo.Email);
And then sign them in:
await SignInManager.SignInAsync(user);
Here is how I was able to solve this issue that you are having. This solution will allow you to register on the site with your email and if you then try to use Google to log in with the same Email address, you will not be requested to register and you will not generate any errors; you will be allowed to login if the email address that you logged in with locally is the same as your google account email. I edited the Default ExternalLoginCallBack code that VS2015 generated with an if / else statement, which is checking for an existing email that matches the login Email. I hope this helps you with your question, for I had the same issue and could not find an answer anywhere. My multiple post requests were ignored and thankfully, I read one of the answers from this post that led me to my own solution that is working for me on VS2015 Core.
[HttpGet]
[AllowAnonymous]
public async Task<IActionResult> ExternalLoginCallback(string returnUrl = null, string remoteError = null)
{
if (remoteError != null)
{
ModelState.AddModelError(string.Empty, $"Error from external provider: {remoteError}");
return View(nameof(Login));
}
var info = await _signInManager.GetExternalLoginInfoAsync();
if (info == null)
{
return RedirectToAction(nameof(Login));
}
// Sign in the user with this external login provider if the user already has a login.
var email = info.Principal.FindFirstValue(ClaimTypes.Email);
var user = await _userManager.FindByNameAsync(email);
if (user != null)
{
await _signInManager.SignInAsync(user, isPersistent: false);
return RedirectToLocal(returnUrl);
}
else
{
// If user does not already exists, invite User to register.
var result = await _signInManager.ExternalLoginSignInAsync(info.LoginProvider, info.ProviderKey, isPersistent: false);
if (result.Succeeded)
{
_logger.LogInformation(5, "User logged in with {Name} provider.", info.LoginProvider);
return RedirectToLocal(returnUrl);
}
if (result.RequiresTwoFactor)
{
return RedirectToAction(nameof(SendCode), new { ReturnUrl = returnUrl });
}
if (result.IsLockedOut)
{
return View("Lockout");
}
else
{
// If the user does not have an account, then ask the user to create an account.
ViewData["ReturnUrl"] = returnUrl;
ViewData["LoginProvider"] = info.LoginProvider;
email = info.Principal.FindFirstValue(ClaimTypes.Email);
return View("ExternalLoginConfirmation", new ExternalLoginConfirmationViewModel { Email = email });
}
}
}
TLDR you need to go through all scenarios manually in your ExternalLoginConfirmation function, and have a database table to be able to match membership user id with OAuth user id. This way you can "associate" multiple OAuth accounts with single local account.
Below is a code snippet from one of our projects - hopefully it's clear enough
public ActionResult ExternalLoginCallback()
{
var returnUrl = HttpContext.Request.QueryString["returnUrl"];
var result = OAuthWebSecurity.VerifyAuthentication(Url.Action("ExternalLoginCallback", new { ReturnUrl = returnUrl }));
if (result.IsSuccessful == false)
{
return this.View("ExternalLoginFailure", result);
}
// Login user if provider represents a valid already registered user
if (OAuthWebSecurity.Login(result.Provider, result.ProviderUserId, createPersistentCookie: false))
{
return this.RedirectToLocal(returnUrl);
}
// If the current user is logged in already - add new account
if (User.Identity.IsAuthenticated)
{
OAuthWebSecurity.CreateOrUpdateAccount(result.Provider, result.ProviderUserId, User.Identity.Name);
return this.RedirectToLocal(returnUrl);
}
var membershipUser = Membership.GetUser(result.UserName);
// so user is new - then create new membership account
if (membershipUser == null)
{
MembershipCreateStatus createStatus;
membershipUser = Membership.CreateUser(username: result.UserName, password: this.GetTempPassword(), email: result.UserName, status: out createStatus);
if (createStatus == MembershipCreateStatus.Success)
{
this.emailService.SendWelcome(this, (Guid)membershipUser.ProviderUserKey);
// Associate social network account with created membership account
OAuthWebSecurity.CreateOrUpdateAccount(result.Provider, result.ProviderUserId, result.UserName);
OAuthWebSecurity.Login(result.Provider, result.ProviderUserId, createPersistentCookie: false);
return this.RedirectToLocal(returnUrl);
}
// The problem occured while creating membership account
this.ViewBag.Error = MembershipErrorNameProvider.FromErrorCode(createStatus);
return this.View("CreateMembershipAccountFailure");
}
// If membership account already exists -> Associate Social network account with exists membership account
OAuthWebSecurity.CreateOrUpdateAccount(result.Provider, result.ProviderUserId, result.UserName);
OAuthWebSecurity.Login(result.Provider, result.ProviderUserId, createPersistentCookie: false);
return this.RedirectToLocal(returnUrl);
}
and OAuthWebSecurity is a helper class which deals with all providers you support:
public static class OAuthWebSecurity
{
....
public static bool Login(string providerName, string providerUserId, bool createPersistentCookie)
{
var context = new HttpContextWrapper(HttpContext.Current);
var provider = GetOAuthClient(providerName);
var securityManager = new OpenAuthSecurityManager(context, provider, OAuthDataProvider);
return securityManager.Login(providerUserId, createPersistentCookie);
}
public static void CreateOrUpdateAccount(string openAuthProvider, string openAuthId, string userName)
{
var user = UserRepository.FindByName(userName);
if (user == null)
{
throw new MembershipUserNotFoundException();
}
var userOAuthAccount = UserOAuthAccountRepository.Find(openAuthProvider, openAuthId);
if (userOAuthAccount == null)
{
UserOAuthAccountRepository.InsertOrUpdate(new UserOAuthAccount
{
OAuthProvider = openAuthProvider,
OAuthId = openAuthId,
UserId = user.Id
});
}
else
{
userOAuthAccount.UserId = user.Id;
}
UserOAuthAccountRepository.Save();
}
}
This is a normal behavior in MVC template, since external logins attempt to create user and if the user's email (or external identity) already exists, attempt to signin. Furthermore, external login provider tries to assign an optional and unique identifier in your application (locally) separate from your external identity. But, the following is strange, as you said:
If I leave the email as is and click the 'Register' button, it tells
me that address is already taken, as I have earlier registered on my
app's own login provider.
Which should work since each user's external id should be unique in other sites (I believe) unless you have registered multiple external accounts with the same identity since the table structures looks like this:
The column UserId will be matched with the bellow table's UserId column:
The message will be showed rarely, when a user tries to assign duplicate Username which will be enumerated with your email or identity name in other sites (e.g. somename#gmail.com will be somename as Username)
This following link shows you how to build an ASP.NET MVC 5 web application that enables users to log in using OAuth 2.0 with credentials from an external authentication provider, such as Facebook, Twitter, LinkedIn, Microsoft, or Google. For simplicity, this tutorial focuses on working with credentials from Facebook and Google existing accounts.
Refer this:
MVC 5 App with Facebook, Twitter, LinkedIn and Google OAuth2 Sign-on (C#)

How to extend the Owin OAuth to accommodate the Application level login confirmation

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.

How can I deny anonymous users from directly calling action methods?

I'm busting my head against the wall here. Please help.
I've developed an MVC 4.0 web application.
I am using the Facebook SDK to log in to the website.
For argument sake, I have 2 action methods: Index and Info.
Index is where the user clicks the "Log in using Facebook" button, and after he enters his credentials, he is supposed to be redirected to Info action method. (from there he can log out).
This is the Index method:
[AllowAnonymous]
public ActionResult Index()
{
return View();
}
I have this in my web.config:
<authentication mode="Forms">
<forms loginUrl="~/Home/Index" timeout="2880" />
</authentication>
These are the settings I entered at Facebook developers for my app:
Canvas URL:
http://localhost/[AppName]/
Secure Canvas URL:
https://localhost/[AppName]/
Site URL:
http://localhost/[AppName]/
Valid OAuth redirect URIs:
http://localhost/
and also
http://localhost/[AppName]/home/index
The problem is:
I want to make the Info action method only available to authorized users, so naturally, I decorated it with [Authorized] attribute:
[Authorize]
public ActionResult Info()
{
var client = new FacebookClient();
var oauthResult = client.ParseOAuthCallbackUrl(Request.Url);
// Build the Return URI form the Request Url
var redirectUri = new UriBuilder(Request.Url);
redirectUri.Path = Url.Action("Info", "Home");
// Exchange the code for an access token
dynamic result = client.Get("/oauth/access_token",
new { client_id = AppId,
redirect_uri = redirectUri.Uri.AbsoluteUri,
client_secret = SecId,
code = oauthResult.Code });
// Read the auth values
string accessToken = result.access_token;
Session["access_token"] = accessToken;
DateTime expires = DateTime.UtcNow.AddSeconds(Convert.ToDouble(result.expires));
// Get the user's profile information
dynamic me = client.Get("/me",
new
{
fields = "first_name,last_name,email,picture",
access_token = accessToken
});
var userInfo = new InfoModel()
{
firstName = me.first_name,
lastName = me.last_name,
emailId = me.email,
picture = me.picture,
accessToken = result.access_token
};
return View("Info", userInfo);
}
So now I have two unwated scenraios: I can either log in to my app, BUT after log in, i'm redirected to Home/Index, instead to Home/Info.
OR
I can log in properly (I am redirected to Home/Info after log in, as expected) BUT I can also browse directly to Info action method (since I had to remove the [Authorize] attribute in order to log in.
It's like a Rubik's Cube!
I'm sorry the post is long, but I had to elaborate.
I found and answer.
It's involve a little bit of a work-around, but it's as good:
[AllowAnonymous]
public ActionResult Info()
{
if (Request.Url.AbsoluteUri.Contains("Home/Info?code"))
{
var client = new FacebookClient();
var oauthResult = client.ParseOAuthCallbackUrl(Request.Url);
// Build the Return URI form the Request Url
var redirectUri = new UriBuilder(Request.Url);
redirectUri.Path = Url.Action("Info", "Home");
// Exchange the code for an access token
dynamic result = client.Get("/oauth/access_token",
new
{
client_id = AppId,
redirect_uri = redirectUri.Uri.AbsoluteUri,
client_secret = SecId,
code = oauthResult.Code
});
// Read the auth values
string accessToken = result.access_token;
Session["access_token"] = accessToken;
DateTime expires = DateTime.UtcNow.AddSeconds(Convert.ToDouble(result.expires));
// Get the user's profile information
dynamic me = client.Get("/me",
new
{
fields = "first_name,last_name,email,picture",
access_token = accessToken
});
var userInfo = new InfoModel()
{
firstName = me.first_name,
lastName = me.last_name,
emailId = me.email,
picture = me.picture,
accessToken = result.access_token
};
FormsAuthentication.SetAuthCookie(userInfo.emailId, true);
return View("Info", userInfo);
}
else
return View("Index");
}
As you can see, I had a condition at the beginning of the Action method:
If, and only if the url contains the word "code" then the user can authenticate, otherwise, he is redirected back to the first page.
If the word "code" exists, it means that the user has a token and therefore he has already entered his credentials correctly, so now his details can be extracted.
At the end of the extraction process, just before the redirection to the view, I added:
FormsAuthentication.SetAuthCookie(userInfo.emailId, true);
In order to save the cookie. This is what required for the [Authorize] attribute to function correctly, otherwise the code will act as though you are not authenticated, even though you are.
Now I can safely add an [Authorize] attribute to all the action methods I wish to make available to authenticated users only.
Don't forget to add [AllowAnonymous] attribute to every action method that are used as part of the authentication process, otherwise the user won't be able to browse their respective views.
BTW,
If the user tries to type he will get an error, since he still needs the token.

Using OpenID/OpenAuth in MVC3 app with overridden authentication method

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.

Resources