我正在爲我的SPA創建一個簡單的OAuth服務器,以及this教程。我可以向OAuth提供商請求JWT罰款。但是,當我在授權標題中發送JWT時(例如:Authorization Bearer <token>
),我總是收到401「此請求的授權已被拒絕」。ASP.NET JWT承載認證不會改變請求上下文中的用戶
單步執行代碼,我可以看到我的用戶在請求上下文中被設置爲匿名Windows身份驗證用戶,而不是JWT的所有者。
我看到很多人說在OAuth配置後把app.UseWebAPI()
調用,但我沒有在我的startup.cs中註冊WebAPI。我可以做?我目前在我的代碼中沒有app.UseWebAPI()
任何地方(並且API沒有授權就可以正常工作)。
這裏是我的startup.cs:
public partial class Startup
{
public void Configuration(IAppBuilder app)
{
ConfigureOAuth(app);
}
}
public partial class Startup
{
/// <summary>
/// OAuth configuration
/// </summary>
/// <param name="app"></param>
public void ConfigureOAuth(IAppBuilder app)
{
// Unique identifier for the entity issuing the token
var issuer = ConfigurationManager.AppSettings["issuer"];
// Private key used to secure the token
var secret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["secret"]);
// Enable JWT authentication
app.UseJwtBearerAuthentication(new JwtBearerAuthenticationOptions
{
AuthenticationMode = AuthenticationMode.Active,
AllowedAudiences = new[] { "Any" },
IssuerSecurityTokenProviders = new IIssuerSecurityTokenProvider[]
{
new SymmetricKeyIssuerSecurityTokenProvider(issuer, secret)
}
});
// Create authentication endponit
app.UseOAuthAuthorizationServer(new OAuthAuthorizationServerOptions
{
AllowInsecureHttp = true,
TokenEndpointPath = new PathString("/api/login/"),
AccessTokenExpireTimeSpan = TimeSpan.FromHours(1),
Provider = new CustomOAuthProvider(),
AccessTokenFormat = new CustomJwtFormat(issuer)
});
}
}
這裏是我的CustomOAuthProvider類:
public class CustomOAuthProvider : OAuthAuthorizationServerProvider
{
IMessengerRepository repo;
public CustomOAuthProvider() : base()
{
repo = new MessengerRepository();
}
public override Task GrantResourceOwnerCredentials(OAuthGrantResourceOwnerCredentialsContext context)
{
// Enable CORS
context.OwinContext.Response.Headers.Add("Access-Control-Allow-Origin", new[] { "*" });
// Check against the repository to see if the login details are correct
var user = repo.Authenticate(context.UserName, context.Password);
// If the login details are incorrect, return an error
if (user == null)
{
context.SetError("invalid_grant", "The username or password is incorrect");
context.Rejected();
return Task.FromResult<object>(null);
}
// Otherwise, return a JWT
var ticket = new AuthenticationTicket(SetClaimsIdentity(context, user), new AuthenticationProperties());
context.Validated(ticket);
return Task.FromResult<object>(null);
}
public override Task ValidateClientAuthentication(OAuthValidateClientAuthenticationContext context)
{
// Because the audience is not validated, the request is validated straight away without any checks
context.Validated();
return Task.FromResult<object>(null);
}
/// <summary>
/// Create a ClaimsIdentity based on their username and role
/// </summary>
/// <param name="context"></param>
/// <param name="user"></param>
/// <returns></returns>
public static ClaimsIdentity SetClaimsIdentity(OAuthGrantResourceOwnerCredentialsContext context, UserDto user)
{
var identity = new ClaimsIdentity("JWT");
// Add claims for the user themselves
identity.AddClaim(new Claim(ClaimTypes.Name, context.UserName));
identity.AddClaim(new Claim("sub", context.UserName));
// Add claim for the user's role
identity.AddClaim(new Claim(ClaimTypes.Role, user.UserRole.RoleName.TrimEnd(' ')));
return identity;
}
}
和我CustomJwtFormat類:
public class CustomJwtFormat : ISecureDataFormat<AuthenticationTicket>
{
// Private key for encoding token
private static readonly byte[] secret = TextEncodings.Base64Url.Decode(ConfigurationManager.AppSettings["secret"]);
// Entity issuing the token
private readonly string issuer;
public CustomJwtFormat(string issuer)
{
this.issuer = issuer;
}
public string Protect(AuthenticationTicket data)
{
if (data == null)
throw new ArgumentNullException(nameof(data));
var signingKey = new HmacSigningCredentials(secret);
var issued = data.Properties.IssuedUtc;
var expires = data.Properties.ExpiresUtc;
return new JwtSecurityTokenHandler().WriteToken(new JwtSecurityToken(issuer, null, data.Identity.Claims, issued.Value.UtcDateTime, expires.Value.UtcDateTime, signingKey));
}
public AuthenticationTicket Unprotect(string protectedText)
{
throw new NotImplementedException();
}
}