ASP.NET Web API 2: How do I log in with external authentication services?
Solution 1
I had the same problem today and found the following solution:
At first get all available providers
GET /api/Account/ExternalLogins?returnUrl=%2F&generateState=true
The response message is a list in json format
[{"name":"Facebook",
"url":"/api/Account/ExternalLogin?provider=Facebook&response_type=token&client_id=self&redirect_uri=http%3A%2F%2Flocalhost%3A15359%2F&state=QotufgXRptkAfJvcthIOWBnGZydgVkZWsx8YrQepeDk1",
"state":"QotufgXRptkAfJvcthIOWBnGZydgVkZWsx8YrQepeDk1"}]
Now send a GET request to the url of the provider you want to use. You will be redirected to the login page of the external provider. Fill in your credentials and the you will be redirected back to your site. Now parse the access_token
from the url.
http://localhost:15359/#access_token=[..]&token_type=bearer&expires_in=[..]&state=QotufgXRptkAfJvcthIOWBnGZydgVkZWsx8YrQepeDk1
If the user already has a local account, the .AspNet.Cookies
cookie is set and you are done. If not, only the .AspNet.ExternalCookie
cookie is set and you have to register a local account.
There is an api to find out if the user is registered:
GET /api/Account/UserInfo
The response is
{"userName":"xxx","hasRegistered":false,"loginProvider":"Facebook"}
To create a local account for the user, call
POST /api/Account/RegisterExternal
Authorization: Bearer VPcd1RQ4X... (access_token from url)
Content-Type: application/json
{"UserName":"myusername"}
Now send the same request with the provider url as before
GET /api/Account/ExternalLogin?provider=Facebook&response_type=token&client_id=self&redirect_uri=http%3A%2F%2Flocalhost%3A15359%2F&state=QotufgXRptkAfJvcthIOWBnGZydgVkZWsx8YrQepeDk1
But this time the user already has an account and gets authenticated. You can verify this by calling /api/Account/UserInfo
again.
Now extract the access_token
from the url. You have to add the Authorization: Bearer [access_token]
header to every request you make.
Solution 2
I found another post showing pretty details how this external authentication works. The client is WPF and server uses ASP.NET Identity.
Comments
-
acor3 almost 2 years
According to this post http://www.asp.net/web-api/overview/security/external-authentication-services... I'm able to log in with a local authentication service (with the new ASP.NET identity framework)
but I can't find a walkthrough to properly call (from a mobile app or Postman) the default web API generated in the Visual Studio 2013 SPA template.
Can anyone help me?
-
acor3 over 10 yearsmy main problem is that my client in not a web application but a native mobile application (andoird app or IOS app)
-
Joe the Coder over 10 years@acor3 - it's the same process no matter what platform you're using. In the case of native mobile apps, you would open up a web view within your app (UIWebView on iPhone, WebView on Android), send the user to the url of the provider they chose, allow them to log in, and have the redirect url go to a page in your api that contains a magic token. Your app would then look in the web view for that token, and once it's found, it would grab the bearer/access tokens from it, then you can use it in your native HTTP GET/POST calls.
-
Bartosz over 10 years@berhir - thanks for your answer, I found it really useful with getting my head around Web API 2 security. My scenario is slightly different though. I call API from another MVC application. So where you say 'you will be redirected back to your site. Now parse the access_token from the url' how would you approach it in my case?
-
acor3 over 10 yearshi all.. check this question. stackoverflow.com/questions/21092723/…
-
Alexey Strakh about 10 yearsThank you for the detailed guide! What if I already have a token and would like to authenticate user against web api using it. I got this token by internal iOS SDK api, which allows you to get the token without opening a webview.
-
berhir about 10 yearsI have no experience with iOS so I don't know what API you have there. But if you already got a token from your web api application, you just need to add it to the header of every http request as mentioned in my answer. If this doesn't help you, please give some more details on what you want to do.
-
razeth01 almost 10 years@berhir I successfully followed your example and its working fine till the point after I register the user external. The when I want to log in again with a with the external user(local saved now) the access token does not get attached to the url. The browser stays at the same ExternalLogin page. Could the problem be that I'm using the applicationUser override and not the IdentityUser.
-
NVM over 9 yearsI believe 'Now send a GET request to the url of the provider you want to use.' should actually be 'Redirect to to the url of the provider you want to use." and also "Now send the same request with the provider url as before" should be "Now again Now send the same request with the provider url as before."
-
Richa about 9 yearsI am working on a project with titanium SDK and using above steps for facebook login. All works fine in iOS but on android webview it's getting fail at step “If user is not already registered, only the .AspNet.ExternalCookie cookie is set and you have to register a local account." , I am not getting '.AspNet.ExternalCookie' and therefore can not succeed further. If user is already registered all works well even on android. Did anyone faced this issue ? I've been stuck in this issue since a long time. Any help would be really appreciated.
-
berhir about 9 years@Richa Unfortunately I'm not familiar with the Android WebView, but maybe the answers to this question can help: stackoverflow.com/questions/2566485/…
-
Alberto Montellano about 9 yearsHow do you extract the access token from url?
-
BrunoRamalho over 8 yearscould you explain how to get the Facebook access token after the login?
-
berhir over 8 years@BrunoRamalho To get the access token, you need to parse it from the url. If you use C# for example, you can use the
Uri.Fragment
property. -
berhir over 8 years@BrunoRamalho Sorry, I made a mistake. Here is an example: In C# you can use the
Uri
class andHttpUtility.ParseQueryString
method.Uri uri= new Uri("http://localhost:15359/#access_token=[..]&token_type=bearer&expires_in=[..]&state=QotufgXRptkAfJvcthIOWBnGZydgVkZWsx8YrQepeDk1"); string access_token = HttpUtility.ParseQueryString(uri.Query).Get("access_token");
-
BrunoRamalho over 8 yearsyes, is more or less this, however this is helping me solving the problem stackoverflow.com/questions/32508261/…
-
Warren over 8 yearsDoes anyone have any idea how to set the returnUrl to an angular view, cuz I'm really looking for a way to make this work? '/api/account/externalLogins?returnUrl='+ encodeURIComponent('/user#/register-external') +'
-
berhir over 8 years@WarrenDodsworth It's not possible to add a hash property to the return url. But you can add the return url to the state parameter and then parse it from there when the call comes back from the authorization server. Here is a working sample: github.com/manfredsteyer/angular-oauth-oidc/blob/master/app/… It's a hack but I don't know about a better solution.
-
Nithish Inpursuit Ofhappiness over 8 years@berhir I'm making a GET request to the url of the provider I want to use - Microsoft Account. I am redirected to the login page and I'm able to get the access token from the URL. However when I try to use this against the api/Account/UserIdentity, I'm still unauthorized. I tried making this action method as [AlowAnonymous] and the Email attribute of the returned user was null and so was the AuthenticationProvider attribute of the JSON object. More weirdly, the IsRegistered attribute is already returning true. Any pointers??
-
Hithesh over 8 years@berhir When i will call the GET /api/Account/UserInfo it showing {"Message":"Authorization has been denied for this request."} ,could have any idea,please let me help me
-
Insomniac over 8 years@berhir Hi , i am struck in getting access token , could you please help me . here is the link stackoverflow.com/questions/34036961/…
-
Bimal Das almost 8 yearsok. I am success of getting access token using the url. The problem is, at the RegisterExternal method, on this line: var info = await Authentication.GetExternalLoginInfoAsync(); It returns null, and thus returning a BadRequest().In debugging , I am getting my info. but why it is not giving me null info when everything is current ?
-
Randal Cunanan over 7 yearsI know this post is old but can you update the answer on how to do this in ASP.NET Core and ASP.NET Identity 3, I can no longer find some enpoints like Account/UserInfo and Account/ExternalLogins on the default template.
-
Himalaya Garg over 7 yearsYou are awesome man... I was searching for this since long.. Microsoft people has made it too confusing by mixing-matching SPA/ MVC/ API templates.
-
Braydie over 7 yearsIf I call
GetUserInfo
again after registering I get a null reference exception because my issuer is LOCAL AUTHORITY after logging in and registering via Facebook (in my instance). -
Himalaya Garg about 7 yearsRead the hash part of URL using javascript window.location.hash and get the access_token param from it by splitting params. Hash is not available on server side. You can save the access_token in hidden field to use it later for further requests.
-
Abhishek Jaiswal almost 6 years@berhir Sir, while creating a local account for the user via POST method to RegisterExternal, I need emailid to pass as body. Now, userinfo return Username, not email-id. Any idea, How can I get email-id at this moment? All previous codes are just as you told, and are working fine. Pleasee help.
-
Karthik over 5 yearsHow to do this for asp.net core ?
-
Su Ming Yuan over 2 yearsI could able to follow the step until RegisterExternal step.
POST /api/Account/RegisterExternal
. Upon submitting that method, getting NULL response fromawait Authentication.GetExternalLoginInfoAsync();
Could we try these steps using Postman or need to use html/javascript to following this steps? I've posted the question too. https://stackoverflow.com/questions/70144285/c-sharp-web-api-2-authentication-getexternallogininfoasync-return-null