Google Authentication using OWIN Oauth in MVC5 not hitting ExternalLoginCallback function - asp.net-mvc

I am currently upgrading my login process for Google to use OAuth before they depricate their OpenID login method.
The steps I have Identified so far is that I have upgraded the package Microsoft.Owin.Security.Google to version 2.1.0 as this version includes the ability to include options in the UseGoogleAuthentication method.
I have tried to use Alex Wheat's Solution in the link:
Get ExtraData from MVC5 framework OAuth/OWin identity provider with external auth provider
The code in Startup.Auth.cs (which also includes Facebook authentication) goes from this:
var facebookAuthenticationOptions = new FacebookAuthenticationOptions()
{
AppId = "MYAPPID",
AppSecret = "MYSECRET"
};
facebookAuthenticationOptions.Scope.Add("email");
app.UseFacebookAuthentication(facebookAuthenticationOptions);
app.UseGoogleAuthentication();
To this:
var facebookAuthenticationOptions = new FacebookAuthenticationOptions()
{
AppId = "MYAPPID",
AppSecret = "MYSECRET"
};
facebookAuthenticationOptions.Scope.Add("email");
app.UseFacebookAuthentication(facebookAuthenticationOptions);
var googleOAuth2AuthenticationOptions = new GoogleOAuth2AuthenticationOptions
{
ClientId = "MYCLIENTID",
ClientSecret = "MYSECRET",
CallbackPath = new PathString("/en/Account/ExternalLoginCallback"),
Provider = new GoogleOAuth2AuthenticationProvider()
{
}
};
app.UseGoogleAuthentication(googleOAuth2AuthenticationOptions);
After I add options to the Google Authentication, my app does not allow the ExternalLoginCallback action to be called for either google or facebook (no changes to facebook code but the issue still affects it).
On the front end, after clicking the external login buttons, the page redirects me to the link below and returns an empty white screen
https....../en/Account/ExternalLoginCallback#__=_ (There is actually only a single underscore before the = sign, SO syntax removes it if I have it as it appears on my address bar).
for facebook and
https....../en/Account/ExternalLoginCallback
for google. It does not hit the controller method below as it normally does (I have tried to place debug breakpoints within this function and it never gets stopped when there are google authentication options.
// GET: /Account/ExternalLoginCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
If I remove the authentication options from Google Authentication, it just reverts back to the old OpenID login and works fine again.
Am I missing something simple here? or is there something bad happening inside the Owin.Security.Google Library that is causing the problem?

I'm using the default ASP.NET MVC 5 template with Identity Authentication for simplicity, but hopefully this can be modified for different use cases.
StartupAuth.cs
Do not customize the redirect path. It gets replaced by /signin-google anyways and my attempts at getting around that caused "silent" (not in the debugger) Internal Server 500 errors.
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
ClientId = "whatevs.apps.googleusercontent.com",
ClientSecret = "whatevs_secrut",
Provider = new GoogleOAuth2AuthenticationProvider()
});
Make sure to add http://whatever.com/signin-google to https://console.developers.google.com/ in your APIs & auth > Credentials > Redirect URIs section.
RouteConfig.cs
Add a route to a permanent redirect controller action to your routes. Permanent redirects are the only thing that will suffice here. It is not enough to simply direct directly to the Callback URL.
public static void RegisterRoutes(RouteCollection routes)
{
routes.IgnoreRoute("{resource}.axd/{*pathInfo}");
routes.MapRoute(
name: "Google API Sign-in",
url: "signin-google",
defaults: new { controller = "Account", action = "ExternalLoginCallbackRedirect" }
);
routes.MapRoute(
name: "Default",
url: "{controller}/{action}/{id}",
defaults: new { controller = "Home", action = "Index", id = UrlParameter.Optional }
);
}
AccountController.cs
Permanent redirect to the built-in callback method and you should be fine.
[AllowAnonymous]
public ActionResult ExternalLoginCallbackRedirect(string returnUrl)
{
return RedirectPermanent("/Account/ExternalLoginCallback");
}
A template project has been posted on GitHub for reference: https://github.com/Pritchard/Test-AspNetGoogleOAuth2Authentication

Try only this
var googleOAuth2AuthenticationOptions = new GoogleOAuth2AuthenticationOptions
{
ClientId = "MYCLIENTID",
ClientSecret = "MYSECRET",
};
app.UseGoogleAuthentication(googleOAuth2AuthenticationOptions);
This worked for me

Make sure you have also enabled the Google+ API in your developer console. It is an additional step after you have your client and secret

As #Ronen said in the comments, this link should solve the issues with Google OAuth in MVC5:
http://blogs.msdn.com/b/webdev/archive/2014/07/02/changes-to-google-oauth-2-0-and-updates-in-google-middleware-for-3-0-0-rc-release.aspx
Also update the OWIN packages from NuGet. This is how my code looks and is working great:
var googleOptions = new GoogleOAuth2AuthenticationOptions ()
{
ClientId = "xxxxxxxxxx",
ClientSecret = "xxxxxxxxxx",
CallbackPath = new PathString("/signin-google")
};
googleOptions.Scope.Add("email");
app.UseGoogleAuthentication(googleOptions);

