How to use DotNetOpenAuth to login to websites? - asp.net

I want to do is, if the users are logged into gmail and if they go to my website they automatically get logged in.
I am doing it in the following way... maybe there is a better way of doing it.
In my website I have a place for uses to give their gmail address so my website knows gamil address of the registered user.
So when they go to my website I want to know whether they are logged into gmail and what is their gmail address.
How should I find this information using DotNetOpenAuth?
I found following code from the web and it is authenticating the user. But i have to press the button and go to gmail login every time.
if the user is already using gmail I don’t have to ask the user for login i can use it.
How do i modify this code to achieve that?
static string openidurl = "https://www.google.com/accounts/o8/id";
protected void Page_Load(object sender, EventArgs e)
{
//The Response
OpenIdRelyingParty openid = new OpenIdRelyingParty();
var response = openid.GetResponse();
if (response != null)
{
switch (response.Status)
{
case AuthenticationStatus.Authenticated:
var fetch = response.GetExtension<FetchResponse>();
string email = "";
if (fetch != null)
{
email = fetch.GetAttributeValue(WellKnownAttributes.Contact.Email);
}
break;
}
}
}
protected void Button1_Click(object sender, EventArgs e)
{
using (OpenIdRelyingParty openid = new OpenIdRelyingParty())
{
IAuthenticationRequest request = openid.CreateRequest(openidurl);
var fetch = new FetchRequest();
fetch.Attributes.AddRequired(WellKnownAttributes.Contact.Email);
request.AddExtension(fetch);
// Send your visitor to their Provider for authentication.
request.RedirectToProvider();
}
}

It sounds like what you're asking for is "single-sign-on", where a visitor to your site who is already logged into Google is immediately logged into your site when they first visit it, rather than after clicking a "Google Login" button on your site.
The short answer is you can't do this. The longer answer is that you can get close.
The first and hard restriction is that first-time visitors to your site will never get automatically signed in, because Google and the user don't yet trust your site. Every user has to explicitly log in once, with Google asking the user "do you want to log into this site and remember this choice?" If they say yes, then in the future when the user is already logged into Google and visits your site, they can click the Google Login button on your site and they'll never see Google -- they'll just be immediately logged into your site.
So the next question is how do you remove the requirement on the user to click "google Login". You can accomplish this by when an unauthenticated user visits your site, you can immediately redirect them to your log in page, which will immediately initiate the "Google Login" flow (the OpenIdRelyingParty.CreateRequest(google).RedirectToProvider() call), using "immediate mode". This will fail if the user isn't logged into Google and trust your site, but the impact will be the user won't see a Google login screen if they do trust your site, but will rather be immediately logged in.

You might find my answer useful: What OpenID solution is really used by Stack Overflow?
I've also made a simple blog post about it: http://codesprout.blogspot.com/2011/03/using-dotnetopenauth-to-create-simple.html
My examples are with MyOpenID, but gmail should work the same way. The OpenID provider basically takes care of the log in, including the case when they're already logged in with the provider.
Update:
In an ASP.NET (in this case ASP.NET MVC) application you would create a cookie when the user is successfully logged in and you would check the cookie to determine if the user is logged in. As I said, please see the links above for detailed code examples and an explanation of how it all works. Here are two code samples from the Controller where I demonstrate how to check if the user is logged in:
// **************************************
// URL: /User/LogIn
// **************************************
public ActionResult LogIn()
{
if (User.Identity.IsAuthenticated) // <--- CHECKS IF THE USER IS LOGGED IN
{
return RedirectToAction("Profile", "User");
}
Identifier openID;
if (Identifier.TryParse(Request.QueryString["dnoa.userSuppliedIdentifier"], out openID))
{
return LogIn(new User { OpenID = openID }, Request.QueryString["ReturnUrl"]);
}
else
{
return View();
}
}
[HttpPost]
public ActionResult LogIn(User model, string returnUrl)
{
string openID = ModelState.IsValid?model.OpenID:Request.Form["openid_identifier"];
if (User.Identity.IsAuthenticated)//<--- CHECKS IF THE USER IS LOGGED IN
{
return RedirectToAction("Profile", "User");
}
else if (!string.IsNullOrEmpty(openID))
{
return Authenticate(openID, returnUrl);
}
else if(ModelState.IsValid)
{
ModelState.AddModelError("error", "The OpenID field is required.");
}
// If we got this far, something failed, redisplay form
return View(model);
}

