实施OAuth授权码流程
基于本教程http://www.asp.net/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server,我创建了一个授权服务器,一个资源服务器和一个MVC客户端。 MVC客户端有一个控制器,它从资源服务器获取一些数据。 资源服务器需要认证。 MVC客户端从授权服务器获取授权码,并将用户重定向到授权服务器进行验证。 最后,MVC客户端交换访问令牌的授权码以访问资源服务器。 这是OAuth 2协议所描述的授权码流程。 这工作正常。
现在,我有要求使MVC客户端的控制器本身需要身份验证。 我找不到这个教程。
我补充说
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
到我的Startup.Auth.cs。 我假设,我需要设置选项重定向到授权服务器。 我也可以在选项上设置提供者:
app.UseOAuthBearerAuthentication(new Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationOptions()
{
Provider = new OAuthBearerAuthenticationProvider()
});
但我也坚持实施提供者的事件。 任何人都可以引导我朝着正确的方向发展吗? 或者是否有任何教程可以帮助我?
我最终得到了来自Brock Allen的这两篇文章的解决方案:
有趣的想法是注册两个认证中间件。 活动的Cookie身份验证和被动OAuthBearer身份验证。 在Startup.Auth.cs中,它们是这样添加的:
app.UseCookieAuthentication(new CookieAuthenticationOptions()
{
AuthenticationType = DefaultAuthenticationTypes.ApplicationCookie,
LoginPath = new PathString("/ExternalLogin/Login"),
});
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions()
{
AuthenticationType = DefaultAuthenticationTypes.ExternalBearer,
AuthenticationMode = Microsoft.Owin.Security.AuthenticationMode.Passive,
});
您还需要添加一个ExternalLogin-Controller。 它的登录方法必须将用户重定向到授权服务器的登录页面以获取授权码。 您必须提供一个回调函数,您将在其中处理授权码。
public async Task<ActionResult> Login(string returnUrl)
{
if (string.IsNullOrEmpty(returnUrl) && Request.UrlReferrer != null)
returnUrl = Server.UrlEncode(Request.UrlReferrer.PathAndQuery);
if (Url.IsLocalUrl(returnUrl) && !string.IsNullOrEmpty(returnUrl))
_returnUrl = returnUrl;
//callback function
_redirectUrl = Url.Action("AuthorizationCodeCallback", "ExternalLogin", null, Request.Url.Scheme);
Dictionary<string, string> authorizeArgs = null;
authorizeArgs = new Dictionary<string, string>
{
{"client_id", "0123456789"}
,{"response_type", "code"}
,{"scope", "read"}
,{"redirect_uri", _redirectUrl}
// optional: state
};
var content = new FormUrlEncodedContent(authorizeArgs);
var contentAsString = await content.ReadAsStringAsync();
return Redirect("http://localhost:64426/oauth/authorize?" + contentAsString);
}
在您的回调函数中,您交换访问令牌(加上刷新令牌)的授权代码来挑战您的被动OAuthBearer身份验证中间件,并使用Access令牌作为您的Cookie进行签名。
public async Task<ActionResult> AuthorizationCodeCallback()
{
// received authorization code from authorization server
string[] codes = Request.Params.GetValues("code");
var authorizationCode = "";
if (codes.Length > 0)
authorizationCode = codes[0];
// exchange authorization code at authorization server for an access and refresh token
Dictionary<string, string> post = null;
post = new Dictionary<string, string>
{
{"client_id", "0123456789"}
,{"client_secret", "ClientSecret"}
,{"grant_type", "authorization_code"}
,{"code", authorizationCode}
,{"redirect_uri", _redirectUrl}
};
var client = new HttpClient();
var postContent = new FormUrlEncodedContent(post);
var response = await client.PostAsync("http://localhost:64426/token", postContent);
var content = await response.Content.ReadAsStringAsync();
// received tokens from authorization server
var json = JObject.Parse(content);
_accessToken = json["access_token"].ToString();
_authorizationScheme = json["token_type"].ToString();
_expiresIn = json["expires_in"].ToString();
if (json["refresh_token"] != null)
_refreshToken = json["refresh_token"].ToString();
//SignIn with Token, SignOut and create new identity for SignIn
Request.Headers.Add("Authorization", _authorizationScheme + " " + _accessToken);
var ctx = Request.GetOwinContext();
var authenticateResult = await ctx.Authentication.AuthenticateAsync(DefaultAuthenticationTypes.ExternalBearer);
ctx.Authentication.SignOut(DefaultAuthenticationTypes.ExternalBearer);
var applicationCookieIdentity = new ClaimsIdentity(authenticateResult.Identity.Claims, DefaultAuthenticationTypes.ApplicationCookie);
ctx.Authentication.SignIn(applicationCookieIdentity);
var ctxUser = ctx.Authentication.User;
var user = Request.RequestContext.HttpContext.User;
//redirect back to the view which required authentication
string decodedUrl = "";
if (!string.IsNullOrEmpty(_returnUrl))
decodedUrl = Server.UrlDecode(_returnUrl);
if (Url.IsLocalUrl(decodedUrl))
return Redirect(decodedUrl);
else
return RedirectToAction("Index", "Home");
}
我希望这对于在他的MVC 5应用程序中实现OAuth授权代码流的人员非常有用。
我使用了官方示例MVC隐式客户端,我相信这是MVC应用程序的正确验证流程。
对于授权,我使用了这个入门,特别是当指定角色[Authorize(Roles = "Foo,Bar")]
时有关无限循环的部分[Authorize(Roles = "Foo,Bar")]
并且用户已通过身份验证但未拥有任何这些身份。