49

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?

Boann
  • 48,794
  • 16
  • 117
  • 146
acor3
  • 633
  • 1
  • 6
  • 9

3 Answers3

117

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.

berhir
  • 1,401
  • 1
  • 10
  • 7
  • 4
    my main problem is that my client in not a web application but a native mobile application (andoird app or IOS app) – acor3 Feb 10 '14 at 15:56
  • 6
    @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. – Joe the Coder Feb 14 '14 at 19:32
  • 1
    @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? – Bartosz Feb 19 '14 at 10:22
  • hi all.. check this question. http://stackoverflow.com/questions/21092723/webapi-asp-net-identity-facebook-login – acor3 Feb 20 '14 at 18:22
  • 3
    Thank 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. – Mando Apr 15 '14 at 23:54
  • I 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. – berhir Apr 16 '14 at 16:46
  • @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. – razeth01 Jun 25 '14 at 13:12
  • 3
    I 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." – NVM Dec 19 '14 at 05:12
  • I 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. – Richa Apr 20 '15 at 10:09
  • @Richa Unfortunately I'm not familiar with the Android WebView, but maybe the answers to this question can help: http://stackoverflow.com/questions/2566485/webview-and-cookies-on-android – berhir May 06 '15 at 11:26
  • How do you extract the access token from url? – Alberto Montellano May 11 '15 at 00:43
  • could you explain how to get the Facebook access token after the login? – BrunoRamalho Sep 10 '15 at 19:08
  • @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 Sep 14 '15 at 06:49
  • 2
    @BrunoRamalho Sorry, I made a mistake. Here is an example: In C# you can use the `Uri` class and `HttpUtility.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");` – berhir Sep 14 '15 at 07:04
  • yes, is more or less this, however this is helping me solving the problem http://stackoverflow.com/questions/32508261/asp-net-mvc-api-get-facebook-token/32512752#32512752 – BrunoRamalho Sep 14 '15 at 21:13
  • Does 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') +' – Warren Nov 11 '15 at 08:32
  • @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: https://github.com/manfredsteyer/angular-oauth-oidc/blob/master/app/components/oauth/oauth-service.js It's a hack but I don't know about a better solution. – berhir Nov 11 '15 at 12:53
  • @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?? – Nithish Inpursuit Ofhappiness Dec 09 '15 at 03:50
  • @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 – Hithesh Jan 06 '16 at 04:48
  • @berhir Hi , i am struck in getting access token , could you please help me . here is the link http://stackoverflow.com/questions/34036961/open-authentication-cross-domain-redirection-issue – Insomniac Jan 13 '16 at 05:16
  • ok. 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 ? – Bimal Das Jul 03 '16 at 11:42
  • I 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. – Randal Cunanan Sep 13 '16 at 07:34
  • You are awesome man... I was searching for this since long.. Microsoft people has made it too confusing by mixing-matching SPA/ MVC/ API templates. – Himalaya Garg Dec 18 '16 at 05:45
  • If 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). – Braydie Jan 30 '17 at 14:09
  • Read 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. – Himalaya Garg Apr 16 '17 at 04:35
  • @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. – Abhishek Jaiswal Jul 04 '18 at 19:02
  • How to do this for asp.net core ? – Karthik Dec 15 '18 at 20:46
  • I could able to follow the step until RegisterExternal step. `POST /api/Account/RegisterExternal`. Upon submitting that method, getting NULL response from `await 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](https://stackoverflow.com/questions/70144285/c-sharp-web-api-2-authentication-getexternallogininfoasync-return-null) – Su Ming Yuan Nov 30 '21 at 15:00
5

I found another post showing pretty details how this external authentication works. The client is WPF and server uses ASP.NET Identity.

T N
  • 396
  • 5
  • 13
0

For those trying to use Web Api 2 External Login with Facebook in Android App this post is explaining only the first part of what we have to do. Here is a very explanatory link of the whole picture:

[Authenticated access to WebAPI via Facebook token from Android App

Kalin Krastev
  • 552
  • 6
  • 19