Related

Unable to retrieve LinkedIn Authorization Code

I'm building an app that I would use to make posts to LinkedIn. I'm using Visual Studio 2017 (asp.net mvc c#).
I have already created an app in LinkedIn with Client ID and Client Secret.
I have also create an async Function GetCode() my application in Visual Studio.
This function makes a request to this url:
string url = "https://www.linkedin.com/uas/oauth2/authorization?response_type=code&client_id=kjahldj9384&state=DCEEFWKDHIUs5dffef424&redirect_uri=http://localhost:51272/";
When i run the application and this function gets called, I'm redirected to the LinkedIn Authentication page to login. But when I enter the LinkedIn login credentials and hit login, it takes me to this page: https://www.linkedin.com/uas/login-submit which displays the error message:
Request Error
We’re sorry, there was a problem with your request. Please make sure you have cookies enabled and try again.
Or follow this link to return to the home page.
This is my GetCode function:
//Get LinkedIn Code
[HttpGet]
[Route("api/LinkedIn/GetCode")]
public async Task<HttpResponseMessage> GetCode()
{
using (var client = new HttpClient())
{
client.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Content-Type", "application/x-www-form-urlencoded");
string url = "https://www.linkedin.com/uas/oauth2/authorization?response_type=code&client_id=54565kggfh&state=DCEEFW55754FD5dffef424&redirect_uri=http://localhost:57313/";
HttpResponseMessage apiResponseMsg = await client.GetAsync(new Uri(url));
return apiResponseMsg;
}
}
Is there something I'm doing wrong?
First, make sure to add your redirect_uri to OAuth 2.0 Settings in the Linkedin developer portal (under Auth tab). If that isn't the issue, try below.
If you build the same URL, and redirect the page to it instead of using a response message, then you should be sent back to your website with the code as a query parameter (http://localhost:60137/?code=xxxxxxxxxxxxx). I made a working example with a test application:
[HttpGet]
public ActionResult GetCode()
{
//build url from config file
string url = "https://www.linkedin.com/uas/oauth2/authorization?response_type=code&client_id=78z99sg1ncgbma&redirect_uri=http://localhost:60137";
return new RedirectResult(url);
}
Hope this helps.

MVC 5 openid connect on-premises ADFS 4.0 logout issue

I am trying to setup a new MVC 5 project and a new 2016 server with ADFS. I can authenticate with Oauth 2 and OpenID Connect. The issue I have is when I try to logout of the application. The sign out link points to the default /Account/SignOut action. When that action is called I get redirected to the post logout redirect uri which is https://website.com. This redirect loops until the browser errors out with "website redirected you too many times".
This is my signout method.
public void SignOut()
{
string callbackUrl = Url.Action("SignOutCallback", "Account", routeValues: null, protocol: Request.Url.Scheme);
// Send an OpenID Connect sign-out request.
HttpContext.GetOwinContext().Authentication.SignOut(
new AuthenticationProperties { RedirectUri = callbackUrl },
OpenIdConnectAuthenticationDefaults.AuthenticationType, CookieAuthenticationDefaults.AuthenticationType
);
}
If I just call
HttpContext.GetOwinContext().Authentication.SignOut(CookieAuthenticationDefaults.AuthenticationType)
the logout fails but at least the constant redirects don't happen.
Is there an overload of the Authentication.Signout() method which logs me out and then redirects to the post logout redirect uri?
I worked with MS support and determined that Sign Out is not currently supported for OID Connect with ADFS 2016. It may be part of a future release, but as of now there is no set release date for support.
ADFS 4.0 (on Windows Server 2016) supports "Single log-out". If you use Microsoft.AspNetCore.Authentication.OpenIDConnect package (this is the newer one, for ASP.NET Core) it works seamlessly.
If you query the well known endpoint for OpenID Connect configuration, which is https://your-server/adfs/.well-known/openid-configuration, you will find a key end_session_endpoint which contains the logout URL you should use.
Also, you can check the documentation: https://learn.microsoft.com/en-us/windows-server/identity/ad-fs/development/ad-fs-logout-openid-connect
For instance, in order to invoke it from your controller, you would would use an action method like this one:
[HttpGet]
public IActionResult SignOut()
{
var callbackUrl = Url.Action(nameof(SignedOut), "Account", values: null, protocol: Request.Scheme);
return SignOut(
new AuthenticationProperties { RedirectUri = callbackUrl },
CookieAuthenticationDefaults.AuthenticationScheme,
OpenIdConnectDefaults.AuthenticationScheme);
}
You might find this sample useful (even though it is for Azure ADFS, it works for local installs as well): https://github.com/Azure-Samples/active-directory-dotnet-webapp-openidconnect-aspnetcore

Owin Middleware per controller in asp.net web API

I am using the following JWT middleware to authorize all controllers with a valid JWT and it works as expected. I need to enable client certificate based authorization on one of the controllers. I understand that i can create a new middleware and plug it into owin pipeline which validates client certificates.
How to decide which controller to use which middleware? As far as i know OWIN does not have any knowledge of any controller. Please suggest
public void ConfigureAuth(IAppBuilder app)
{
TextEncodings.Base64Url.Decode("IxrAjDoa2FqElO7IhrSrUJELhUckePEPVpaePlS_Xaw");
var issuer = System.Configuration.ConfigurationManager.AppSettings["issuer"].ToString();
var audience = System.Configuration.ConfigurationManager.AppSettings["ClientID"].ToString();
var secret = TextEncodings.Base64Url.Decode(System.Configuration.ConfigurationManager.AppSettings["ClientSecret"].ToString());
// Api controllers with an [Authorize] attribute will be validated with JWT
app.UseJwtBearerAuthentication(
new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { audience },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret),
}
});
}
Owin "knows" which controller to authorise because of the [Authorise] tag on top of it.
Why don't you do the same for the other system? Create another tag which then fires up the other authentication system? It's the easiest way to keep a clean system working.
You can more than likely use the owin source code to get some inspiration and se exactly how things are done.

