.Net Web API 2、OWIN 和 OAuth:范围和角色。有什么区别?
.Net Web API 2, OWIN, and OAuth: Scopes and roles. What are the differences?
我想在脑海中更清楚地了解 .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 中概述的基于令牌的身份验证。下面是我当前不完整的代码片段,它将负责验证请求的客户端和范围:
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);
}
在此先感谢所有想法、建议和想法!
在我看来,范围定义了资源。
基本上请求挑战是 "may client (=application) access resource x on your behalf"?
其中 x 是您 API 服务的任何资源。
我在一个项目中使用了一个便利,其中一个范围可以特定于对资源的 CRUD 操作。例如 scope = tweets.read 或 tweets.create.
拥有范围令牌并不会授予客户端权限。该权限基于这样一个事实,即用户有权执行操作,并让客户端在其令牌中拥有正确的资源范围。当然,用户权限可以基于访客或管理员等角色
因此理论上用户可以授予对其没有权限的范围(资源)的访问权限。
令牌的生命周期假设为 20 分钟,如果您将权限基于访问令牌中的任何值,则在令牌生命周期内无法撤销或更改权限。
我想在脑海中更清楚地了解 .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 中概述的基于令牌的身份验证。下面是我当前不完整的代码片段,它将负责验证请求的客户端和范围:
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);
}
在此先感谢所有想法、建议和想法!
在我看来,范围定义了资源。 基本上请求挑战是 "may client (=application) access resource x on your behalf"?
其中 x 是您 API 服务的任何资源。 我在一个项目中使用了一个便利,其中一个范围可以特定于对资源的 CRUD 操作。例如 scope = tweets.read 或 tweets.create.
拥有范围令牌并不会授予客户端权限。该权限基于这样一个事实,即用户有权执行操作,并让客户端在其令牌中拥有正确的资源范围。当然,用户权限可以基于访客或管理员等角色
因此理论上用户可以授予对其没有权限的范围(资源)的访问权限。
令牌的生命周期假设为 20 分钟,如果您将权限基于访问令牌中的任何值,则在令牌生命周期内无法撤销或更改权限。