OWIN / OAuth2用户的第三方登录:从客户端应用程序,授权认证的网络API [英] OWIN/OAuth2 3rd party login: Authentication from Client App, Authorization from Web API

查看:742
本文介绍了OWIN / OAuth2用户的第三方登录:从客户端应用程序,授权认证的网络API的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我想创建一个Web API,允许API的客户端(原生移动应用)使用第三方云存储提供商登录。我使用的是微软的以下一般流程:

下面就是我想实现:

我使用外部身份验证默认的ASP.NET Web API的Visual Studio模板,用<一个沿href=\"https://github.com/owin-middleware/OwinOAuthProviders/tree/master/Owin.Security.Providers\">OWin.Security.Providers的NuGet包Dropbox的登录功能,并为谷歌(驱动器)和微软(OneDrive)。现有的内置登录功能

我遇到的问题是,内置功能都似乎做身份验证和授权为一个流程的一部分。例如,如果我成立了中Startup.Auth.cs以下内容:

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

...从我的网页浏览器导航到这个网址:

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

我成功地重定向到Dropbox的登录:

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

...然后我授予访问权限后,我重定向到:

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

...你可以看到令牌的一部分,因此可以提取。问题是,客户端需要一个导航到Dropbox和返回授权code备份到Web API和Web API就送授权code回第三方获得令牌如图所示在上述这将随后被返回给客户端...。我需要的ExternalLogin行动中的AccountController以某种方式获取Dropbox的URL,并返回给客户端(这纯粹是一个JSON响应),但我不明白的方式检索(它只是返回ChallengeResult,而实际Dropbox的网址是什么地方埋葬)。另外,我觉得我需要一种方法来单独要求的基础上授权code来自第三方的标记。

这帖子似乎有点类似于我试图做的事:

<一个href=\"http://stackoverflow.com/questions/21167648/registering-web-api-2-external-logins-from-multiple-api-clients-with-owin-identi\">Registering网页API从OWIN身份多个API客户端2外部登录

...但溶液中似乎需要在客户端是一个MVC应用,这并不一定是我的情况。我想保持这种尽可能简单,在客户端,请按照我上面的图中的流量,也不能推倒重来(重用尽可能的东西在OWIN /执行的OAuth2已经存在)。理想情况下,我不希望客户端必须引用任何OWIN / OAuth的库,因为我真正需要的客户端做的是访问由API(Dropbox的在我的例子)提供的外部链接,让用户输入他们的凭据并给予许可,并发送所产生的授权code备份到API。

这个概念听起来不那么难,但我不知道如何实现它,仍然使用尽可能多的现有的OAuth code尽可能的。请帮助!


解决方案

要清楚,我在你贴可以与任何OAuth2用户端使用的链接中提到的样本,使用任何支持流(隐式,code或自定义)。当您对自己的授权服务器通信,你当然可以使用隐式的流程,如果你想用JS或移动应用:你只需要使用建立一个授权请求 RESPONSE_TYPE =标记和提取的JS一侧的URI片段的访问令牌。

<一个href=\"http://localhost:55985/connect/authorize?client_id=myClient&redirect_uri=http%3a%2f%2flocalhost%3a56854%2f&response_type=token\" rel=\"nofollow\">http://localhost:55985/connect/authorize?client_id=myClient&redirect_uri=http%3a%2f%2flocalhost%3a56854%2f&response_type=token

作为参考,这里的样本:<一href=\"https://github.com/aspnet-security/AspNet.Security.OpenIdConnect.Server/tree/dev/samples/Mvc/Mvc.Server\" rel=\"nofollow\">https://github.com/aspnet-security/AspNet.Security.OpenIdConnect.Server/tree/dev/samples/Mvc/Mvc.Server


在情况下,你preFER一个更简单的方法(这将不涉及定制的OAuth2授权服务器),这里使用的OAuth2承载认证中间件和实现自定义的另一种选择 IAuthenticationTokenProvider 手动验证由Dropbox的发布的不透明令牌。不像提到的样品(其作用类似于Dropbox和MVC的客户端应用程序之间的授权代理服务器),JS应用程序直接与Dropbox的注册。

