【问题标题】:.Net Web API 2, OWIN, and OAuth: Scopes and roles. What are the differences?.Net Web API 2、OWIN 和 OAuth:范围和角色。有什么区别?
【发布时间】:2016-07-22 16:40:59
【问题描述】:

我想更清楚地了解 .NET Web API 项目中角色和范围之间的区别。这比其他任何问题都更像是一个最佳方法问题,我发现自己对于如何最好地授权想要访问我的 API 的用户有点困惑。我来自 .NET MVC 背景,所以我熟悉角色,我想知道相同的方法是否适用于 Web API 框架。我很难将范围放在图片中,以及我应该如何使用它们来允许使用特定客户端 ID 的用户访问。范围是否类似于访问权限?为了说明我的困惑,让我们用这个例子:

Client A
Native app: displays event calendar
Role: Event
User login required? No
Allowed scopes: Read events

Client B
Web app: shows next upcoming event, displays registrant names
Role: Event
User login required? Yes
Allowed scopes: Read events, read registrants

Client C
Native app: registers a person for an event
Role: Registrant
User login required? Yes
Allowed scopes: Read events, read registrants, write registrants

基本上,我想知道我上面对范围的使用是否正确,以及授予资源所有者凭据的最佳方法是什么。我正在使用Taiseers tutorial 中概述的基于令牌的身份验证。以下是我当前不完整的代码 sn-p,它将负责验证请求的客户端和范围:

public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
    ApiClient client = null;
    string clientId = string.Empty;
    string clientSecret = string.Empty;

    if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
        context.TryGetFormCredentials(out clientId, out clientSecret);

    if (context.ClientId == null)
    {
        context.Validated();
        context.SetError("invalid_clientId", "ClientId should be sent.");
        return Task.FromResult<object>(null);
    }

    using (ApiClientRepo _clientRepo = context.OwinContext.GetUserManager<ApiClientRepo>())
    {
        client = _clientRepo.FindClient(context.ClientId);
    }

    if (client == null)
    {
        context.SetError("invalid_clientId", string.Format("Client '{0}' is not registered in the system.", context.ClientId));
        return Task.FromResult<object>(null);
    }

    // Validate client secret

    if (string.IsNullOrWhiteSpace(clientSecret))
    {
        context.SetError("invalid_secret", "Client secret should be sent.");
        return Task.FromResult<object>(null);
    }
    else
    {
        WPasswordHasher passwordHasher = new WPasswordHasher();
        PasswordVerificationResult passwordResult = passwordHasher.VerifyHashedPassword(client.SecretHash, clientSecret);

        if (passwordResult == PasswordVerificationResult.Failed)
        {
            context.SetError("invalid_secret", "Client secret is invalid.");
            return Task.FromResult<object>(null);
        }
    }

    if (!client.Active)
    {
        context.SetError("invalid_clientId", "Client is inactive.");
        return Task.FromResult<object>(null);
    }

    context.OwinContext.Set<int>("as:clientRepoId", client.Id);
    context.OwinContext.Set<string>("as:clientAllowedOrigin", client.AllowedOrigin);
    context.OwinContext.Set<string>("as:clientRefreshTokenLifeTime", client.RefreshTokenLifeTime.ToString());

    context.Validated();
    return Task.FromResult<object>(null);
}

public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
    IApiUser user = null;
    string scope = null;

    // Get parameters sent in body
    Dictionary<string, string> body = context.Request.GetBodyParameters();

    // Get API scope
    body.TryGetValue("scope", out scope);

    if (scope == null)
    {
        context.Validated();
        context.SetError("invalid_scope", "Invalid requested scope.");
        return;
    }

    var allowedOrigin = context.OwinContext.Get<string>("as:clientAllowedOrigin");

    context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { allowedOrigin });    

    // At this point I got the requested scope.
    // What should I do with it?

    if (user == null)
    {
        context.SetError("invalid_grant", "The user name or password is incorrect.");
        return;
    }      

    // create claims identity based on user info
    ClaimsIdentity identity = new ClaimsIdentity(context.Options.AuthenticationType);
    identity.AddClaim(new Claim(ClaimTypes.Name, user.FirstName + " " + user.LastName));
    identity.AddClaim(new Claim(ClaimTypes.NameIdentifier, user.Username));
    identity.AddClaim(new Claim(ClaimTypes.Role, scope));

    var props = new AuthenticationProperties(new Dictionary<string, string>
        {
            { 
                "as:client_id", (context.ClientId == null) ? string.Empty : context.ClientId
            },
            { 
                "userName", context.UserName
            }
        });

    var ticket = new AuthenticationTicket(identity, props);
    context.Validated(ticket);
}

提前感谢所有想法、建议和想法!

【问题讨论】:

    标签: oauth scope asp.net-web-api2 owin


    【解决方案1】:

    在我看来,范围定义资源。 基本上,请求挑战是“客户端(=应用程序)可以代表您访问资源 x”吗?

    其中 x 是您的 API 提供的任何资源。 我在一个项目中使用了一个方便,其中范围可以特定于对资源的 CRUD 操作。例如 scope = tweets.read 或 tweets.create。

    拥有一个范围的令牌并不会给客户权限。该权限基于用户有权执行操作并让客户端在其令牌中具有正确的资源范围这一事实。当然,用户权限可以基于访客或管理员等角色。

    所以理论上用户可以授予对它没有权限的范围(资源)的访问权限。

    令牌的生命周期为 20 分钟,如果您基于访问令牌中的任何值授予权限,则该权限不能在令牌生命周期内撤销或更改。

    【讨论】:

    • 感谢您的解释。我理解在您的情况下,您使用范围作为每个资源的权限,非常精细。我看了一下scopes document from Google,看来您的做法与他们相似。那么如何验证请求是否具有资源 x 的适当范围?您是否有自定义操作过滤器来执行此操作?
    • 我不会说它是权限本身,也许是客户端访问用户行为资源的权限。事实上,我已经使用自定义过滤器验证了范围,并在每个 WebApi 方法上定义了具有所需范围的自定义属性。
    • 啊,是的,这是有道理的。我相信我现在对 Web API 的范围和角色有了更清晰的认识。我对自定义过滤器的想法是相同的,以检查所需的范围。谢谢!
    猜你喜欢
    • 2017-04-19
    • 1970-01-01
    • 2012-06-12
    • 1970-01-01
    • 1970-01-01
    • 2014-12-22
    • 2013-08-19
    • 2015-02-26
    • 1970-01-01
    相关资源
    最近更新 更多