OWIN/OAuth2第三方登录:来自客户端应用程序的身份验证,来自Web API的授权

may*_*lle 12 authentication oauth oauth-2.0 asp.net-web-api owin

我正在尝试创建一个Web API,允许API的客户端(本机移动应用程序)使用第三方云存储提供程序登录.我正在使用Microsoft的以下一般流程:

这是我想要实现的目标:

我使用带有外部身份验证的默认ASP.NET Web API Visual Studio模板,以及用于Dropbox登录功能的OWin.Security.Providers Nuget包,以及用于Google(Drive)和Microsoft(OneDrive)的现有内置登录功能.

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

DropboxAuthenticationOptions dropboxAuthOptions = new DropboxAuthenticationOptions
                                                    {
                                                        AppKey = _dropboxAppKey,
                                                        AppSecret = _dropboxAppSecret
                                                    };
app.UseDropboxAuthentication(dropboxAuthOptions);
Run Code Online (Sandbox Code Playgroud)

...并从我的网络浏览器导航到此网址:

http://<api_base_url>/api/Account/ExternalLogin?provider=Dropbox&response_type=token&client_id=self&redirect_uri=<api_base_url>
Run Code Online (Sandbox Code Playgroud)

我已成功重定向到Dropbox登录:

https://www.dropbox.com/1/oauth2/authorize?response_type=code&client_id=<id>&redirect_uri=<redirect_uri>
Run Code Online (Sandbox Code Playgroud)

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

http://<api_base_url>/Help#access_token=<access_token>&token_type=bearer&expires_in=1209600
Run Code Online (Sandbox Code Playgroud)

...因为你可以看到令牌是其中的一部分,所以可以提取.问题是客户端需要导航到Dropbox并将授权代码返回到Web API,Web API会将授权代码发送回第三方以获取令牌,然后将其返回给客户......如上图所示.我需要AccountController中的ExternalLogin操作以某种方式检索Dropbox url并将其返回给客户端(它只是一个json响应),但我没有看到一种方法来检索它(它只返回一个ChallengeResult,并且实际的Dropbox网址埋在某处).此外,我认为我需要一种方法来根据授权代码单独请求来自第三方的令牌.

这篇文章似乎与我想要做的有点类似:

使用OWIN标识从多个API客户端注册Web API 2外部登录

...但是那里的解决方案似乎要求客户端成为MVC应用程序,对我来说不一定是这样.我希望在客户端保持尽可能简单,遵循上图中的流程,但也不要重新发明轮子(尽可能多地重用OWIN/OAuth2实现中已存在的内容).理想情况下,我不希望客户端必须引用任何OWIN/OAuth库,因为我真正需要客户端做的就是访问API提供的外部URL(在我的示例中为Dropbox),让用户输入他们的凭据并授予权限,并将生成的授权代码发送回api.

从概念上讲,这听起来并不难,但我不知道如何实现它,仍然尽可能多地使用现有的OAuth代码.请帮忙!

Kév*_*let 5

需要明确的是,我在您发布的链接中提到的示例可以与任何 OAuth2 客户端一起使用,使用任何支持的流程(隐式、代码或自定义)。当与您自己的授权服务器通信时,如果您想使用 JS 或移动应用程序,您当然可以使用隐式流程:您只需使用response_type=tokenJS 端的 URI 片段构建授权请求并提取访问令牌即可。

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

作为参考,这里是示例: https: //github.com/aspnet-security/AspNet.Security.OpenIdConnect.Server/tree/dev/samples/Mvc/Mvc.Server


如果您更喜欢更简单的方法(不涉及自定义 OAuth2 授权服务器),这里有另一个选项,使用 OAuth2 不记名身份验证中间件并实现自定义来手动IAuthenticationTokenProvider验证 Dropbox 颁发的不透明令牌。与提到的示例(充当 Dropbox 和 MVC 客户端应用程序之间的授权代理服务器)不同,JS 应用程序直接向 Dropbox 注册。

您必须使用收到的令牌向 Dropbox 配置文件端点 ( https://api.dropbox.com/1/account/info ) 发出请求,以对其进行验证并ClaimsIdentity为 API 收到的每个请求构建适当的实例。这是一个示例(但请不要按原样使用它,它尚未经过测试):

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()));
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

您可以通过以下属性轻松插入它AccessTokenProvider:

app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions {
    AccessTokenProvider = new DropboxAccessTokenProvider()
});
Run Code Online (Sandbox Code Playgroud)

它有其自身的缺点:它需要缓存以避免淹没 Dropbox 端点,并且如果您想接受不同提供商(例如 Dropbox、Microsoft、Google、Facebook)颁发的令牌,则不是正确的方法。

更不用说 if 提供了非常低的安全级别:由于您无法验证访问令牌的受众(即令牌颁发给的一方),因此您无法确保访问令牌已颁发给客户端应用程序您完全信任,这允许任何第三方开发人员将他自己的 Dropbox 令牌与您的 API 一起使用,而无需请求用户同意。

显然,这是一个主要的安全问题,这就是为什么您应该更喜欢链接示例中使用的方法。您可以在此线程上阅读有关混淆代理攻击的更多信息:/sf/answers/1220752221/。

祝您好运,如果您仍然需要帮助,请不要犹豫。