Compartir vía


Autenticación y autorización

Nota:

Este libro electrónico se publicó en la primavera de 2017 y no se ha actualizado desde entonces. Mucho contenido del libro sigue siendo valioso, pero algunos de los materiales están obsoletos.

La autenticación es el proceso de obtener credenciales de identificación, como el nombre y la contraseña de un usuario, y validar esas credenciales en una autoridad. Si las credenciales son válidas, la entidad que envió las credenciales se considera una identidad autenticada. Una vez autenticada una identidad, un proceso de autorización determina si esa identidad tiene acceso a un recurso determinado.

Hay muchos enfoques para integrar la autenticación y autorización en una aplicación de Xamarin.Forms que se comunica con una aplicación web de MVC de ASP.NET, incluido el uso de ASP.NET Core Identity, proveedores de autenticación externos, como Microsoft, Google, Facebook o Twitter, y middleware de autenticación. La aplicación móvil eShopOnContainers realiza la autenticación y la autorización con un microservicio de identidad en contenedor que usa IdentityServer 4. La aplicación móvil solicita tokens de seguridad de IdentityServer, ya sea para autenticar a un usuario o para acceder a un recurso. Para que IdentityServer emita tokens en nombre de un usuario, el usuario debe iniciar sesión en IdentityServer. Sin embargo, IdentityServer no proporciona una interfaz de usuario ni una base de datos para la autenticación. Por lo tanto, en la aplicación de referencia eShopOnContainers, ASP.NET Core Identity se usa para este propósito.

Autenticación

La autenticación es necesaria cuando una aplicación necesita conocer la identidad del usuario actual. El mecanismo principal de ASP.NET Core para identificar a los usuarios es el sistema de pertenencia a ASP.NET Core Identity, que almacena la información del usuario en un almacén de datos configurado por el desarrollador. Normalmente, este almacén de datos será un almacén de EntityFramework, aunque se pueden usar almacenes personalizados o paquetes de terceros para almacenar información de identidad en Azure Storage, Azure Cosmos DB u otras ubicaciones.

En escenarios de autenticación que usan un almacén de datos de usuario local y que conservan la información de identidad entre las solicitudes a través de cookies (como es habitual en ASP.NET aplicaciones web de MVC), ASP.NET Core Identity es una solución adecuada. En cambio, las cookies no son una manera natural de conservar y transmitir datos. Por ejemplo, una aplicación web de ASP.NET Core que expone puntos de conexión RESTful a los que se accede desde una aplicación móvil normalmente necesitará usar la autenticación de token de portador, ya que las cookies no se pueden usar en este escenario. Sin embargo, los tokens de portador se pueden recuperar e incluir fácilmente en el encabezado de autorización de las solicitudes web realizadas desde la aplicación móvil.

Emisión de tokens de portador mediante IdentityServer 4

IdentityServer 4 es un marco de código abierto de OpenID Connect y OAuth 2.0 para ASP.NET Core, que se puede usar para muchos escenarios de autenticación y autorización, incluida la emisión de tokens de seguridad para usuarios locales de ASP.NET Core Identity.

Nota:

OpenID Connect y OAuth 2.0 son muy similares, aunque tienen diferentes responsabilidades.

OpenID Connect es una capa de identidad basada en el protocolo OAuth 2.0. OAuth 2 es un protocolo que permite a las aplicaciones solicitar tokens de acceso desde un servicio de token de seguridad y usarlos para comunicarse con las API. Esta delegación reduce la complejidad tanto en las aplicaciones cliente como en las API, ya que la autenticación y la autorización se pueden centralizar.

La combinación de OpenID Connect y OAuth 2.0 combinan los dos problemas de seguridad fundamentales de la autenticación y el acceso a la API, y IdentityServer 4 es una implementación de estos protocolos.

En las aplicaciones que usan la comunicación directa de cliente a microservicio, como la aplicación de referencia eShopOnContainers, se puede usar un microservicio de autenticación dedicado que actúa como servicio de token de seguridad (STS) para autenticar a los usuarios, como se muestra en la figura 9-1. Para obtener más información sobre la comunicación directa de cliente a microservicio, consulte comunicación entre cliente y microservicios.