您将不得不作出对Dropbox的轮廓端点的请求( https://开头的API。 dropbox.com/1/account/info )与接收到的令牌来验证它,并建立适当的 ClaimsIdentity 实例您收到API的每个请求。下面是一个示例(但请不要使用它原来的样子,还没有经过测试):

 公共密封类DropboxAccessTokenProvider:AuthenticationTokenProvider {
    公共覆盖异步任务ReceiveAsync(AuthenticationTokenReceiveContext上下文){
        使用(VAR的客户=新的HttpClient()){
            VAR要求=新的Htt prequestMessage(HttpMethod.Gethttps://api.dropbox.com/1/account/info);
            request.Headers.Authorization =新AuthenticationHeaderValue(旗手,context.Token);            VAR响应=等待client.SendAsync(请求);
            如果(response.Status code!=的HTTPStatus code.OK){
                返回;
            }            变种有效载荷= JObject.Parse(等待response.Content.ReadAsStringAsync());            VAR身份=新ClaimsIdentity(Dropbox的);
            identity.AddClaim(新索赔(ClaimTypes.NameIdentifier,payload.Value&LT;串GT;(UID)));            context.SetTicket(新AuthenticationTicket(身份,新AuthenticationProperties()));
        }
    }
}

您可以很容易地通过 AccessTokenProvider 属性将其插入

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

它有自己的缺点:它需要缓存,以避免水浸Dropbox的端点,是不正确的方式去,如果你想接受不同的供应商(如Dropbox的,微软,谷歌,Facebook)发出的令牌。

更何况,如果提供的非常低安全级别:既然你无法验证访问令牌的用户(即令牌颁发给党),你不能保证该访问令牌颁发给客户端应用程序你充分信任,这使得任何第三方开发者使用自己的Dropbox的令牌与您的API,而无需请求用户的同意。

这是 - 很明显 - 一个重大的安全问题,这就是为什么你的应该 preFER链接的示例中使用的方法。你可以阅读更多关于这个线程混淆副攻击:<一href=\"http://stackoverflow.com/a/17439317/542757\">http://stackoverflow.com/a/17439317/542757.

祝你好运,不,如果你还需要帮助犹豫了。

I am trying to create a Web API that allows the API's clients (native mobile apps) to login using a 3rd party cloud storage provider. I'm using the following general flow from Microsoft:

Here is what I am trying to achieve:

I am using the default ASP.NET Web API Visual Studio template with external authentication, along with the OWin.Security.Providers Nuget package for Dropbox login functionality, and the existing built-in login functionality for Google (Drive) and Microsoft (OneDrive).

The issue I'm having is that the built-in functionality all seems to do the authentication and authorization as part of one flow. For example, if I set up 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 am 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 I grant access, am redirected back to:

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

... as you can see the token is part of that, so could be extracted. The problem is that the client needs to be the one navigating to Dropbox and returning the authorization code back up to the Web API, and the Web API would send the authorization code back to the third party to get the token which would then be returned to the client... as shown in the diagram above. I need the ExternalLogin action in the AccountController to somehow retrieve the Dropbox url and return that to the client (it would just be a json response), but I don't see a way to retrieve that (it just returns a ChallengeResult, and the actual Dropbox url is buried somewhere). Also, I think I need a way to separately request the token from the third party based on the authorization code.

This post seems a little similar to what I am trying to do:

Registering Web API 2 external logins from multiple API clients with OWIN Identity

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

Conceptually this doesn't sound that hard but I have no idea how to implement it and still use as much of the existing OAuth code as possible. Please help!

解决方案

To be clear, the sample I mentioned in the link you posted CAN be used with any OAuth2 client, using any supported flow (implicit, code or custom). When communicating with your own authorization server, you can of course use the implicit flow if you want to use JS or mobile apps: you just have to build an authorization request using response_type=token and extract the access token from the URI fragment on 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's the sample: https://github.com/aspnet-security/AspNet.Security.OpenIdConnect.Server/tree/dev/samples/Mvc/Mvc.Server


In case you'd prefer a simpler approach (that would involve no custom OAuth2 authorization server), here's another option using the OAuth2 bearer authentication middleware and implementing a custom IAuthenticationTokenProvider to manually validate the opaque token issued by Dropbox. Unlike the mentioned sample (that acts like an authorization proxy server between Dropbox and the MVC client app), the JS app is directly registered with Dropbox.

You'll have to make a request against the Dropbox profile endpoint (https://api.dropbox.com/1/account/info) with the received token to validate it and build an adequate ClaimsIdentity instance for each request received by your API. Here's a sample (but please don't use it as-is, it hasn't 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 plug it via the AccessTokenProvider property:

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

It has its own downsides: it requires caching to avoid flooding the Dropbox endpoint and is not the right way to go if you want to accept tokens issued by different providers (e.g Dropbox, Microsoft, Google, Facebook).

Not to mention that if offers a very low security level: since you can't verify the audience of the access token (i.e the party the token was issued to), you can't ensure that the access token was issued to a client application you fully trust, which allows any third party developer to use his own Dropbox tokens with your API without having to request user's consent.

This is - obviously - a major security concern and that's why you SHOULD prefer the approach used in the linked sample. You can read more about confused deputy attacks on this thread: http://stackoverflow.com/a/17439317/542757.

Good luck, and don't hesitate if you still need help.

这篇关于OWIN / OAuth2用户的第三方登录:从客户端应用程序,授权认证的网络API的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