LinkedIn Authentication via Oauth2 returns null result (error=access_denied) - asp.net-mvc

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.

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.

Microsoft Account Authentication - AuthenticationManager.GetExternalLoginInfoAsync() always null

I have created a MVC web application and trying to use Microsoft Account authentication.
I have created the App on https://apps.dev.microsoft.com
After obtaining the App-ID and App-Secret I pasted both in my web.config
In my StartupAuth.cs I have configured Microsoft authentication with this code snippet:
var microsoftOptions = new MicrosoftAccountAuthenticationOptions()
{
ClientId = Config.MSAppId,
ClientSecret = Config.MSAppSecret,
};
app.UseMicrosoftAccountAuthentication(microsoftOptions);
It is possible to authenticate with my Windows account
In the account controller I am trying to create the account from data returned from the authentication provider. The snippet below only shows the important part.
public async Task<ActionResult> ExternalLoginCallback(string returnUrl)
{
var userName = string.Empty;
var eMail = string.Empty;
var loginInfo = await AuthenticationManager.GetExternalLoginInfoAsync();
if (loginInfo == null)
{
return RedirectToAction("Index", "Home");
}
// Benutzer mit diesem externen Anmeldeanbieter anmelden, wenn der Benutzer bereits eine Anmeldung besitzt
var result = await SignInManager.ExternalSignInAsync(loginInfo, isPersistent: false);
...
await AuthenticationManager.GetExternalLoginInfoAsync() always returns null.
The configuration in the developer portal looks like this:
I have tried to add scopes for e-mail and basic information, but this seems to be deprecated and the authentication page shows an error.
In the debug console in Visual Studio I get the following error:
Microsoft.Owin.Security.MicrosoftAccount.MicrosoftAccountAuthenticationMiddleware Error: 0 : Authentication failed
System.Net.Http.HttpRequestException: Der Antwortstatuscode gibt keinen Erfolg an: 400 (Bad Request).
bei System.Net.Http.HttpResponseMessage.EnsureSuccessStatusCode()
bei Microsoft.Owin.Security.MicrosoftAccount.MicrosoftAccountAuthenticationHandler.d__4.MoveNext()
I have created a clean test project from scratch, to find out what the cause for the problem is. This is what I have found out:
After the initial configuration of the authentication all Microsoft.Owin.Security.* components are on Version 3.0.1 and Microsoft Account authentication (not Windows Azure authentication - this is not part of the problem) works fine
After updating Microsoft.Owin.Security.* to Version 3.1.0 some changes happen with Microsoft Account authentication. The logon experience changes. After clicking he login button, it redirects to the azure logon experience, unlike before, where I was redirected directly to the Microsoft Account logon experience and authentication stops working.
After this I left all Microsoft.Owin.Security.* components on Version 3.1.0 and downgraded Microsoft.Owin.Security.MicrosoftAccount back to 3.0.1. And voila, I got the "old" logon experience back, with the result, that authentication worked again.
The Problem must be in Microsoft.Owin.Security.MicrosoftAccount.
Does anyone know - except from using Version 3.0.1 - how to solve the issue or is this a problem for Microsoft support?

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

Google OAuth on MVC5 ExternalLoginCallback?error=access_denied

I have set up my Google OAuth
And I have added the code into Startup.Auth.cs
app.UseGoogleAuthentication(new GoogleOAuth2AuthenticationOptions()
{
// LRC
ClientId = "xxxxxxxxx",
ClientSecret = "xxxxx"
//CallbackPath = new PathString("/signin-google")
});
But after I chose a google account to log in, it redirected me to the login page again,
I checked the network via Chrome and found that the access was denied.
http://www.liferunningclub.com.au/Account/ExternalLoginCallback?error=access_denied
I cannot figure it out.
Update
Now I did something else:
I added an annotation ([RequireHttps]) on the Account Controller
I enabled the SSL for my project.
I updated the url and re-direct url in Google Console to https
Tried to log in with Google, after I selected my Google account, it returned the same access_denied.
It would be better if the response from Google could give more detailed information.
I had the same problem using the latest ASP.Net MVC template with "Individual Accounts" selected.
The solution was to enable the Google+ API for my project in the Google Developer console.
I found my answer here (scroll down to "Changes to Google OAuth 2.0...").
The same error happened to me for Facebook provider.
Turns out the solution was as simple as updating the nuget package to 3.1.
It turns out that Facebook did a "force upgrade" of their graph API
from version 2.2 to 2.3 on 27th March 2017
For the record I'm using the following:
http://localhost:58364 in iisexpress with NO https
In Facebook I have the following settings configured for a test app:
In addition if you're using a sample template the error parameter returned isn't being consumed which can be misleading. You should add string error to ExternalLoginCallback
[AllowAnonymous]
public async Task<ActionResult> ExternalLoginCallback(string returnUrl, string error)
{
if (error != null)
{
return View("Error");
}
I had this problem as well. After I enabled the Google+ API the problem is not solved yet. Turns out I haven't set the 'Authorized JavaScript origins' in my google API console. So I set the authorized javascript origins, and the problem solved.
I had the same issue. I had Google+ API active and set JavaScript providers. Turns out that my version of Microsoft.Owin 3.1 was too old. I've updated every single nugget which had Microsoft.Owin.(whatever) in it's name and it started working fine (version 4.1)
Hope it helps!
This is most likely because you have not enabled the Google + API in the developer console.
So when your account trys to get the details about the Google Account, it says access_denied.
Simply go to the developer console and enable the Google + API
None of the above solution worked for me. Turns out In my case I was tweaking with Google OAuth Playground and I added https://developers.google.com/oauthplayground this url in Authorized Redirect Uris section of my Google Credentials for Client ID and Secrets.
When I removed it and retried, it worked fine.
PS: I had to reset the OAuth Playground settings that I had modified too.
EDIT
The other issue was, my code threw an Exception when the user was OnAthenticated EventHandler was triggered. Turns out a null reference which was resulting in access_denied status being returned.
GoogleOAuth2AuthenticationOptions googleOptions = new GoogleOAuth2AuthenticationOptions()
{
ClientId = "xxxxx.apps.googleusercontent.com",
ClientSecret = "XXXX",
Provider = new GoogleOAuth2AuthenticationProvider()
{
OnAuthenticated = (context) =>
{
try
{
TokenHelper tokenHelper = new TokenHelper();
// Any exception here will result in 'loginInfo == null' in AccountController.ExternalLoginCallback.
// Be sure to add exception handling here in case of production code.
context.Identity.AddClaim(new Claim(tokenHelper.AccessToken, context.AccessToken)); // From This line and onwards. tokenHelper's properties were null.
// For clarity, we don't check most values for null but RefreshToken is another kind of thing. It's usually
// not set unless we specially request it. Typically, you receive the refresh token only on the initial request,
// store it permanently and reuse it when you need to refresh the access token.
if (context.RefreshToken != null)
{
context.Identity.AddClaim(new Claim(tokenHelper.RefreshToken, context.RefreshToken));
}
// We want to use the e-mail account of the external identity (for which we doing OAuth). For that we save
// the external identity's e-mail address separately as it can be different from the main e-mail address
// of the current user.
context.Identity.AddClaim(new Claim(tokenHelper.Email, context.Email));
context.Identity.AddClaim(new Claim(tokenHelper.Name, context.Name));
context.Identity.AddClaim(new Claim(tokenHelper.IssuedOn, DateTime.Now.ToString()));
context.Identity.AddClaim(new Claim(tokenHelper.ExpiresIn,
((long)context.ExpiresIn.Value.TotalSeconds).ToString()));
return Task.FromResult(0);
}
catch (Exception ex)
{
throw;
}
},
},
AccessType = "offline",
UserInformationEndpoint= "https://www.googleapis.com/oauth2/v2/userinfo"
};
Default Google authentication no longer works, you can add updated Owin.Security.Provider.Google package through NuGet or find it here
Try to use https:// instead of http:

owin external login not redirecting to client application

I have a dedicated web api identity server used by potentially multiple client applications. Now I am trying to get the flow for Facebook authentication work. The process is very similar to the one found in microsoft examples. Here's an image of the flow:
Basically my problem is that once last call is finished successfully, browser is not redirected to client application (localhost:9053).
ExternalLogin in API:
[OverrideAuthentication]
[HostAuthentication(DefaultAuthenticationTypes.ExternalCookie)]
[AllowAnonymous]
[Route("api/externalLogin", Name = "externallogin")]
public async Task<IHttpActionResult> GetExternalLogin(string provider)
{
if (User == null || !User.Identity.IsAuthenticated)
{
return new ChallengeResult(provider, this);
}
ExternalLoginData externalLogin = FromIdentity(provider, User.Identity as ClaimsIdentity);
if (externalLogin == null)
{
return InternalServerError();
}
if (externalLogin.LoginProvider != provider)
{
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
return new ChallengeResult(provider, this);
}
IdentityUser user = await UserManager.FindByIdAsync(externalLogin.UserName);
Authentication.SignOut(DefaultAuthenticationTypes.ExternalCookie);
ClaimsIdentity oAuthIdentity = await _userManager.CreateIdentityAsync(user, OAuthDefaults.AuthenticationType);
ClaimsIdentity cookieIdentity = await _userManager.CreateIdentityAsync(user, CookieAuthenticationDefaults.AuthenticationType);
IDictionary<string, string> data = new Dictionary<string, string>
{
{ "userName", user.UserName }
};
var properties = new AuthenticationProperties(data);
Authentication.SignIn(properties, oAuthIdentity, cookieIdentity);
return Ok();
}
Given a user who is signed into their facebook account and have already accepted permission for this application, it performs the tasks in the image, which ends up coming all the way to the bottom of this method and returning Ok() (200). After this step, I expected owin facebook middleware to intercept and redirect the user to the client user localhost:9053 but that is not happening and it just hangs there.
Any ideas anyone?
When you register your application with Facebook, they ask you to provide a Site URL. (Go to https://developers.facebook.com/, then look under: Settings > Website > Site URL)
The Site URL is also used by Facebook as a server whitelist. In other words, Facebook will only respond to a request with a redirect if the redirect_uri matches the Site URL exactly. The reason they do this is to protect you against an attack called an Open Redirect.
Make both servers share the same hostname by proxying one of them.

Resources