Autenticación mediante un microservicio de autenticación dedicado

Figura 9-1: autenticación por un microservicio de autenticación dedicado

La aplicación móvil eShopOnContainers se comunica con el microservicio de identidad, que usa IdentityServer 4 para realizar la autenticación y el control de acceso para las API. Por lo tanto, la aplicación móvil solicita tokens de IdentityServer, ya sea para autenticar a un usuario o para acceder a un recurso:

  • La autenticación de usuarios con IdentityServer se logra mediante la aplicación móvil que solicita un token de identidad, que representa el resultado de un proceso de autenticación. Como mínimo, contiene un identificador para el usuario e información sobre cómo y cuándo se autentica el usuario. También puede contener datos de identidad adicionales.
  • El acceso a un recurso con IdentityServer se logra mediante la aplicación móvil que solicita un token de acceso, que permite el acceso a un recurso de API. Los clientes solicitan tokens de acceso y los reenvían a la API. Los tokens de acceso contienen información sobre el cliente y el usuario, (si están presentes). A continuación, las API usan esa información para autorizar el acceso a sus datos.

Nota:

Un cliente debe registrarse con IdentityServer para poder solicitar tokens.

Adición de IdentityServer a una aplicación web

Para que una aplicación web de ASP.NET Core use IdentityServer 4, debe agregarse a la solución de Visual Studio de la aplicación web. Para obtener más información, consulte Información general en la documentación de IdentityServer.

Una vez que IdentityServer se incluye en la solución de Visual Studio de la aplicación web, se debe agregar a la canalización de procesamiento de solicitudes HTTP de la aplicación web, de modo que pueda atender solicitudes a los puntos de conexión de OpenID Connect y OAuth 2.0. Esto se logra en el método Configure de la clase Startup de la aplicación web, como se muestra en el ejemplo de código siguiente:

public void Configure(  
    IApplicationBuilder app, IHostingEnvironment env, ILoggerFactory loggerFactory)  
{  
    ...  
    app.UseIdentity();  
    ...  
}

El orden es importante en la canalización de procesamiento de solicitudes HTTP de la aplicación web. Por lo tanto, IdentityServer debe agregarse a la canalización antes del marco de la interfaz de usuario que implementa la pantalla de inicio de sesión.

Configuración de IdentityServer

IdentityServer debe configurarse en el método ConfigureServices de la clase Startup de la aplicación web llamando al método services.AddIdentityServer, como se muestra en el ejemplo de código siguiente de la aplicación de referencia eShopOnContainers:

public void ConfigureServices(IServiceCollection services)  
{  
    ...  
    services.AddIdentityServer(x => x.IssuerUri = "null")  
        .AddSigningCredential(Certificate.Get())                 
        .AddAspNetIdentity<ApplicationUser>()  
        .AddConfigurationStore(builder =>  
            builder.UseSqlServer(connectionString, options =>  
                options.MigrationsAssembly(migrationsAssembly)))  
        .AddOperationalStore(builder =>  
            builder.UseSqlServer(connectionString, options =>  
                options.MigrationsAssembly(migrationsAssembly)))  
        .Services.AddTransient<IProfileService, ProfileService>();  
}

Después de llamar al método services.AddIdentityServer, se llama a otras API fluidas para configurar lo siguiente:

  • Credenciales usadas para firmar.
  • Los recursos de identidad y de API a los que los usuarios podrían solicitar acceso.
  • Los clientes que se conectarán para solicitar tokens.
  • ASP.NET Core Identity.

Sugerencia

Cargue dinámicamente la configuración de IdentityServer 4. Las API de IdentityServer 4 permiten configurar IdentityServer desde una lista en memoria de objetos de configuración. En la aplicación de referencia eShopOnContainers, estas colecciones en memoria se codifican de forma rígida en la aplicación. Sin embargo, en escenarios de producción, se pueden cargar dinámicamente desde un archivo de configuración o desde una base de datos.

Para obtener información sobre cómo configurar IdentityServer para usar ASP.NET Core Identity, consulte Uso de ASP.NET Core Identity en la documentación de IdentityServer.

Configuración de recursos de API

