我正在尝试在ASP.NET WebAPI 2中实现一个简单的OAuthAuthorizationServerProvider。我的主要目的是学习如何为移动应用程序生成令牌。我希望用户使用用户名和密码登录,然后接收一个令牌(以及一个刷新令牌,这样他们就不必在令牌过期后重新输入凭据)。稍后,我希望有机会向其他应用程序(比如使用Facebook api等)开放API。
以下是我设置AuthorizationServer的方式:
这是我的
我不完全理解
以下是我设置AuthorizationServer的方式:
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions()
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/token"),
AccessTokenExpireTimeSpan = TimeSpan.FromMinutes(5),
Provider = new SimpleAuthorizationServerProvider(new SimpleAuthorizationServerProviderOptions()
{
ValidateUserCredentialsFunction = ValidateUser
}),
RefreshTokenProvider = new SimpleRefreshTokenProvider()
});
这是我的
SimpleAuthorizationServerProviderOptions
实现:public class SimpleAuthorizationServerProvider : OAuthAuthorizationServerProvider
{
public delegate Task<bool> ClientCredentialsValidationFunction(string clientid, string secret);
public delegate Task<IEnumerable<Claim>> UserCredentialValidationFunction(string username, string password);
public SimpleAuthorizationServerProviderOptions Options { get; private set; }
public SimpleAuthorizationServerProvider(SimpleAuthorizationServerProviderOptions options)
{
if (options.ValidateUserCredentialsFunction == null)
{
throw new NullReferenceException("ValidateUserCredentialsFunction cannot be null");
}
Options = options;
}
public SimpleAuthorizationServerProvider(UserCredentialValidationFunction userCredentialValidationFunction)
{
Options = new SimpleAuthorizationServerProviderOptions()
{
ValidateUserCredentialsFunction = userCredentialValidationFunction
};
}
public SimpleAuthorizationServerProvider(UserCredentialValidationFunction userCredentialValidationFunction, ClientCredentialsValidationFunction clientCredentialsValidationFunction)
{
Options = new SimpleAuthorizationServerProviderOptions()
{
ValidateUserCredentialsFunction = userCredentialValidationFunction,
ValidateClientCredentialsFunction = clientCredentialsValidationFunction
};
}
public override async Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
if (Options.ValidateClientCredentialsFunction != null)
{
string clientId, clientSecret;
if (!context.TryGetBasicCredentials(out clientId, out clientSecret))
{
context.TryGetFormCredentials(out clientId, out clientSecret);
}
var clientValidated = await Options.ValidateClientCredentialsFunction(clientId, clientSecret);
if (!clientValidated)
{
context.Rejected();
return;
}
}
context.Validated();
}
public override async Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
if (Options.ValidateUserCredentialsFunction == null)
{
throw new NullReferenceException("ValidateUserCredentialsFunction cannot be null");
}
var claims = await Options.ValidateUserCredentialsFunction(context.UserName, context.Password);
if (claims == null)
{
context.Rejected();
return;
}
// create identity
var identity = new ClaimsIdentity(claims, context.Options.AuthenticationType);
// create metadata to pass to refresh token provider
var props = new AuthenticationProperties(new Dictionary<string, string>()
{
{ "as:client_id", context.UserName }
});
var ticket = new AuthenticationTicket(identity, props);
context.Validated(ticket);
}
public override async Task GrantRefreshToken(OAuthGrantRefreshTokenContext context)
{
var originalClient = context.Ticket.Properties.Dictionary["as:client_id"];
var currentClient = context.ClientId;
// enforce client binding of refresh token
if (originalClient != currentClient)
{
context.Rejected();
return;
}
// chance to change authentication ticket for refresh token requests
var newIdentity = new ClaimsIdentity(context.Ticket.Identity);
newIdentity.AddClaim(new Claim("newClaim", "refreshToken"));
var newTicket = new AuthenticationTicket(newIdentity, context.Ticket.Properties);
context.Validated(newTicket);
}
}
这是我的SimpleRefreshTokenProvider
实现:
public class SimpleRefreshTokenProvider : IAuthenticationTokenProvider
{
private static ConcurrentDictionary<string, AuthenticationTicket> _refreshTokens =
new ConcurrentDictionary<string, AuthenticationTicket>();
public void Create(AuthenticationTokenCreateContext context)
{
}
public async Task CreateAsync(AuthenticationTokenCreateContext context)
{
var guid = Guid.NewGuid().ToString();
var refreshTokenProperties = new AuthenticationProperties(context.Ticket.Properties.Dictionary)
{
IssuedUtc = context.Ticket.Properties.IssuedUtc,
ExpiresUtc = DateTime.UtcNow.AddYears(1)
};
var refreshTokenTicket = new AuthenticationTicket(context.Ticket.Identity, refreshTokenProperties);
_refreshTokens.TryAdd(guid, refreshTokenTicket);
context.SetToken(guid);
}
public void Receive(AuthenticationTokenReceiveContext context)
{
}
public async Task ReceiveAsync(AuthenticationTokenReceiveContext context)
{
AuthenticationTicket ticket;
if (_refreshTokens.TryRemove(context.Token, out ticket))
{
context.SetTicket(ticket);
}
}
}
我不完全理解
ClientId和Secret
与Username和Password
的使用区别。我粘贴的代码通过用户名和密码生成一个令牌,我可以使用该令牌(直到过期),但是当我尝试获取刷新令牌时,必须拥有ClientId。此外,如果令牌过期,正确的方法是发送刷新令牌并获取新令牌吗?如果刷新令牌被盗,那怎么办呢?这难道不就像用户名和密码被盗一样吗?