Related

ASP.NET Core 5: Best way to use common URL for login page and root page

In an ASP.NET Core 5 web app with Identity (and in earlier versions), the URL for the login page defaults to:
https://[yourhost]/account/login
and once you're logged in, the root of your project lives at:
https://[yourhost]
But in many (most?) web apps, the login page shares the URL with the root page. Using Facebook as an exmaple, before I'm logged in, the URL is https://www.facebook.com, and after logging in the URL is the same. What's the best way to achieve this in ASP.NET Core?
The best solution I have so far is:
(AccountController.cs)
[HttpGet]
[AllowAnonymous]
[Route("/")]
public async Task<IActionResult> Login(string returnUrl = null)
{
// If authenticated, serve the application page.
if (User.Identity.IsAuthenticated)
{
return View("~/Views/Weather/Index.cshtml");
}
// Otherwise, serve the login page.
else
{
ViewData["ReturnUrl"] = returnUrl;
return View();
}
}
This doesn't feel great because , from AccountController, I'm returning a view that should correspond to WeatherController.
Ideally, I'd like to say, "for URL '/' , only if the user is logged in, match this endpoint in the weather controller. Else, fall back to this other endpoint in the account controller." I was thinking this might be possible with a custom route constraint, but I'm not necessarily passing any parameters to the URL. I was also looking into DynamicRouteValueTransformer, but wasn't successful.
Or, is it misguided for me to try to have the login page share a URL with the root page? Thanks for any suggestions.
Change Default Settings of Identity Framework,
services.ConfigureApplicationCookie(options =>
{
// Cookie settings
options.Cookie.HttpOnly = true;
options.ExpireTimeSpan = TimeSpan.FromMinutes(5);
options.LoginPath = "/Home/Index/; // here
options.AccessDeniedPath = "/Identity/Account/AccessDenied";
options.SlidingExpiration = true;
});
after that check, if the user is authenticated return a different view, and if the user is not authenticated return a different view that user can login.

Weird login behaviour using Azure AD B2C with Xamarin iOS

We have a Xamarin Forms app that uses Azure AD B2C for login. I have followed this example code https://github.com/Azure-Samples/active-directory-b2c-xamarin-native
The Android version works perfectly. The workflow is seamless and works as expected.
On the iOS version we are getting some weird behaviour though.
Here is the flow on the iOS version of the app.
User launches the app and is presented with the Azure AD B2C login screen (correct)
User enters their credentials (correct)
The app redirects the user back to the login screen (with the username and password fields blank) (wrong)
After a few seconds the app then logs them in and navigates them to the main screen
After entering their credentials the user should be immediately navigated to the app main screen, NOT back to the login screen.
Once they are logged in, subsequently launching the app correctly logs them in. The problem is only on first login.
I can't seem to find any help on this anywhere. Is it the Safari browser, the token cache, something else? The code and workflow are correct as the Android version works perfectly, so it's something specific to the iOS app.
UPDATE
After some investigation it seems that this method may be causing the issue.
public override bool OpenUrl(UIApplication app, NSUrl url, NSDictionary options)
{
AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(url);
return true;
}
According to the docs
this logic is meant to ensure that once the interactive portion of the
authentication flow is concluded, the flow goes back to MSAL
But it doesn't. It redirects the user back to the OnAppearing() method of the login page where it attempts (again) to acquire a token (which it does).
Is there any way to fix the behaviour of this method?
AuthenticationContinuationHelper.SetAuthenticationContinuationEventArgs(url)
UPDATE2
Here is the login code (simplified for clarity)
protected override async void OnAppearing()
{
base.OnAppearing();
// Check to see if we have a User in the cache already.
try
{
IEnumerable<IAccount> accounts = await AuthenticationService.PCA().GetAccountsAsync();
var account = this.GetAccountByPolicy(accounts, ApplicationConstants.SignUpSignInPolicy);
AuthenticationResult ar = await AuthenticationService.PCA().AcquireTokenSilentAsync(ApplicationConstants.Scopes, account, ApplicationConstants.Authority, false);
}
catch (Exception)
{
// Doesn't matter, we go in interactive mode
this.OnSignIn();
}
}
private async void OnSignIn()
{
try
{
IEnumerable<IAccount> accounts = await AuthenticationService.PCA().GetAccountsAsync();
var account = this.GetAccountByPolicy(accounts, ApplicationConstants.SignUpSignInPolicy);
AuthenticationResult ar = await AuthenticationService.PCA().AcquireTokenAsync(ApplicationConstants.Scopes, account, App.UiParent);
}
catch (Exception ex)
{
InstanceManager.LoggingHelper().TrackError(ex);
// Checking the exception message
// should ONLY be done for B2C
// reset and not any other error.
if (ex.Message.Contains("AADB2C90118"))
{
this.OnPasswordReset();
}
else
{
await DisplayAlert(StringConstants.ExceptionText, StringConstants.AuthenticationError, StringConstants.CloseDialog);
this.OnSignIn();
}
}
}