Al configurar recursos de API, el método AddInMemoryApiResources espera una colección IEnumerable<ApiResource>. En el ejemplo de código siguiente se muestra el método GetApis que proporciona esta colección en la aplicación de referencia eShopOnContainers:

public static IEnumerable<ApiResource> GetApis()  
{  
    return new List<ApiResource>  
    {  
        new ApiResource("orders", "Orders Service"),  
        new ApiResource("basket", "Basket Service")  
    };  
}

Este método especifica que IdentityServer debe proteger las API de cesta de la compra y pedidos. Por lo tanto, se necesitarán tokens de acceso administrados por IdentityServer al realizar llamadas a estas API. Para obtener más información sobre el tipo ApiResource, consulte Recurso de API en la documentación de IdentityServer 4.

Configuración de recursos de identidad

Al configurar recursos de API, el método AddInMemoryIdentityResources espera una colección IEnumerable<IdentityResource>. Los recursos de identidad son datos como el identificador de usuario, el nombre o la dirección de correo electrónico. Cada recurso de identidad tiene un nombre único y se pueden asignar tipos de notificación arbitrarios, que se incluirán en el token de identidad para el usuario. En el ejemplo de código siguiente se muestra el método GetResources que proporciona esta colección en la aplicación de referencia eShopOnContainers:

public static IEnumerable<IdentityResource> GetResources()  
{  
    return new List<IdentityResource>  
    {  
        new IdentityResources.OpenId(),  
        new IdentityResources.Profile()  
    };  
}

La especificación de OpenID Connect especifica algunos recursos de identidad estándar. El requisito mínimo es que se proporcione compatibilidad para emitir un identificador único para los usuarios. Esto se logra mediante la exposición del recurso de identidad IdentityResources.OpenId.

Nota:

La clase IdentityResources admite todos los ámbitos definidos en la especificación de OpenID Connect (OpenID, correo electrónico, perfil, teléfono y dirección).

IdentityServer también admite la definición de recursos de identidad personalizados. Para obtener más información sobre el tipo de IdentityResource, consulte Identity Resource en la documentación de IdentityServer 4.

Configuración de clientes

Los clientes son aplicaciones que pueden solicitar tokens de IdentityServer. Normalmente, se debe definir la siguiente configuración para cada cliente como mínimo:

  • Un identificador de cliente único.
  • Las interacciones permitidas con el servicio de token (conocido como tipo de concesión).
  • La ubicación a la que se envían los tokens de identidad y acceso (conocida como URI de redireccionamiento).
  • Una lista de recursos a los que el cliente puede acceder (conocidos como ámbitos).

Al configurar clientes, el método AddInMemoryClients espera una colección IEnumerable<Client>. En el ejemplo de código siguiente se muestra la configuración de la aplicación móvil eShopOnContainers en el método GetClients que proporciona esta colección en la aplicación de referencia eShopOnContainers:

public static IEnumerable<Client> GetClients(Dictionary<string,string> clientsUrl)
{
    return new List<Client>
    {
        ...
        new Client
        {
            ClientId = "xamarin",
            ClientName = "eShop Xamarin OpenId Client",
            AllowedGrantTypes = GrantTypes.Hybrid,
            ClientSecrets =
            {
                new Secret("secret".Sha256())
            },
            RedirectUris = { clientsUrl["Xamarin"] },
            RequireConsent = false,
            RequirePkce = true,
            PostLogoutRedirectUris = { $"{clientsUrl["Xamarin"]}/Account/Redirecting" },
            AllowedCorsOrigins = { "http://eshopxamarin" },
            AllowedScopes = new List<string>
            {
                IdentityServerConstants.StandardScopes.OpenId,
                IdentityServerConstants.StandardScopes.Profile,
                IdentityServerConstants.StandardScopes.OfflineAccess,
                "orders",
                "basket"
            },
            AllowOfflineAccess = true,
            AllowAccessTokensViaBrowser = true
        },
        ...
    };
}

