如何通过DotNetOpenAuth使用OAuth2访问令牌授权访问ServiceStack资源?

Dyl*_*tie 12 dotnetopenauth oauth-2.0 servicestack

我使用DotNetOpenAuth创建了一个OAuth2授权服务器,它工作正常 - 我正在使用资源所有者密码流,并成功地为访问令牌交换用户凭据.

我现在想要使用该访问令牌从ServiceStack API中的安全端点检索数据,我无法弄清楚如何执行此操作.我已经检查了ServiceStack附带的Facebook,Google等提供商,但目前尚不清楚我是否应该遵循相同的模式.

我想要实现的目标(我想!)是

  1. OAuth 客户端(我的应用程序)要求资源所有者('Catherine Smith')提供凭据
  2. 客户端授权服务器提交请求,接收访问令牌
  3. 客户端资源服务器请求安全资源() GET /users/csmith/photos
    • 访问令牌被包括在HTTP头,例如 Authorization: Bearer 1234abcd...
  4. 资源服务器解密访问令牌验证的身份资源拥有者
  5. 资源服务器会检查资源所有者可以访问请求的资源
  6. 资源服务器返回的资源客户

步骤1和2正在运行,但我无法弄清楚如何将DotNetOpenAuth资源服务器代码与ServiceStack授权框架集成.

我是如何实现这一目标的?我在如何使用ServiceStack构建安全api作为OAuth2.0的资源服务器时发现了类似的StackOverflow帖子但它不是一个完整的解决方案,似乎没有使用ServiceStack授权提供程序模型.

编辑:更多细节.这里有两个不同的网络应用程序.一个是身份验证/授权服务器 - 它不承载任何客户数据(即没有数据API),但公开了接受用户名/密码并返回OAuth2访问令牌和刷新令牌的/ oauth/token方法,还有提供令牌刷新功能.这是基于ASP.NET MVC构建的,因为它几乎与DotNetOpenAuth附带的AuthorizationServer示例相同.这可能会在以后更换,但现在它是ASP.NET MVC.

对于实际的数据API,我使用的是ServiceStack,因为我发现它比WebAPI或MVC更好地暴露了ReSTful数据服务.

所以在下面的例子中:

序列图

客户端是用户的本地机器上运行的桌面应用程序,该验证服务器是ASP.NET MVC + DotNetOpenAuth和资源服务器是ServiceStack

需要的特定DotNetOpenAuth代码片段是:

// scopes is the specific OAuth2 scope associated with the current API call.
var scopes = new string[] { "some_scope", "some_other_scope" }

var analyzer = new StandardAccessTokenAnalyzer(authServerPublicKey, resourceServerPrivateKey);
var resourceServer = new DotNetOpenAuth.OAuth2.ResourceServer(analyzer);
var wrappedRequest = System.Web.HttpRequestWrapper(HttpContext.Current.Request);
var principal = resourceServer.GetPrincipal(wrappedRequest, scopes);

if (principal != null) {
    // We've verified that the OAuth2 access token grants this principal
    // access to the requested scope.
}
Run Code Online (Sandbox Code Playgroud)

因此,假设我处于正确的轨道上,我需要做的是在ServiceStack请求管道中的某处运行该代码,以验证API请求中的Authorization标头是否代表授予访问请求范围的有效主体.

我开始认为实现这个的最合理的地方是我用来装饰我的ServiceStack服务实现的自定义属性:

using ServiceStack.ServiceInterface;
using SpotAuth.Common.ServiceModel;

namespace SpotAuth.ResourceServer.Services {
    [RequireScope("hello")]
    public class HelloService : Service {
        public object Any(Hello request) {
            return new HelloResponse { Result = "Hello, " + request.Name };
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

此方法还允许指定每种服务方法所需的范围.但是,这似乎与OAuth2背后的"可插拔"原则以及ServiceStack的AuthProvider模型中内置的可扩展性挂钩相反.

换句话说 - 我担心因为找不到锤子而砸钉鞋

Dyl*_*tie 8

OK,一个后很多通过与调试各种库步进的,我觉得你不喜欢这样写道:https://github.com/dylanbeattie/OAuthStack

有两个关键的集成点.首先,在服务器上使用自定义过滤器属性来装饰应使用OAuth2授权保护的资源端点:

