Trabajar con OAuth2 y OpenID desde una aplicación Xamarin utilizando IdentityServer3

OAuth2 es un protocolo abierto que permite la autorización segura en un método simple y estándar de aplicaciones web, móviles y de escritorio . OpenID Connect es una capa de identidad simple en la parte superior del protocolo OAuth2 .
En cierto modo, OAuth2 es un protocolo para construir,
Lo que es exactamente lo que hace OpenID Connect. OIDC es más estricto que el protocolo OAuth2, que, gracias a ese rigor, lo abre a otros escenarios, como la autenticación. En estos días, la mayoría de las aplicaciones utilizan OIDC en lugar de OAuth2, ya que requieren iniciar sesión en una aplicación cliente o en información relacionada con la identidad, ambas provistas por OIDC.
¿Por qué de repente necesitamos todas estos antecedentes? La autenticación de formularios podría funcionar cuando se está creando una aplicación ASP.NET estándar de n niveles o incluso una aplicación basada en servicios que se ejecute en el mismo contexto. Pero en estos días, la mayoría de las aplicaciones modernas están basadas en API, con una API completamente separada de cualquier cliente que lo consuma (esto es realmente una de las restricciones fundamentales impuestas por una arquitectura basada en REST).
La aplicación cliente que consume esa API puede ser una aplicación ASP.NET MVC, que consume la API del servidor, pero también podría ser una aplicación móvil o una aplicación basada en JavaScript (por ejemplo, Angular). En este caso, la API Se consume desde un dispositivo cliente. La autenticación de formularios, obviamente, no es adecuada para esos escenarios. Pero … el envío de un string de nombre de usuario / contraseña en cada HttpRequest a tu API realmente no es algo que quieras hacer.
Los tokens representan el consentimiento, por ejemplo: concedido por el usuario, a la aplicación cliente, para acceder a una API — normalmente a través de ámbitos, en la jerga OAuth2.
Su aplicación cliente debería — de alguna manera — obtener un token. Ese símbolo se puede utilizar en el cliente, o se puede pasar a la API, que se utiliza en ese nivel para autorizar el acceso. Hace un tiempo, la gente escribía “servicios simbólicos” para eso. Típicamente: una llamada a un punto de inicio auto-creado / de inicio de sesión, que aceptaría su nombre de usuario / contraseña y devolvería un Token Web JSON.
Pero simplemente enviar su nombre de usuario / contraseña a una acción auto-creada en una API y obtener un token a cambio no es un buen ajuste para muchas aplicaciones, ni una buena idea. Aún así, es necesario que comparta su nombre de usuario / contraseña con esa API, que podría estar bien para su propia aplicación de confianza (aunque no soy un gran fans), pero es una muy mala idea para aplicaciones de terceros, incluidas otras aplicaciones. Que podría vivir en su empresa. Por otra parte, una vez que comenzamos a crear extremos como estos nosotros mismos, estamos esencialmente reinventando la rueda. Tenemos que implementar la expiración nosotros mismos. Tenemos que implementar la validación de token. Tenemos que implementar el cierre de sesión. Es posible que desee implementar soporte de token de referencia. Necesitaremos separar identidad y acceso, y así sucesivamente. En resumen, los errores son casi inevitables.
Introduzca el protocolo OAuth2. El protocolo OAuth2 define una forma de obtener de forma segura un tipo específico de token (“obtener autorización”) : un token de acceso. Lo hace a través de uno de los diferentes flujos / subvenciones como se define en el RFC. Es su responsabilidad elegir el flujo correcto, dependiendo del tipo de aplicación que esté construyendo.
Este tipo de token se utiliza para autorizar el acceso a esa API. Por supuesto, sigue siendo la API que elige si el consentimiento que contiene el token de acceso es suficiente para permitir el acceso. Por lo tanto, su aplicación cliente debe obtener un token de acceso de un servicio de token de seguridad (como IdentityServer) a través de una concesión / flujo específico y pasar ese token de acceso en cada llamada a la API.
Sin embargo, los clientes de acceso OAuth2 no deben utilizarse para la autenticación: el protocolo no es lo suficientemente estricto para permitirlo de forma segura. Sin embargo, OIDC, construido sobre OAuth2, define un nuevo tipo de token: un token de identidad. Y que se puede utilizar para la autenticación.
El ejemplo arquetípico es una aplicación ASP.NET MVC que llama a una API a través de una instancia de HttpClient. Utiliza el token de identidad para iniciar sesión en la aplicación ASP.NET MVC y utiliza el token de acceso para acceder a la API.
En este caso, esa aplicación cliente es un cliente Xamarin. Pero … no hay ningún uso real para iniciar sesión en un cliente de este tipo. Los clientes móviles (Xamarin, iOS nativo, Android, Windows Phone, …) se despliegan en un teléfono, un dispositivo que no es de confianza. No podemos no entregamos código dependiendo de la identidad del usuario: el código ya está presente en el dispositivo cliente. El mismo principio es válido para clientes basados ​​en JavaScript, como un cliente Angular. Esto es contrario a lo que podemos hacer con una tecnología de servidor, por ejemplo: una aplicación ASP.NET MVC. En esos tipos de aplicaciones, podemos bloquear eficazmente el acceso a partes del código — nunca se ejecutarán, los resultados nunca se servirán a su navegador. Para los clientes móviles, normalmente necesitamos fichas de acceso más de lo que necesitamos fichas de identidad.
Sin embargo, los tokens de identidad también se utilizan para que estos tipos de clientes obtengan información relacionada con la identidad del token (alternativamente, también puede utilizar el punto final de UserInfo). Por lo tanto, aunque realmente no estamos iniciando sesión en el cliente como lo haríamos en un cliente ASP.NET MVC, OpenID Connect tiene sentido ya que permite el acceso a la información relacionada con la identidad, ya sea directamente contenida en un token de identidad o por Utilizando un token de acceso que contiene ámbitos de identidad para acceder al punto final UserInfo. Junto a eso — quizás aún más importante -, OIDC es más estricto que OAuth2 (como se mencionó anteriormente), lo que significa que añade características de seguridad adicionales (entonces, por ejemplo) — Yo aconsejaría utilizar siempre OIDC, incluso si no Necesitan fichas de identidad.
Ahora, ¿cómo encaja IdentityServer en esta historia? Si usted lee a través de los RFC, notará que hay una gran cantidad de implementación que tiene que suceder a nivel de servidor. Esto no es algo que usted normalmente quiere hacer usted mismo: Dominick Baier y Brock Allen han hecho esto por nosotros. IdentityServer implementa los estándares OAuth2 y OIDC, entre otros. Lo que nosotros, como desarrolladores de las aplicaciones cliente y API tenemos que manejar es la parte del cliente de los estándares y asegurando que nuestra API pueda trabajar con esos tokens.
Hay un gran tutorial sobre cómo configurar IdentityServer en la documentación , en caso de que no lo haya hecho antes. El tutorial también contiene un ejemplo sobre cómo proteger su API.
Uff, hasta ahora para ese curso acelerado — he cortado algunas esquinas aquí y allá, pero esta es la esencia de la misma. En la aplicación de ejemplo.
La Aplicación de Ejemplo de Xamarin Forms
Como sabemos ahora, es importante utilizar el flujo correcto para el tipo de aplicación que va a construir. No utilizar el flujo correcto podría abrirse a riesgos que no desea; Por ejemplo: un flujo de credenciales de cliente, que se basa en un secreto de cliente, no debe utilizarse de las aplicaciones que se ejecutan en el cliente: no tienen medios de almacenar con seguridad ese secreto, haciendo que el secreto esencialmente inútil.
El flujo aconsejado para una aplicación móvil (nativa) es el flujo implícito, de acuerdo con la especificación actual, aunque también puede utilizar el flujo de código de autorización o el flujo híbrido. Hay bastante discusión sobre esto que está sucediendo actualmente, y esta postura podría cambiar en el futuro (cercano) (probablemente favoreciendo el flujo híbrido). Al igual que con todas las cosas relacionadas con la seguridad: el trabajo nunca es realmente terminado (que es, incidentalmente, bastante buenas noticias para mi seguridad laboral ;-)).
De todos modos, he implementado esto utilizando el flujo implícito, pero los otros flujos se implementaría de la misma manera.
Este es un flujo basado en la redirección, por lo que necesitaremos usar un WebView en nuestra aplicación Xamarin Forms. La idea general es que utilizaremos ese WebView para navegar hasta el punto final de autorización en el nivel de Identity Server, pasando los tipos de ámbitos y respuestas que necesitamos. En la página LoginView.xaml, hay una WebView oculta:
codigo
public void StartFlow(string responseType, string scope)
{
var authorizeRequest =
new AuthorizeRequest(“https://localhost:44333/core/connect/authorize”);

// dictionary with values for the authorize request
var dic = new Dictionary<string, string>();
dic.Add(“client_id”, “implicitclient”);
dic.Add(“response_type”, responseType);
dic.Add(“scope”, scope);
dic.Add(“redirect_uri”, “https://xamarin-oidc-sample/redirect”);
dic.Add(“nonce”, Guid.NewGuid().ToString(“N”));

// add CSRF token to protect against cross-site request forgery attacks.
_currentCSRFToken = Guid.NewGuid().ToString(“N”);
dic.Add(“state”, _currentCSRFToken);

var authorizeUri = authorizeRequest.Create(dic);

wvLogin.Source = authorizeUri;
wvLogin.IsVisible = true;
}
El código es de la muestra en el proyecto IdentityServer3.Samples, pero el proyecto autónomo también contiene código)
Como estamos trabajando con OIDC, debemos incluir un token en esa solicitud. Para protegernos de los ataques CSRF, debemos incluir un token CSRF, que es esencialmente un valor aleatorio, no adivinable, que se devolverá al cliente. Una vez que se ha recuperado, tenemos que comprobar que coincide con el valor que enviamos en la solicitud inicial.
Por lo tanto, lo primero que debe hacer: crear el URI en el punto final de autorización en el nivel IdentityServer. He utilizado el paquete IdentityModel para esto, lo que ayuda mucho con la creación de los URIs correctos y el análisis de los resultados. El método StartFlow contiene el código para crear el URI y navegar el WebView a ese URI.

Los botones de la muestra llaman a ese método, pasando en response_type & scopes. Por ejemplo, esta pieza de código pedirá un token de identidad y un token de acceso:
Private void GetIdTokenAndAccessToken (object sender, EventArgs e)
{
// al preguntar a ambos, podemos pedir ámbitos relacionados con la identidad
// así como ámbitos de recursos
StartFlow (“id_token token”, “openid profile read write”);
}
Una vez que el URI de WebView esté configurado con el URI que acabamos de crear, mostrará la página de inicio de sesión de IdentityServer, donde tendrá que proporcionar sus credenciales (alice / alice para el ejemplo de IdentityServer3.Samples, Kevin / secreto para el ejemplo independiente).
A continuación se analizan los tokens una vez que se redirigen. Estos tokens se incluyen en el URI y podemos analizarlos captando el evento Navigated del WebView y comprobando si el URI comienza con el URI de redirección que pasamos al crear el URI de autorización. AuthorizeResponse también forma parte del paquete IdentityModel. No olvide comprobar el token CSRF para protegerse contra ataques CSRF.
private void WvLogin_Navigating(object sender, WebNavigatingEventArgs e)
{
if (e.Url.Contains(“https://xamarin-oidc-sample/redirect”))
{
wvLogin.IsVisible = false;

// parse response
_authResponse = new AuthorizeResponse(e.Url);

// CSRF check
var state = _authResponse.Values[“state”];
if (state != _currentCSRFToken)
{
txtResult.Text = “CSRF token doesn’t match”;
return;
}

string decodedTokens = “”;
// decode tokens
if (!string.IsNullOrWhiteSpace(_authResponse.IdentityToken))
{
decodedTokens += “Identity token \r\n”;
decodedTokens += DecodeToken(_authResponse.IdentityToken) + “\r\n”;
}

if (!string.IsNullOrWhiteSpace(_authResponse.AccessToken))
{
decodedTokens += “Access token \r\n”;
decodedTokens += DecodeToken(_authResponse.AccessToken);
}

txtResult.Text = decodedTokens;
}
}
Y eso es todo lo que hay. A partir de este momento, usted tiene el token (s) que usted pidió, que contiene los ámbitos demandados. Puede utilizar el token de identidad para obtener información relacionada con la identidad y puede utilizar el token de acceso para acceder a su API.

Eso es todo por ahora, feliz Codificación!
Fuente y repositorio
Articulo original,
IdentityModel/IdentityModel.OidcClient2
IdentityModel.OidcClient2 – Certified C#/NetStandard OpenID Connect Client Library for native mobile/desktop…github.com
https://www.kevindockx.com/working-with-oauth2-and-openid-connect-from-a-xamarin-forms-application-using-identityserver3/