Esta configuración especifica los datos de las siguientes propiedades:

  • ClientId: Un identificador único para el cliente.
  • ClientName: El nombre para mostrar del cliente, que se usa para el registro y la pantalla de consentimiento.
  • AllowedGrantTypes: Especifica cómo un cliente quiere interactuar con IdentityServer. Para obtener más información, consulte Configuración del flujo de autenticación.
  • ClientSecrets: Especifica las credenciales del secreto de cliente que se usan al solicitar tokens desde el punto de conexión del token.
  • RedirectUris: Especifica los URI permitidos a los que se van a devolver tokens o códigos de autorización.
  • RequireConsent: Especifica si se requiere una pantalla de consentimiento.
  • RequirePkce: Especifica si los clientes que usan un código de autorización deben enviar una clave de prueba.
  • PostLogoutRedirectUris: Especifica los URI permitidos a los que se realizará el redireccionamiento después de cerrar la sesión.
  • AllowedCorsOrigins: Especifica el origen del cliente para que IdentityServer pueda permitir llamadas entre orígenes desde el origen.
  • AllowedScopes: Especifica los recursos a los que tiene acceso el cliente. De forma predeterminada, un cliente no tiene acceso a ningún recurso.
  • AllowOfflineAccess: Especifica si el cliente puede solicitar tokens de actualización.

Configuración del flujo de autenticación

El flujo de autenticación entre un cliente y IdentityServer se puede configurar especificando los tipos de concesión en la propiedad Client.AllowedGrantTypes. Las especificaciones de OpenID Connect y OAuth 2.0 definen una serie de flujos de autenticación, entre los que se incluyen:

  • Implícitas. Este flujo está optimizado para aplicaciones basadas en el explorador y se debe usar para las solicitudes de solo autenticación de usuario o de autenticación y token de acceso. Todos los tokens se transmiten a través del explorador y, por tanto, no se admiten características avanzadas como los tokens de actualización.
  • Código de autorización. Este flujo proporciona la capacidad de recuperar tokens en un canal posterior, en lugar del canal frontal del explorador, a la vez que admite la autenticación de cliente.
  • Híbrido. Este flujo es una combinación de los tipos de concesión de código implícito y de autorización. El token de identidad se transmite a través del canal del explorador y contiene la respuesta del protocolo firmado junto con otros artefactos, como el código de autorización. Después de validar correctamente la respuesta, se debe usar el canal back para recuperar el token de acceso y actualización.

Sugerencia

Use el flujo de autenticación híbrida. El flujo de autenticación híbrida mitiga varios ataques que se aplican al canal del explorador y es el flujo recomendado para las aplicaciones nativas que desean recuperar tokens de acceso, y posiblemente actualizar tokens.

Para obtener más información sobre los flujos de autenticación, consulte Tipos de concesión en la documentación de IdentityServer 4.

Realización de la autenticación

Para que IdentityServer emita tokens en nombre de un usuario, el usuario debe iniciar sesión en IdentityServer. Sin embargo, IdentityServer no proporciona una interfaz de usuario ni una base de datos para la autenticación. Por lo tanto, en la aplicación de referencia eShopOnContainers, ASP.NET Core Identity se usa para este propósito.

La aplicación móvil eShopOnContainers se autentica con IdentityServer con el flujo de autenticación híbrida, que se muestra en la figura 9-2.

Información general de alto nivel del proceso de inicio de sesión

Figura 9-2: Información general de alto nivel del proceso de inicio de sesión

Se realiza una solicitud de inicio de sesión para <base endpoint>:5105/connect/authorize. Después de una autenticación correcta, IdentityServer devuelve una respuesta de autenticación que contiene un código de autorización y un token de identidad. El código de autorización se envía a <base endpoint>:5105/connect/token, que responde con tokens de acceso, identidad y actualización.

La aplicación móvil eShopOnContainers cierra sesión en IdentityServer enviando una solicitud a <base endpoint>:5105/connect/endsession, con parámetros adicionales. Después de que se produzca el cierre de sesión, IdentityServer responde enviando un URI de redirección de cierre de sesión posterior a la aplicación móvil. En la figura 9-3 se muestra este proceso.

Información general de alto nivel del proceso de cierre de sesión

Figura 9-3: Información general de alto nivel del proceso de cierre de sesión

