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.
Related
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
I have web api account controller where I confirm an user email
[System.Web.Http.AllowAnonymous]
[System.Web.Http.HttpGet]
[System.Web.Http.Route("ConfirmEmail", Name = "ConfirmEmailRoute")]
public async Task<IHttpActionResult> ConfirmEmail(string userId = "", string code = "")
{
if (string.IsNullOrWhiteSpace(userId) || string.IsNullOrWhiteSpace(code))
{
ModelState.AddModelError("", "User Id and Code are required");
return BadRequest(ModelState);
}
IdentityResult result = await this.AppUserManager.ConfirmEmailAsync(userId, code);
if (result.Succeeded)
{
return Ok();
}
else
{
return GetErrorResult(result);
}
}
}
This method is called when user clicks the confirmation link from email. After that I want to redirect it to "ConfirmidSuccessfully" page
In MVC we could do it like:
return View("ConfirmidSuccessfully");
There are other ways to redirect like:
var response = Request.CreateResponse(HttpStatusCode.Moved);
response.Headers.Location = new Uri("/ConfirmidSuccessfully");
return response;
Actually there are 2 questions:
Is it good to redirect from web api method according to WEB API, or there's better approach
What is the most appropriate way to do it
It is not a good practice to redirect to a view or a web page when you are using REST hence ASP.Net Web API.
Just return a successful status code to the client. Let the client do the redirection itself.
For example, if you are using an AngularJS App to connect with your Web API then after a call to email confirmation finished with success, redirect to the web page/view by using the web page URL you store in the client side.
[EDIT]
Based on your comment
I use angularjs, but the call to email confirmation comes from user's email, not from client.
Then you must generate the confirmation email on server side by making the host's url to be your Angular JS app host. e.g. myangularjsapp.com/emilconfirmation/token. Send an email with this URL to your user.
With URL like that the user is redirect from his email to your AngularJS App. When he hit the app you initialize a call to the ASP.Net Web API by retrieving the token from your AngularJS App url.
Since you are returning IHttpActionResult, you can return redirect in the action and this is the preferable way:
return this.Redirect("/path/to/redirect");
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.
I'm implementing the following scenario: ASP.NET MVC 5 application access OData WebAPI with Azure Active Directory authentication (like in this article: http://msdn.microsoft.com/en-us/magazine/dn463788.aspx ).
However, when I call AuthenticationContext.AcquireToken I get System.Threading.ThreadStateException saying: ActiveX control '8856f961-340a-11d0-a96b-00c04fd705a2' cannot be instantiated because the current thread is not in a single-threaded
apartment.
EDITED:
Steps to reproduce:
Create New MVC project with Organizational Authentication. Use your Windows Azure Domain and MSDN Account
Add Actice Directory Authentication Library via NuGet
Add action with the following code:
public async Task<ActionResult> Index(){
AuthenticationContext ac = new AuthenticationContext("https://login.windows.net/domain.onmicrosoft.com");
AuthenticationResult ar = ac.AcquireToken("https://domain.onmicrosoft.com/WindowsAzureADWebAPITest",
"a4836f83-0f69-48ed-aa2b-88d0aed69652",
new Uri("https://domain.onmicrosoft.com/myWebAPItestclient")
);
// Call Web API
string authHeader = ar.CreateAuthorizationHeader();
HttpClient client = new HttpClient();
HttpRequestMessage request = new HttpRequestMessage(HttpMethod.Get, "https://server.com:44353/api/Values");
request.Headers.TryAddWithoutValidation("Authorization", authHeader);
HttpResponseMessage response = await client.SendAsync(request);
string responseString = await response.Content.ReadAsStringAsync();
return View();
}
Run the code and reproduce the issue (AcqureToken method call).
Please suggest a fix.
Thank you!
That particular overload of AcquireToken() is only usable in a native client app because the way it handles user authentication is by opening a browser window to login.windows.net. This requires the app to host a browser ActiveX control and that's why it needs an STA thread.
Now, in your example the code runs inside IIS on the server machine where hosting ActiveX controls is just not possible.
What you really need is delegation which is described here: http://www.cloudidentity.com/blog/2013/10/29/using-adals-acquiretokenby-authorizationcode-to-call-a-web-api-from-a-web-app/
Same author, just the different article.
I'm trying to implement OAuth with twitter so my users can log into my site using their Twitter IDs. For this I am using the TweetSharp library. Following the examples they have I wrote the following code which seems to work.
public ActionResult Login(string oauth_token, string oauth_verifier)
{
var service = new TwitterService(consumerKey, consumerSecret);
if (oauth_token == null)
{
var requestToken = service.GetRequestToken(Request.Url.ToString());
var uri = service.GetAuthorizationUri(requestToken);
return new RedirectResult(uri.ToString(), false);
}
else
{
var requestToken = new OAuthRequestToken { Token = oauth_token };
OAuthAccessToken accessToken = service.GetAccessToken(requestToken, oauth_verifier);
service.AuthenticateWith(accessToken.Token, accessToken.TokenSecret);
TwitterUser user = service.VerifyCredentials(new VerifyCredentialsOptions());
TempData["response"] = string.Format("Your username is {0}", user.ScreenName);
return RedirectToAction("Success");
}
}
public ActionResult Success()
{
ViewBag.Response = TempData["response"];
return View();
}
However, there is a problem. Every time the user logs into the system twitter asks them to authorize the application even though they have done it before. Is there a way to prevent this behavior? I have also implemented OAuth with Facebook and Google and I don't need to authorize the application every time I want to log into the system.
Unfortunately not as far as I'm aware. Twitter uses OAuth 1.1 rather than OAuth 2.0 like Facebook and Google, so there is a manual step in the middle in which users are asked to authorise the application even though they have done already. I'm having exactly the same issue and it appears to be something we have to live with.
I was having the same problem with a slightly older app I was trying to resurrect. I noticed that the problem went away when I selected "Sign in with Twitter" in my app settings at http://dev.twitter.com/apps.