 /// <summary>Restrict this service to clients with a valid OAuth2 access 
/// token granting access to the specified scopes.</summary>
[AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, Inherited = true)]
public class RequireOAuth2ScopeAttribute : RequestFilterAttribute {
    private readonly string[] oauth2Scopes;
    public RequireOAuth2ScopeAttribute(params string[] oauth2Scopes) {
        this.oauth2Scopes = oauth2Scopes;
    }

    public override void Execute(IHttpRequest request, IHttpResponse response, object requestDto) {
        try {
            var authServerKeys = AppHostBase.Instance.Container.ResolveNamed<ICryptoKeyPair>("authServer");
            var dataServerKeys = AppHostBase.Instance.Container.ResolveNamed<ICryptoKeyPair>("dataServer");
            var tokenAnalyzer = new StandardAccessTokenAnalyzer(authServerKeys.PublicSigningKey, dataServerKeys.PrivateEncryptionKey);
            var oauth2ResourceServer = new DotNetOpenAuth.OAuth2.ResourceServer(tokenAnalyzer);
            var wrappedRequest = new HttpRequestWrapper((HttpRequest)request.OriginalRequest);
            HttpContext.Current.User = oauth2ResourceServer.GetPrincipal(wrappedRequest, oauth2Scopes);
        } catch (ProtocolFaultResponseException x) {
            // see the GitHub project for detailed error-handling code
            throw;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

其次,这是您如何挂钩ServiceStack HTTP客户端管道并使用DotNetOpenAuth将OAuth2 Authorization: Bearer {key}令牌添加到传出请求:

// Create the ServiceStack API client and the request DTO
var apiClient = new JsonServiceClient("http://api.mysite.com/");
var apiRequestDto = new Shortlists { Name = "dylan" };

// Wire up the ServiceStack client filter so that DotNetOpenAuth can 
// add the authorization header before the request is sent
// to the API server
apiClient.LocalHttpWebRequestFilter = request => {
    // This is the magic line that makes all the client-side magic work :)
    ClientBase.AuthorizeRequest(request, accessTokenTextBox.Text);
}

// Send the API request and dump the response to our output TextBox
var helloResponseDto = apiClient.Get(apiRequestDto);

Console.WriteLine(helloResponseDto.Result);
Run Code Online (Sandbox Code Playgroud)

授权请求将成功; 丢失令牌,过期令牌或范围不足的请求将引发一个WebServiceException

这仍然是非常概念验证的东西,但似乎工作得很好.我欢迎任何比我更了解ServiceStack或DotNetOpenAuth的人的反馈.


Pau*_*ice 6

更新 在进一步思考时,您最初的想法是创建一个RequiredScope属性,这将是一种更清晰的方法.它添加到ServiceStack管道是添加IHasRequestFilter接口,实现自定义的请求过滤器一样简单,如下记载:https://github.com/ServiceStack/ServiceStack/wiki/Filter-attributes

public class RequireScopeAttribute : Attribute, IHasRequestFilter {
  public void RequireScope(IHttpRequest req, IHttpResponse res, object requestDto)
  {
      //This code is executed before the service
      //Close the request if user lacks required scope
  }

  ...
}
Run Code Online (Sandbox Code Playgroud)

然后按照您的概述装饰您的DTO或服务:

using ServiceStack.ServiceInterface;
using SpotAuth.Common.ServiceModel;

namespace SpotAuth.ResourceServer.Services {
    [RequireScope("hello")]
    public class HelloService : Service {
        public object Any(Hello request) {
            return new HelloResponse { Result = "Hello, " + request.Name };
        }
    }
}   
Run Code Online (Sandbox Code Playgroud)

您的RequireScope自定义过滤器几乎与ServiceStack的RequiredRoleAttribute实现完全相同.,所以用它作为编码的起点.

或者,您可以将范围映射到权限.然后相应地装饰你的DTO或服务(详见SS维基),例如:

[Authenticate]
[RequiredPermission("Hello")]
    public class HelloService : Service {
        public object Any(Hello request) {
            return new HelloResponse { Result = "Hello, " + request.Name };
        }
    }
Run Code Online (Sandbox Code Playgroud)

通常,ServiceStack在IAuthSession中调用方法bool HasPermission(字符串权限).此方法检查IAuthSession中的列表权限是否包含所需权限,因此,在自定义IAuthSession中,您可以覆盖HasPermission并将OAuth2范围检查到那里.