LinkedIn Authentication via Oauth2 returns null result (error=access_denied)

I moved my ASP.NET MVC web application from membership to Identity authentication and since that I cannot authenticate on LinkedIn anymore.
The Facebook authentication is still working fine but the LinkedIn is always returning a null loginInfo after the GetExternalLoginInfo call.
For the LinkedIn I'm using the Owin LinkedIn provider: LinkedIn APIs for .NET. I also unsuccessful tried to follow this post from Jerrie Pelser.
The Application calls the ExternalLogin Action that executes the ExecuteResult method and calls back the ExternalLoginCallback (after I allow access to the application). As I stated before, the method AuthenticationManager.GetExternalLoginInfoAsync() always returns a null loginInfo.
I checked the application settings in the LinkedIn and everything seems to be OK.
Ops! I almost forgot to say that the LinkedIn is returning back the URL with a generic error message: "GET /Account/ExternalLoginCallback?error=access_denied HTTP/1.1"
I can Authenticate using the DotNetOpenAuth.Clients (hosted github) but I'd like to just use the Identity.
Startup.Auth.cs
var linkedInOptions = new LinkedInAuthenticationOptions();
linkedInOptions.ClientId = "Xxxxx";
linkedInOptions.ClientSecret = "Yyyyyyy";
linkedInOptions.Scope.Add("r_fullprofile");
linkedInOptions.Provider = new LinkedInAuthenticationProvider()
{
OnAuthenticated = async context =>
{
context.Identity.AddClaim(new System.Security.Claims.Claim("LinkedIn_AccessToken", context.AccessToken));
}
};
linkedInOptions.SignInAsAuthenticationType = DefaultAuthenticationTypes.ExternalCookie;
app.UseLinkedInAuthentication(linkedInOptions);
ExternalLogin
public ActionResult ExternalLogin(string provider, string returnUrl)
{
// Request a redirect to the external login provider
return new ChallengeResult(provider, Url.Action("ExternalLoginCallback", "Account", new { ReturnUrl = returnUrl }));
}
CallBack Action
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Login");
}
LinkedIn CallBack URI
http://localhost:3279/signin-linkedin
After some researches and a visit the NuGet package repository I found a prerelease version of Owin.Security.Providers that worked like a charm. I just had to install it from package manager console and the issue with the null return from the LinkedIn External Login has gone.
Install-Package Owin.Security.Providers -Pre
Caution: Please be aware that the use of pre release packages may cause unexpected problems.

asp.net identity 2.1 google authentication

I'm using trying to use Google authentication in an ASP.NET MVC application.
For testing purposes I'm using the template app generated by VS2013 Update 4
In Google settings the return URLs are properly set and Google+ API is turned on. The app works fine when I publish it to an azure website. I can login using Google accounts without any problems.
However I'd like to deploy it on premises but here we have a reverse proxy setup which works like this:
the server sees itself as server01.mysite.com but this is an
internal name
outside world sees it as www.mysite.com (certain paths are
reverese proxied to the server01.mysite.com
Essentially www.mysite.com/myapp is reverse proxied to server01.mysite.com/myapp
With this setup I can't seem to use Google authentication. GetExternalLoginInfoAsync returns null and the app redirects itself to the login page.
By default the system generates a redirectUri using the private hostname. I tried changing it to the public address but this does not solve the problem.
Below is what I did at startup.auth.cs
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
ClientId = "...",
ClientSecret = "...",
Provider = new GoogleOAuth2AuthenticationProvider
{
OnApplyRedirect = context =>
{
var redirectUri = context.RedirectUri.Replace("server01", "www");
context.Response.Redirect(redirectUri);
},
}
});
Is there anyway I can make Google authentication work in a setup like this?
Thanks
To achieve this one has to tell the app to use the outside URL earlier so that the relevant hashes are built taking that into account. So instead of changing the redirect URI at the OnApplyRedirect call this before UseGoogleAuthentication:
app.Use((context, next) =>
{
context.Request.Host = new HostString(
context.Request.Host.Value.Replace("server01", "www"));
return next();
}
);
and remove the Provider=... from UseGoogleAuthentication
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
ClientId = "...",
ClientSecret = "..."
});

Resources