OWIN / OAuth2 Third-party login: authentication from the client application, authorization from the web API

I am trying to create a web API that allows API clients (native mobile applications) to log in using a third-party cloud storage provider. I use the following generic thread from Microsoft:

Here is what I am trying to achieve:

I use the standard ASP.NET Web API Visual Studio template with external authentication, as well as the OWin.Security.Providers Nuget package for Dropbox login features and existing built-in login features for Google (Drive) and Microsoft (OneDrive).

The problem I am facing is that the built-in functionality seems to perform authentication and authorization as part of a single thread. For example, if I set the following in Startup.Auth.cs:

DropboxAuthenticationOptions dropboxAuthOptions = new DropboxAuthenticationOptions { AppKey = _dropboxAppKey, AppSecret = _dropboxAppSecret }; app.UseDropboxAuthentication(dropboxAuthOptions); 

... and navigate to this URL from my web browser:

 http://<api_base_url>/api/Account/ExternalLogin?provider=Dropbox&response_type=token&client_id=self&redirect_uri=<api_base_url> 

I have successfully redirected to Dropbox to login:

 https://www.dropbox.com/1/oauth2/authorize?response_type=code&client_id=<id>&redirect_uri=<redirect_uri> 

... and then, after granting access, it is redirected back to:

 http://<api_base_url>/Help#access_token=<access_token>&token_type=bearer&expires_in=1209600 

... since you can see that the token is part of this, so it can be retrieved. The problem is that the client must be one of those who moves to Dropbox and returns the authorization code back to the web API, and the web API sends the authorization code back to a third party in order to receive the token, which the client will then return ... as shown in the diagram above. I need the ExternalLogin action in the AccountController to somehow get the Dropbox URL and return it to the client (this will be just a json response), but I don't see a way to get it (it just returns a ChallengeResult, and the actual Dropbox code is buried somewhere ) In addition, I think I need a way to separately request a token from a third party based on the authorization code.

This post seems a bit like what I'm trying to do:

Web API 2 front end registration from multiple API clients with OWIN identifier

... but the solution seems to require the client to be an MVC application, which is not necessary for me. I want it to be as simple as possible on the client side, follow the flow from my diagram above, but also do not reinvent the wheel (use as much as possible what already exists in the OWIN / OAuth2 implementation). Ideally, I do not want the client to reference any of the OWIN / OAuth libraries, since all I really need is access to the external url provided by the API (Dropbox in my example), the user enters their credentials and give permission, and send the received authorization code back to the api.

Conceptually this doesnโ€™t sound so complicated, but I donโ€™t know how to implement it and still use as much of the existing OAuth code as possible. Please, help!

+12
authentication asp.net-web-api oauth owin
Feb 12 '15 at 20:55
source share
1 answer

To be clear, the sample that I mentioned in the link you provided can be used with any OAuth2 client using any supported stream (implicit, code, or custom). When communicating with your own authorization server, you can of course use an implicit stream if you want to use JS or mobile applications: you just need to create an authorization request using response_type=token and extract the access token from the URI fragment in the JS side.

http: // localhost: 55985 / connect / authorize? client_id = myClient & redirect_uri = http% 3a% 2f% 2flocalhost% 3a56854% 2f & response_type = token

For reference, here is a sample: https://github.com/aspnet-security/AspNet.Security.OpenIdConnect.Server/tree/dev/samples/Mvc/Mvc.Server




In case you prefer a simpler approach (in which there will be no custom OAuth2 authorization server), here is another option using middleware for bearer authentication OAuth2 and implementing a custom IAuthenticationTokenProvider for manually checking the opaque token issued by Dropbox. Unlike the example mentioned (which acts as an authorization proxy between Dropbox and the MVC client application), the JS application is directly registered with Dropbox.

You will need to make a request against the Dropbox profile endpoint ( https://api.dropbox.com/1/account/info ) with the accepted token in order to verify it and build an adequate instance of ClaimsIdentity for each request received by your API. Here's a sample (but please do not use it as is, it has not been tested):

 public sealed class DropboxAccessTokenProvider : AuthenticationTokenProvider { public override async Task ReceiveAsync(AuthenticationTokenReceiveContext context) { using (var client = new HttpClient()) { var request = new HttpRequestMessage(HttpMethod.Get, "https://api.dropbox.com/1/account/info"); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", context.Token); var response = await client.SendAsync(request); if (response.StatusCode != HttpStatusCode.OK) { return; } var payload = JObject.Parse(await response.Content.ReadAsStringAsync()); var identity = new ClaimsIdentity("Dropbox"); identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, payload.Value<string>("uid"))); context.SetTicket(new AuthenticationTicket(identity, new AuthenticationProperties())); } } } 

You can easily connect it through the AccessTokenProvider property:

 app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions { AccessTokenProvider = new DropboxAccessTokenProvider() }); 

This has its own drawbacks: it requires caching to avoid flooding the Dropbox endpoint and is not the right way if you want to accept tokens issued by different providers (for example, Dropbox, Microsoft, Google, Facebook).

Not to mention that if you offer a very low level of security : since you cannot check the audience of the access token (that is, the participant to whom the token was issued), you cannot ensure that the access token was issued to the client application to which you fully trust, which allows any third-party developer to use their own Dropbox tokens with your API without asking for user consent.

This is obviously a serious security issue, and why you SHOULD prefer the approach used in the linked sample. You can learn more about the confusing substitutions in this thread: https://stackoverflow.com/a/312960/

Good luck and don't be shy if you still need help.

+4
Feb 12 '15 at 22:08
source share



All Articles