我正在尝试创建一个Web API,允许API的客户端(本机移动应用程序)使用第三方云存储提供程序进行登录。我使用了以下来自Microsoft的一般流程:
这是我想要实现的目标:
我使用带有外部身份验证的默认ASP.NET Web API Visual Studio模板,以及用于Dropbox登录功能的OWin.Security.Providers Nuget包,以及用于Google(云端硬盘)和Microsoft的现有内置登录功能(OneDrive)。
我遇到的问题是,内置功能似乎都是作为一个流程的一部分进行身份验证和授权。例如,如果我在Startup.Auth.cs中设置以下内容:
DropboxAuthenticationOptions dropboxAuthOptions = new DropboxAuthenticationOptions
{
AppKey = _dropboxAppKey,
AppSecret = _dropboxAppSecret
};
app.UseDropboxAuthentication(dropboxAuthOptions);
...并从我的网络浏览器导航到此网址:
http://<api_base_url>/api/Account/ExternalLogin?provider=Dropbox&response_type=token&client_id=self&redirect_uri=<api_base_url>
我已成功重定向到Dropbox登录:
https://www.dropbox.com/1/oauth2/authorize?response_type=code&client_id=<id>&redirect_uri=<redirect_uri>
...然后在我授予访问权限后,重定向回到:
http://<api_base_url>/Help#access_token=<access_token>&token_type=bearer&expires_in=1209600
...因为你可以看到令牌是其中的一部分,所以可以提取。问题是客户端需要导航到Dropbox并将授权代码返回到Web API,Web API会将授权代码发送回第三方以获取令牌,然后将其返回给客户......如上图所示。我需要在AccountController中使用ExternalLogin操作以某种方式检索Dropbox url并将其返回给客户端(它只是一个json响应),但我没有看到检索它的方法(它只返回ChallengeResult,并且实际的Dropbox网址被埋在某处。此外,我认为我需要一种方法来根据授权代码单独请求来自第三方的令牌。
这篇文章似乎与我想要做的有点类似:
Registering Web API 2 external logins from multiple API clients with OWIN Identity
...但是解决方案似乎要求客户端成为MVC应用程序,对我来说不一定是这种情况。我希望在客户端保持尽可能简单,遵循上图中的流程,但也不要重新发明轮子(尽可能多地重用OWIN / OAuth2实现中已存在的内容)。理想情况下,我不希望客户端必须引用任何OWIN / OAuth库,因为我真正需要客户端做的就是访问API提供的外部URL(在我的示例中为Dropbox),具有用户输入他们的凭据并给予许可,并将生成的授权代码发送回api。
从概念上讲,这听起来并不难,但我不知道如何实现它,仍然尽可能多地使用现有的OAuth代码。请帮忙!
答案 0 :(得分:4)
要明确的是,我发布的链接中提到的示例可以与任何OAuth2客户端一起使用,使用任何支持的流(隐式,代码或自定义)。当与您自己的授权服务器通信时,如果您想使用JS或移动应用程序,您当然可以使用隐式流程:您只需使用response_type=token
构建授权请求并从URI片段中提取访问令牌JS方面。
如果您更喜欢更简单的方法(不涉及自定义OAuth2授权服务器),这里是使用OAuth2承载身份验证中间件并实现自定义IAuthenticationTokenProvider
以手动验证Dropbox发出的不透明令牌的另一个选项。与上述示例(类似于Dropbox和MVC客户端应用程序之间的授权代理服务器)不同,JS应用程序直接在Dropbox中注册。
您必须使用收到的令牌对Dropbox配置文件端点(https://api.dropbox.com/1/account/info)发出请求以验证它,并为您的API收到的每个请求构建一个足够的ClaimsIdentity
实例。这是一个样本(但请不要按原样使用,它尚未经过测试):
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()));
}
}
}
您可以通过AccessTokenProvider
属性轻松插入它:
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions {
AccessTokenProvider = new DropboxAccessTokenProvider()
});
它有其自身的缺点:它需要缓存以避免泛滥Dropbox端点,如果您想接受由不同提供商(例如Dropbox,Microsoft,Google,Facebook)发布的令牌,则不是正确的方法。
更不用说如果提供非常低的安全级别:因为您无法验证访问令牌的受众(即令牌被发送到的一方),您无法确保访问令牌是发给您完全信任的客户端应用程序,这允许任何第三方开发人员使用他自己的Dropbox令牌与您的API,而无需征得用户的同意。
这显然是一个主要的安全问题,这就是为什么你应该更喜欢链接样本中使用的方法。你可以在这个帖子上阅读更多关于混乱的副攻击:https://stackoverflow.com/a/17439317/542757。
祝你好运,如果你还需要帮助,请不要犹豫。