En la aplicación móvil eShopOnContainers, la comunicación con IdentityServer se realiza mediante la clase IdentityService, que implementa la interfaz IIdentityService. La interfaz especifica que la clase que realiza la implementación debe proporcionar los métodos CreateAuthorizationRequest, CreateLogoutRequest y GetTokenAsync.

Inicio de sesión

Cuando el usuario pulsa el botón LOGIN del LoginView, se ejecuta el SignInCommand de la clase LoginViewModel, que a su vez ejecuta el método SignInAsync. El siguiente ejemplo de código muestra este método:

private async Task SignInAsync()  
{  
    ...  
    LoginUrl = _identityService.CreateAuthorizationRequest();  
    IsLogin = true;  
    ...  
}

Este método invoca el método CreateAuthorizationRequest en la clase IdentityService , que se muestra en el ejemplo de código siguiente:

public string CreateAuthorizationRequest()
{
    // Create URI to authorization endpoint
    var authorizeRequest = new AuthorizeRequest(GlobalSetting.Instance.IdentityEndpoint);

    // Dictionary with values for the authorize request
    var dic = new Dictionary<string, string>();
    dic.Add("client_id", GlobalSetting.Instance.ClientId);
    dic.Add("client_secret", GlobalSetting.Instance.ClientSecret); 
    dic.Add("response_type", "code id_token");
    dic.Add("scope", "openid profile basket orders locations marketing offline_access");
    dic.Add("redirect_uri", GlobalSetting.Instance.Callback);
    dic.Add("nonce", Guid.NewGuid().ToString("N"));
    dic.Add("code_challenge", CreateCodeChallenge());
    dic.Add("code_challenge_method", "S256");

    // Add CSRF token to protect against cross-site request forgery attacks.
    var currentCSRFToken = Guid.NewGuid().ToString("N");
    dic.Add("state", currentCSRFToken);

    var authorizeUri = authorizeRequest.Create(dic); 
    return authorizeUri;
}

Este método crea el URI para el punto de conexión de autorización de IdentityServer con los parámetros necesarios. El punto de conexión de autorización está en /connect/authorize en el puerto 5105 del punto de conexión de base expuesto como una configuración del usuario. Para obtener más información sobre la configuración del usuario, consulte Administración de configuración.

Nota:

La superficie expuesta a ataques de la aplicación móvil eShopOnContainers se reduce mediante la implementación de la extensión Proof Key for Code Exchange (PKCE) en OAuth. PKCE protege el código de autorización para que no se use en caso de que se intercepte. Esto se logra mediante el cliente que genera un comprobador de secretos, cuyo hash se pasa en la solicitud de autorización, y que se presenta sin hash al canjear el código de autorización. Para obtener más información sobre PKCE, vea Clave de prueba para intercambio de código por clientes públicos de OAuth en el sitio web de Internet Engineering Task Force.

El URI devuelto se almacena en la propiedad LoginUrl de la clase LoginViewModel. Cuando la propiedad IsLogin se convierte en true, el WebView de la LoginView se vuelve visible. Los datos de WebView enlazan su propiedad Source a la propiedad LoginUrl de la clase LoginViewModel, por lo que realiza una solicitud de inicio de sesión a IdentityServer cuando la propiedad LoginUrl está establecida en el punto de conexión de autorización de IdentityServer. Cuando IdentityServer recibe esta solicitud y el usuario no está autenticado, el WebView se redirigirá a la página de inicio de sesión configurada, que se muestra en la figura 9-4.

Página de inicio de sesión mostrada por WebView

Figura 9-4: página inicio de sesión mostrada por WebView

Una vez completado el inicio de sesión, a WebView se le redirigirá a un URI de retorno. Esta navegación WebView provocará la ejecución del método NavigateAsync de la clase LoginViewModel, que se muestra en el ejemplo de código siguiente:

private async Task NavigateAsync(string url)  
{  
    ...  
    var authResponse = new AuthorizeResponse(url);  
    if (!string.IsNullOrWhiteSpace(authResponse.Code))  
    {  
        var userToken = await _identityService.GetTokenAsync(authResponse.Code);  
        string accessToken = userToken.AccessToken;  

        if (!string.IsNullOrWhiteSpace(accessToken))  
        {  
            Settings.AuthAccessToken = accessToken;  
            Settings.AuthIdToken = authResponse.IdentityToken;  

            await NavigationService.NavigateToAsync<MainViewModel>();  
            await NavigationService.RemoveLastFromBackStackAsync();  
        }  
    }  
    ...  
}

