oauth - hacer - security asp net mvc
Aplicación MVC 5: implementar el flujo de código de autorización de OAuth (2)
Basado en este tutorial http://www.asp.net/aspnet/overview/owin-and-katana/owin-oauth-20-authorization-server , he creado un servidor de autorización, un servidor de recursos y un cliente MVC. El cliente MVC tiene un controlador que obtiene algunos datos del servidor de recursos. El servidor de recursos requiere autenticación. Los Clientes de MVC obtienen un código de autorización del Servidor de autorización y redirigen al usuario al Servidor de autorización para la autenticación. Finalmente, los clientes de MVC intercambian el código de autorización para un token de acceso para acceder al servidor de recursos. Este es el flujo de código de Autorización según lo descrito por el protocolo OAuth 2. Esto funciona bien
Ahora, tengo el requisito de hacer que un Controlador del MVC Client mismo requiera Autenticación. No puedo encontrar un tutorial para esto.
yo añadí
app.UseOAuthBearerAuthentication(new OAuthBearerAuthenticationOptions());
a mi Startup.Auth.cs. Supongo que necesito configurar las Opciones para redirigir al servidor de autorización. También puedo configurar el proveedor en las opciones:
app.UseOAuthBearerAuthentication(new Microsoft.Owin.Security.OAuth.OAuthBearerAuthenticationOptions()
{
Provider = new OAuthBearerAuthenticationProvider()
});
Pero también estoy atascado en la implementación de los eventos del proveedor. ¿Alguien puede guiarme en la dirección correcta? ¿O hay algún tutorial que pueda ayudarme?
Terminé con una solución basada en estos dos artículos de Brock Allen:
- http://brockallen.com/2013/10/24/a-primer-on-owin-cookie-authentication-middleware-for-the-asp-net-developer/
- http://brockallen.com/2014/01/09/a-primer-on-external-login-providers-social-logins-with-owinkatana-authentication-middleware/
La idea fundamental es registrar dos Middlewares de autenticación. Una autentificación de cookies activa y una autenticación pasiva de OAuthBearer. En Startup.Auth.cs se agregan así:
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,
});
También agrega un ExternalLogin-Controller. Su método de inicio de sesión tiene que redirigir al usuario a la página de inicio de sesión de su servidor de autorización para obtener el código de autorización. Debe proporcionar una función de devolución de llamada donde procesará el código de autorización.
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);
}
En su función de devolución de llamada, intercambia el código de autorización de un token de acceso (más token de actualización) desafíe su Middleware pasivo de autenticación OAuthBearer e inicie sesión con el token de acceso como su 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");
}
Espero que esto sea útil para alguien que está implementando el flujo de código de autorización OAuth en su aplicación MVC 5.
Utilicé la muestra oficial MVC Implicit Client, que creo que es el flujo de autenticación correcto para la aplicación MVC.
Para la autorización utilicé esto para comenzar , especialmente la parte sobre bucle infinito cuando se especifican los roles [Authorize(Roles = "Foo,Bar")]
y el usuario está autenticado pero no posee ninguno de estos.