How to prevent user logout by WebSecurity.ChangePassword()

I was trying to let the users change their password in settings. In the ajax page, I was using
WebSecurity.Logout();
So I thought logging out is because of this code. But then I noticed that the user logs out, even if this line isn't present after Password change success. So I tried to Google it. And on many places I found that this code removes the Cache and Cookies, so the user is logged out.
My Question: Is there any way to prevent User logout? Or can I save the Cookie or cache so that the user is still logged in after password change success.
You should use WebSecurity.ChangePassword, this will renew the current cookie with all new crendentials and then send it back into the response.
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult Manage(ChangePassward model)
{
bool changePasswordSucceeded = false;
try
{
changePasswordSucceeded = WebSecurity.ChangePassword(User.Identity.Name, model.OldPassword, model.NewPassword);
}
catch (Exception)
{
changePasswordSucceeded = false;
}
if (changePasswordSucceeded)
{
return RedirectToAction("Some Page here", new { Message = "Success" });
}
else
{
ModelState.AddModelError("", "The current password is incorrect or the new password is invalid.");
}
}
This is an old question, so apologies for bringing it back to life, but for anyone else who faces this issue:
You already have their username and password in this method. Log them back in.

Disable the User from Navigating after Logging Out.

My application is in Asp.Net MVC3, i have two Users(Admin and general).Im maintaining the cookies of the Logged In users.
Below is my Cookie code.
public static void setCookiestring Password ,string UserName)
{
HttpCookie MyCookie= new HttpCookie("MyCookies");
MyCookie["Password"] = Password;
MyCookie["UserName"] = UserName;
MyCookie.Expires.Add(new TimeSpan(0,30,0));
HttpContext.Current.Response.Cookies.Add(MyCookies);
}
Below is code of how my cookie Expires when user Logs Out
public static bool logout()
{
HttpCookie MyCookie= new HttpCookie("MyCookies");
MyCookie.Expires = DateTime.UtcNow.AddDays(-1);
HttpContext.Current.Response.Cookies.Add(MyCookies);
return true;
}
I have tried to check the Cookie on every Index() of Controller.If the Cookie exists it should navigate to desired page else it should go to Home.
if (!Cookie.CheckCookie())
{
//use the current url for the redirect
filterContext.HttpContext.Response.Redirect("~/Home/Index", true);
}
When the user clicks Log Out they are Redirected to Home,but after clicking Back button,the last visited page is can be viewed.
What can i do so that when the user clicks on LogOut and if they click back button they should still get Redirected to Home and not to Last Visited Page.
Please Suggest
One way to prevent this is to exclude all authenticated pages from being cached on the client side by setting the appropriate response headers. You may take a look at the following post for an example of an action filter that you could apply to the authenticated part of your site.