Este método analiza la respuesta de autenticación contenida en el URI de retorno y, siempre que haya un código de autorización válido, realiza una solicitud al punto de conexión del token de IdentityServer, pasando el código de autorización, el comprobador de secretos PKCE y otros parámetros necesarios. El punto de conexión del token está en /connect/token en el puerto 5105 del punto de conexión de base expuesto como una configuración del usuario. Para obtener más información sobre la configuración del usuario, consulte Administración de configuración.

Sugerencia

Valide los URI devueltos. Aunque la aplicación móvil eShopOnContainers no valida el URI de retorno, el procedimiento recomendado es validar que el URI de devolución hace referencia a una ubicación conocida, para evitar ataques de redirección abierta.

Si el punto de conexión del token recibe un código de autorización válido y un comprobador de secretos PKCE, responde con un token de acceso, un token de identidad y un token de actualización. El token de acceso (que permite el acceso a los recursos de API) y el token de identidad se almacenan como configuración de la aplicación y se realiza la navegación por páginas. Por lo tanto, el efecto global en la aplicación móvil eShopOnContainers es el siguiente: siempre que los usuarios puedan autenticarse correctamente con IdentityServer, se les dirige a la página MainView, que es una TabbedPage que muestra la CatalogView como pestaña seleccionada.

Para obtener información sobre la navegación por páginas, consulte Navegación. Para obtener información sobre cómo la navegación de WebView hace que se ejecute un método de modelo de vista, consulte Invocación de navegación mediante comportamientos. Para obtener información sobre la configuración de la aplicación, consulte Administración de configuración.

Nota:

eShopOnContainers también permite un inicio de sesión ficticio cuando la aplicación está configurada para usar servicios ficticios en SettingsView. En este modo, la aplicación no se comunica con IdentityServer, sino que permite al usuario iniciar sesión con las credenciales.

Cierre de sesión

Cuando el usuario pulsa el botón LOG OUT del ProfileView, se ejecuta el LogoutCommand de la clase ProfileViewModel, que a su vez ejecuta el método LogoutAsync. Este método realiza la navegación por páginas hasta la página LoginView, pasando una instancia de LogoutParameter establecida en true como parámetro. Para obtener más información sobre cómo pasar parámetros durante la navegación de la página, vea Pasar parámetros durante la navegación.

Cuando se crea una vista y se navega hasta ella, se ejecuta el método InitializeAsync del modelo de vista asociado de la vista, que luego ejecuta el método Logout de la clase LoginViewModel, que se muestra en el ejemplo de código siguiente:

private void Logout()  
{  
    var authIdToken = Settings.AuthIdToken;  
    var logoutRequest = _identityService.CreateLogoutRequest(authIdToken);  

    if (!string.IsNullOrEmpty(logoutRequest))  
    {  
        // Logout  
        LoginUrl = logoutRequest;  
    }  
    ...  
}

Este método invoca el método CreateLogoutRequest en la clase IdentityService, pasando el token de identidad recuperado de la configuración de la aplicación como parámetro. Para más información sobre la configuración de la aplicación, consulte Administración de configuración. El siguiente ejemplo de código muestra el método CreateLogoutRequest:

public string CreateLogoutRequest(string token)  
{  
    ...  
    return string.Format("{0}?id_token_hint={1}&post_logout_redirect_uri={2}",   
        GlobalSetting.Instance.LogoutEndpoint,  
        token,  
        GlobalSetting.Instance.LogoutCallback);  
}

Este método crea el URI para el punto de conexión de finalización de la sesión de IdentityServer con los parámetros necesarios. El punto de conexión de finalización de la sesión está en /connect/endsession en el puerto 5105 del punto de conexión de base expuesto como una configuración del usuario. Para obtener más información sobre la configuración del usuario, consulte Administración de configuración.

El URI devuelto se almacena en la propiedad LoginUrl de la clase LoginViewModel. Aunque la propiedad IsLogin es true, el WebView del LoginView está visible. Los datos de WebView enlazan su propiedad Source a la propiedad LoginUrl de la clase LoginViewModel, por lo que realiza una solicitud de cierre de sesión a IdentityServer cuando la propiedad LoginUrl está establecida en el punto de conexión de sesión final de IdentityServer. Cuando IdentityServer recibe esta solicitud, siempre que el usuario haya iniciado sesión, se produce el cierre de sesión. Se realiza un seguimiento de la autenticación con una cookie administrada por el middleware de autenticación de cookies desde ASP.NET Core. Por lo tanto, al cerrar la sesión de IdentityServer, se elimina la cookie de autenticación y se envía al cliente un URI de redireccionamiento posterior al cierre de la sesión.

En la aplicación móvil, el WebView se redirigirá al URI de redireccionamiento posterior al cierre de sesión. Esta navegación WebView provocará la ejecución del método NavigateAsync de la clase LoginViewModel, que se muestra en el ejemplo de código siguiente:

private async Task NavigateAsync(string url)  
{  
    ...  
    Settings.AuthAccessToken = string.Empty;  
    Settings.AuthIdToken = string.Empty;  
    IsLogin = false;  
    LoginUrl = _identityService.CreateAuthorizationRequest();  
    ...  
}

Este método borra tanto el token de identidad como el token de acceso de la configuración de la aplicación y establece la propiedad IsLogin en false, lo que hace que el elemento WebView de la página LoginView se vuelva invisible. Por último, la propiedad LoginUrl se establece en el URI del punto de conexión de autorización de IdentityServer, con los parámetros necesarios, como preparación para la próxima vez que el usuario inicie sesión.

Para obtener información sobre la navegación por páginas, consulte Navegación. Para obtener información sobre cómo la navegación de WebView hace que se ejecute un método de modelo de vista, consulte Invocación de navegación mediante comportamientos. Para obtener información sobre la configuración de la aplicación, consulte Administración de configuración.

Nota:

EShopOnContainers también permite un cierre de sesión ficticio cuando la aplicación está configurada para usar servicios ficticios en SettingsView. En este modo, la aplicación no se comunica con IdentityServer y, en su lugar, borra todos los tokens almacenados de la configuración de la aplicación.

Authorization

Después de la autenticación, las API web de ASP.NET Core a menudo necesitan autorizar el acceso, lo que permite que un servicio haga que las API estén disponibles para algunos usuarios autenticados, pero no para todos.

La restricción del acceso a una ruta de ASP.NET Core MVC se puede lograr mediante la aplicación de un atributo Authorize a un controlador o una acción, que limita el acceso al controlador o la acción a los usuarios autenticados, como se muestra en el ejemplo de código siguiente:

[Authorize]  
public class BasketController : Controller  
{  
    ...  
}

Si un usuario no autorizado intenta acceder a un controlador o una acción marcada con el atributo Authorize, el marco de MVC devuelve un código de estado HTTP 401 (no autorizado).

Nota:

Los parámetros se pueden especificar en el atributo Authorize para restringir una API a usuarios específicos. Para más información, consulte Autorización.

IdentityServer se puede integrar en el flujo de trabajo de autorización para que los tokens de acceso que proporciona controlen la autorización. Este enfoque se muestra en la figura 9-5.

Autorización por token de acceso

Figura 9-5: Autorización por token de acceso

La aplicación móvil eShopOnContainers se comunica con el microservicio de identidad y solicita un token de acceso como parte del proceso de autenticación. A continuación, el token de acceso se reenvía a las API expuestas por los microservicios de pedidos y cesta de la compra como parte de las solicitudes de acceso. Los tokens de acceso contienen información sobre el cliente y el usuario. A continuación, las API usan esa información para autorizar el acceso a sus datos. Para obtener información sobre cómo configurar IdentityServer para proteger las API, consulte Configuración de recursos de API.

Configuración de IdentityServer para realizar la autorización

Para realizar la autorización con IdentityServer, se debe agregar su middleware de autorización a la canalización de solicitudes HTTP de la aplicación web. El middleware se agrega en el método ConfigureAuth de la clase Startup de la aplicación web, que se invoca desde el método Configure y se muestra en el ejemplo de código siguiente de la aplicación de referencia eShopOnContainers:

protected virtual void ConfigureAuth(IApplicationBuilder app)  
{  
    var identityUrl = Configuration.GetValue<string>("IdentityUrl");  
    app.UseIdentityServerAuthentication(new IdentityServerAuthenticationOptions  
    {  
        Authority = identityUrl.ToString(),  
        ScopeName = "basket",  
        RequireHttpsMetadata = false  
    });  
} 

Este método garantiza que solo se pueda acceder a la API con un token de acceso válido. El middleware valida el token entrante para garantizar que se envía desde un emisor de confianza y valida que el token es válido para usarse con la API que lo recibe. Por lo tanto, la navegación al controlador de pedidos o de la cesta devolverá un código de estado HTTP 401 (no autorizado), indicando que se requiere un token de acceso.

Nota:

Se debe agregar el middleware de autorización de IdentityServer a la canalización de solicitudes HTTP de la aplicación web antes de agregar MVC con app.UseMvc() o app.UseMvcWithDefaultRoute().

Realización de solicitudes de acceso a las API

Al realizar solicitudes a los microservicios de pedidos y de cesta de la compra, el token de acceso obtenido de IdentityServer durante el proceso de autenticación debe incluirse en la solicitud, como se muestra en el ejemplo de código siguiente:

var authToken = Settings.AuthAccessToken;  
Order = await _ordersService.GetOrderAsync(Convert.ToInt32(order.OrderNumber), authToken);

El token de acceso se almacena como una configuración de la aplicación, se recupera del almacenamiento específico de la plataforma y se incluye en la llamada al método GetOrderAsync de la clase OrderService.

Del mismo modo, el token de acceso debe incluirse al enviar datos a una API protegida de IdentityServer, como se muestra en el ejemplo de código siguiente:

var authToken = Settings.AuthAccessToken;  
await _basketService.UpdateBasketAsync(new CustomerBasket  
{  
    BuyerId = userInfo.UserId,   
    Items = BasketItems.ToList()  
}, authToken);

El token de acceso se recupera del almacenamiento específico de la plataforma y se incluye en la llamada al método UpdateBasketAsync de la clase BasketService.

La clase RequestProvider de la aplicación móvil eShopOnContainers usa la clase HttpClient para realizar solicitudes a las API de RESTful expuestas por la aplicación de referencia eShopOnContainers. Al realizar solicitudes a las API de pedidos y cesta de la compra, que requieren autorización, se debe incluir un token de acceso válido con la solicitud. Esto se logra agregando el token de acceso a los encabezados de la instancia HttpClient, como se muestra en el ejemplo de código siguiente:

httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", token);

La propiedad DefaultRequestHeaders de la clase HttpClient expone los encabezados que se envían con cada solicitud, y el token de acceso se agrega al encabezado Authorization cuyo prefijo es la cadena Bearer. Cuando la solicitud se envía a una API de RESTful, el valor del encabezado Authorization se extrae y se valida para garantizar que se envía desde un emisor de confianza y se usa para determinar si el usuario tiene permiso para invocar la API que lo recibe.

Para obtener más información sobre cómo la aplicación móvil eShopOnContainers realiza solicitudes web, consulte Acceso a datos remotos.

Resumen

Hay muchos enfoques para integrar la autenticación y la autorización en una aplicación de Xamarin.Forms que se comunica con una aplicación web de ASP.NET MVC. La aplicación móvil eShopOnContainers realiza la autenticación y la autorización con un microservicio de identidad en contenedor que usa IdentityServer 4. IdentityServer es un marco de código abierto de OpenID Connect y OAuth 2.0 para ASP.NET Core que se integra con ASP.NET Core Identity para realizar la autenticación de tokens de portador.

La aplicación móvil solicita tokens de seguridad de IdentityServer, ya sea para autenticar a un usuario o para acceder a un recurso. Al acceder a un recurso, se debe incluir un token de acceso en la solicitud a las API que requieren autorización. El middleware de IdentityServer valida los tokens de acceso entrantes para garantizar que se envían desde un emisor de confianza y que son válidos para usarlos con la API que los recibe.