How to handle "Remember me" in the Asp.Net Membership Provider

Ive written a custom membership provider for my ASP.Net website.
Im using the default Forms.Authentication redirect where you simply pass true to the method to tell it to "Remember me" for the current user.
I presume that this function simply writes a cookie to the local machine containing some login credential of the user.
What does ASP.Net put in this cookie? Is it possible if the format of my usernames was known (e.g. sequential numbering) someone could easily copy this cookie and by putting it on their own machine be able to access the site as another user?
Additionally I need to be able to inercept the authentication of the user who has the cookie. Since the last time they logged in their account may have been cancelled, they may need to change their password etc so I need the option to intercept the authentication and if everything is still ok allow them to continue or to redirect them to the proper login page.
I would be greatful for guidance on both of these two points. I gather for the second I can possibly put something in global.asax to intercept the authentication?
Thanks in advance.
For me the solution was differentiating between a browser-session auth cookie (not to be confused with the asp.net session cookie) and a persistent one - setting a low expiration will create a persistent cookie meaning it gets remembered when the browser is closed and re-opened within the expiration time. The following works for me:
public void SetAuthenticationCookie(LoginView loginModel)
{
if (!loginModel.RememberMe)
{
FormsAuthentication.SetAuthCookie(loginModel.Email, false);
return;
}
const int timeout = 2880; // Timeout is in minutes, 525600 = 365 days; 1 day = 1440.
var ticket = new FormsAuthenticationTicket(loginModel.Email, loginModel.RememberMe, timeout);
//ticket.
string encrypted = FormsAuthentication.Encrypt(ticket);
var cookie = new HttpCookie(FormsAuthentication.FormsCookieName, encrypted)
{
Expires = System.DateTime.Now.AddMinutes(timeout),
HttpOnly = true
};
HttpContext.Current.Response.Cookies.Add(cookie);
}
FormsAuthentication and MembershipProviders are two completely different things, still they are made to work with each other very well. If you have written a persistent cookie ["Remember Me"] then next time, you can simply call Membership.GetUser() which will return you the MembershipUser instance of the currently logged in user or null if no user is logged in.
So first time when user arrives and authenticates with "Remember Me", you shall write a persistent cookie as following.
FormsAuthentication.RedirectFromLoginPage(strUserName, true);
Assuming user does not logout and leaves webpage and comes back after sometime. You can simply call MembershipUser.GetUser() as following and check if the user is already logged from the persistent cookie written by FormsAuthentication.
MembershipUser someUser = Membership.GetUser();
if(someUser == null)
{
FormsAuthentication.SignOut();
FormsAuthentication.RedirectToLoginPage();
}
else
{
//Take where logged in users go.
}
You can do this check on your Login page itself or main landing page to intercept the User account to check if he needs to change the password or if the account is disabled as in your case.
EDIT
There are two ways to do this.
1.) Check for authentication as mentioned above in Session_Start event in global.asax and set a session key that becomes available on all pages for that particular session.
2.) Another way is too keep a common application wide common PageBase class that inherits from System.Web.UI.Page and acts as base page class for all your asp.net pages. On the Page Load of the common PageBase class check for the authentication as mentioned above. You will have to carefully write conditional redirection in this case since this might head towards infinite redirection with no end since it will run on Page_Load of all page from the common PageBase class.
public class PageBase : System.Web.UI.Page
{
/// <summary>
/// Initializes a new instance of the Page class.
/// </summary>
public Page()
{
this.Load += new EventHandler(this.Page_Load);
}
private void Page_Load(object sender, EventArgs e)
{
try
{
AuthenticateUser();
}
catch
{
//handle the situation gracefully.
}
}
private AuthenticateUser()
{
MembershipUser someUser = Membership.GetUser();
if(someUser == null)
{
FormsAuthentication.SignOut();
FormsAuthentication.RedirectToLoginPage();
}
else
{
//Take where logged in users go.
}
}
}
//in your asp.net page code-behind
public partial class contact : PageBase
{
protected void Page_Load(object sender, EventArgs e)
{
}
}

Resources