¿Eres un desarrollador .NET que siempre ha querido hacer una aplicación móvil? O tal vez has intentado construir aplicaciones móviles nativas con Android o iOS pero no te han gustado los lenguajes? Pues entonces, ¡estás de suerte! El mundo .NET ha sido bendecido con Xamarin; un conjunto de herramientas que le permite crear aplicaciones móviles para Android, iOS y Windows dentro de Visual Studio.
Xamarin tiene dos sabores principales: La plataforma Xamarin (Xamarin.iOS y Xamarin.Android) y Xamarin.Forms. Con Xamarin.Forms se puede escribir la mayor parte de la lógica de negocio y la interfaz de usuario en un proyecto compartido que produce aplicaciones totalmente funcionales en los tres sistemas operativos iOS, Android y Windows (UWP). La plataforma Xamarin, por otro lado, es un trabajo muy específico de la plataforma y es más parecido a escribir aplicaciones nativas pero con C#.
En este tutorial, voy a ver más de cerca la plataforma Xamarin y el conjunto de herramientas del sistema operativo Android conocido como Xamarin.Android. El objetivo general es permitirte crear una sencilla aplicación nativa de Android con autenticación de usuario básica incluida.
- Configuración de Visual Studio y su entorno
- Verifica tu entorno Android en Visual Studio
- Cree una aplicación de Xamarin
- Familiarízate con el proyecto Xamarin
- Agregar autenticación de usuarios a Xamarin con OpenID Connect
- Configura tu aplicación de Okta
- Crear el proveedor de autenticación
- Implementa la autenticación en tu interfaz de Xamarin
- Ejecuta tu aplicación Android
- Aprende más sobre Xamarin, OpenID Connect y la autenticación segura
Configuración de Visual Studio y su entorno
Para seguir adelante necesitarás una copia de Visual Studio, además de la carga de trabajo «Desarrollo móvil con .NET». Puedes activar esta función desde la primera instalación de Visual Studio o acceder a ella desde la opción de menú ‘Herramientas -> Obtener herramientas y características…’:
Cuando pruebes y ejecutes tu aplicación tienes la opción de hacerlo con un emulador de Android que se ejecute en tu máquina de desarrollo o conectándote directamente a un dispositivo Android existente. No hay una opción correcta aquí y diferentes desarrolladores prefieren diferentes factores de forma. Si eliges la primera opción, tendrás que asegurarte, una vez seleccionada la carga de trabajo, de que en el panel de la derecha (‘Detalles de la instalación’) están seleccionadas las casillas de verificación de Intel Hardware Accelerated Execution Manager y Google Android Emulator (como se ve arriba).
Verifica tu entorno Android en Visual Studio
Para verificar que todo se ha instalado correctamente y se ha configurado correctamente, ve a ‘Herramientas -> Opciones -> Xamarin -> Configuración de Android’ y comprueba que las rutas de ubicación del kit de desarrollo de Java y del SDK de Android son válidas (es decir, que tienen una marca verde).es decir, que tengan una marca verde):
Si falta alguna de ellas, tendrá que instalar manualmente el Kit de desarrollo de Java o el SDK de Android, respectivamente.
Cree una aplicación de Xamarin
Comience creando un nuevo proyecto y seleccione la plantilla maestra «Aplicación de Android (Xamarin)» (que se encuentra en el menú Android). En la siguiente página querrás elegir la opción ‘Single View App’, ya que es una gran plantilla de inicio para trabajar.
Con respecto a la versión mínima de Android, esto es algo que depende de tu elección personal como desarrollador. Hay un compromiso aquí entre ser capaz de acceder a las últimas y mejores características de la API en las versiones más recientes y apoyar a sus clientes que tienen versiones anteriores. Para ayudarte a tomar esta decisión, Google publica los datos de distribución de la versión de la plataforma que recopila como parte de su panel de distribución con bastante regularidad. Mi preferencia personal es entre la 5.0 o la 6.0, dependiendo de si se trata de una aplicación para consumo público o de una aplicación encargada sólo para teléfonos corporativos (es decir, que probablemente tendrán las últimas actualizaciones); en este ejemplo he optado por esta última. Tenga en cuenta que esta versión difiere de la versión de Android de destino y que siempre debe establecerse en la última versión del SDK liberado, ya que cualquier cosa menos no será aceptada en la tienda de Google Play.
Una vez que haya creado todo lo que queda es importar los paquetes NuGet necesarios. Para este tutorial necesitarás:
- Xamarin.OpenId.AppAuth.Android – Para autenticar al usuario se utilizará el estándar OpenID Connect (una mejora de OAuth 2.0). La forma más fácil de implementar el código del cliente que cumple con esta especificación es utilizando el SDK del cliente AppAuth para Android, y afortunadamente Xamarin ha portado un paquete de esta funcionalidad disponible para su uso.
- System.IdentityModel.Tokens.Jwt – El método de autenticación aquí utiliza JSON Web Tokens. Para extraer los datos necesarios de estos tokens, necesitará este paquete.
Además, necesitará estos dos paquetes, ya que AppAuth depende de ellos:
- Xamarin.Android.Support.v4
- Xamarin.Android.Support.CustomTabs
Familiarízate con el proyecto Xamarin
Si nunca has trabajado con Android, uno de los principios clave que debes entender es el concepto de Actividad. Las actividades son componentes utilizados para mostrar tu interfaz de usuario; en su forma más básica, puedes pensar en las Actividades como si fueran iguales a las páginas de una aplicación entre las que el usuario puede navegar. Una Actividad en el código está representada por una clase, sin embargo, al igual que una página en ASP.NET puede (y casi siempre lo hará) asociar un archivo de diseño basado en XML (un archivo .axml) con ella para un diseño visualizable. Todos los nuevos proyectos crean un archivo ‘MainActivity.cs’ y ‘activity_main.axml’ para comenzar con la primera Actividad (es decir, la página) que se ejecuta al abrir la aplicación. Esto se puede cambiar a cualquier otra actividad mediante la utilización de la propiedad MainLauncher = true
dentro del atributo Activity
de la clase.
Los recursos están diseñados para ser manejados dentro de su propio directorio y seguir una convención de nomenclatura algo estricta. Yo recomendaría encarecidamente el almacenamiento de la mayor cantidad de sus recursos como es factible en este directorio, ya que simplifica la reutilización de estas variables para su desarrollo en curso. En el directorio ‘values’ del directorio Resources es donde encontrarás archivos con propósitos específicos:
- Strings.xml – Alberga todas las cadenas de cara al usuario. Este es especialmente importante ya que le permite localizar sus cadenas para una audiencia global.
- Styles.xml – Donde encontrará los atributos para estilizar sus objetos de diseño; piense en ello como un archivo CSS.
- Colors.xml – Un lugar para almacenar las referencias a los colores que utiliza con más frecuencia como parte de su estilo.
- Dimens.xml – Como el nombre podría implicar, donde se definen las dimensiones del diseño de su aplicación.
Los otros directorios notables no siguen ninguna convención de nomenclatura para sus archivos, pero deben contener ciertos tipos de archivos:
- Layout – Ubicación para almacenar sus archivos .axml. Estos archivos definen los diseños completos de la Actividad, los componentes de diseño que usted genera y rellena mediante programación, los diseños de los cuadros de diálogo (alerta), etc.
- Menú – Donde encontrará las definiciones de los menús y sus elementos. Son archivos .xml que tienen
menu
como elemento raíz yitem
elementos hijos, de los cuales se pueden agrupar con elementosgroup
. Los menús más comunes que encontrarás son el menú de desbordamiento (desde el botón de los tres puntos verticales) o el menú de navegación (desde el botón ‘hamburguesa’ de inicio). - Mipmap – Donde quieres definir las imágenes que necesitan ser escaladas dependiendo de la densidad de la pantalla, es decir, aquellas referenciadas por otras apps, y que no se usan internamente. El icono de la aplicación es el contenido más común que usted pondría en los directorios mipmap.
- Drawable – No es estándar en una plantilla de proyecto por defecto, pero puede ser creado por usted mismo. Aquí es donde almacena sus imágenes / contenido dibujable que se utilizará dentro de la aplicación, por ejemplo, la pantalla de inicio, los diseños personalizados de la barra de progreso, los iconos utilizados dentro de la aplicación, etc.
Por último, si usted tiene cualquier archivo de contenido en bruto que desea utilizar como parte de su aplicación (por ejemplo, un archivo de texto o de la fuente), entonces el directorio Assets es donde colocarlos. Como activos, estos archivos se implementarán con la aplicación para que pueda acceder a ellos con el administrador de activos.
Para obtener más información sobre los activos y los recursos y cómo utilizarlos, hay prácticos archivos de texto «Acerca de» en cada directorio de un proyecto recién creado.
Agregar autenticación de usuarios a Xamarin con OpenID Connect
La mayoría de las aplicaciones de hoy en día requieren alguna forma de identificación del usuario para que el desarrollador pueda ofrecer experiencias personalizadas y, a su vez, permitir que el usuario mantenga sus datos en todos los dispositivos/instalaciones. Además de esto, está la cuestión del control de acceso que podría ser útil para autorizar a un subconjunto de usuarios para una funcionalidad extra. Naturalmente, esto puede ser una tarea bastante laboriosa, especialmente si te dedicas a crear aplicaciones y necesitas un nuevo sistema para cada una de ellas. Afortunadamente, con Okta puedes configurar una aplicación en cuestión de minutos y luego todo el trabajo duro se hace por ti. El servicio de Okta es compatible con OAuth 2.0 y es un proveedor certificado de OpenID Connect, por lo que funciona perfectamente con el SDK de cliente de AppAuth para todas tus necesidades de autenticación y autorización.
Configura tu aplicación de Okta
En primer lugar, debes configurar una nueva aplicación en tu cuenta de Okta para este proyecto. Si aún no tienes una, es muy fácil crear una nueva cuenta de desarrollador libre para siempre.
Una vez que esto se haya completado y hayas iniciado sesión en el panel de control del desarrollador, toma nota de la URL Org, ya que la necesitaremos más adelante:
Desde el panel de control ve a la pestaña ‘Aplicaciones’ y desde allí ‘Añadir aplicación’. Estás creando una aplicación nativa para Android, así que es mejor elegir la plantilla de plataforma ‘Nativa’.
Desde esta página añade un nombre para tu aplicación y deja todo lo demás por defecto. Una vez guardado toma nota de tus URIs de redirección de inicio de sesión y el ID de cliente, ya que los necesitarás a continuación.
Para que puedas utilizar estos tres valores de Okta con facilidad en el futuro, te recomendaría ponerlos en su propia clase estática dentro de un nuevo directorio para la lógica de autenticación:
public static class Configuration{ public const string ClientId = "{yourClientId}"; public const string LoginRedirectUri = "{yourRedirectUri}"; public const string OrgUrl = "https://{yourOktaDomain}";}
Crear el proveedor de autenticación
Saquemos el código de caldera del camino. Tienes que configurar la aplicación para informarle de tu esquema URI de redirección. El esquema es su URI de redirección de inicio de sesión (en forma de nombre de dominio inverso estándar) sin la ruta. Por ejemplo, desde la captura de pantalla anterior mi esquema sería ‘com.oktapreview.dev-123456’.
La forma más fácil de hacer esto es insertar el siguiente fragmento de filtro de intención en su archivo AndroidManifest.xml
en la carpeta Properties
de su solución. Añade el siguiente XML dentro de la etiqueta Application
y cambia el valor del esquema por el tuyo:
<activity android:name="net.openid.appauth.RedirectUriReceiverActivity"> <intent-filter> <action android:name="android.intent.action.VIEW" /> <category android:name="android.intent.category.DEFAULT" /> <category android:name="android.intent.category.BROWSABLE" /> <data android:scheme="{yourRedirectRriScheme}" /> </intent-filter></activity>
También tienes que definir el modelo para el resultado de tu autorización con una clase simple. Aunque no voy a utilizar todos los valores que he escrito a continuación dentro del código, mostraré cómo rellenarlos para que los utilices después. Como esto es parte del modelo de tu aplicación, crea una carpeta llamada Models
y añade una clase AuthorizationResult.cs
dentro de ella. A continuación, añade el siguiente código:
public class AuthorizationResult{ public string AccessToken { get; set; } public string IdentityToken { get; set; } public bool IsAuthorized { get; set; } public string RefreshToken { get; set; } public string Scope { get; set; }}
Android no tiene un estado global para que trabajes con él, por lo que si quieres pasar valores simples entre actividades, la mejor manera de hacerlo es con la funcionalidad Extras
en el objeto Intent
. Un Intent
es una clase predefinida en Android y otro concepto básico a entender. Es la abstracción de una operación a realizar (es decir, tus ‘intenciones’), y para navegar hacia adelante a otra actividad necesitas crear una instancia de esta clase con la actividad a la que ‘pretendes’ ir. Las propiedades Extras en el objeto Intent
son, en efecto, sólo un diccionario de claves a los valores de los objetos y se accede a ellos por Put
y métodos de tipo Get
.
Aunque estos métodos mantienen el uso relativamente claro y fácil, personalmente me gusta mantener todo el acceso a ellos dentro de su propia clase (para ser precisos, una clase de extensiones), para mantener una mejor separación de preocupaciones. Esto es extremadamente útil ya que no necesitas acceder a las claves a través de las clases y puedes asegurar la seguridad de tipo al poner y obtener estos valores. En tu proveedor de autorización querrás: almacenar el AuthState
, ser capaz de comprobar si está ahí, y devolverlo si lo está. Crea una nueva carpeta llamada Extensions
en la raíz de la solución. A continuación, añadir una nueva clase llamada IntentExtensions.cs
. Haz la clase public
y static
, luego añade el siguiente código dentro de la clase:
public const string AuthStateKey = "authState";public static string GetAuthStateExtra(this Intent intent){ return intent.GetStringExtra(AuthStateKey);}public static bool HasAuthStateExtra(this Intent intent){ return intent.HasExtra(AuthStateKey);}public static void PutAuthStateExtra(this Intent intent, AuthState authState){ intent.PutExtra(AuthStateKey, authState.JsonSerializeString());}public static bool TryGetAuthStateFromExtra(this Intent intent, out AuthState authState){ authState = null; if (!intent.HasAuthStateExtra()) { return false; } try { authState = AuthState.JsonDeserialize(intent.GetAuthStateExtra()); } catch (JSONException) { return false; } return authState != null;}
Ahora es el momento de definir el proveedor de autorización, AuthorizationProvider.cs
en la carpeta Authentication
que creaste antes para la clase Configuration.cs
. En primer lugar, eliminar todas las declaraciones using
dentro de la clase recién creada, a continuación, declarar la configuración como static readonly
variables:
private static readonly Uri ConfigurationEndpoint = Uri.Parse($"{Configuration.OrgUrl}/oauth2/default/.well-known/openid-configuration");private static readonly string Scopes = new { "openid", "profile", "email", "offline_access" };
El punto final de configuración es un estándar en OpenID como el punto final de descubrimiento para encontrar todo lo que es compatible. Tenga en cuenta aquí he escrito esto es utilizar el nombre del proveedor ‘por defecto’. Si usted tiene un proveedor diferente, usted querrá cambiar esto aquí. También ten en cuenta que esto está utilizando el sabor Android.Net
de la clase Uri, y no la versión System
– tendrás que añadir la primera a tus usos o calificar completamente el tipo para que esto funcione. La variable Scopes, como cualquier otro sistema OpenID, define a qué estamos autorizados a acceder.
A continuación debes declarar tus variables miembro:
private AuthorizationRequest authorizationRequest;private PendingIntent completedIntent;private AuthState authorizationState;private readonly AuthorizationService authorizationService;private readonly Context context;private readonly TaskCompletionSource<bool> taskCompletionSource = new TaskCompletionSource<bool>();
Una rápida explicación sobre cada una:
- La solicitud de autorización y la intención completada son parámetros creados para ser usados al hacer la llamada de autorización. Los he escrito como variables globales aquí para minimizar la cantidad de parámetros de paso en diferentes métodos.
- La variable authorizationState como se llama define el estado de autorización actual dado.
- La variable authorizationService contiene una instancia del servicio de autorización.
- La variable context aquí es de la actividad de llamada, por lo que puede hacer referencia a ella cuando sea necesario.
- Por último, el taskCompletionSource permite realizar todas estas llamadas de forma asíncrona y devolverlas una vez completadas.
Ahora debes definir los valores de estas variables de sólo lectura en tu constructor, y declarar los métodos públicos a los que llamará tu código:
public AuthorizationProvider(Context context){ authorizationService = new AuthorizationService(context); this.context = context;}public async Task<AuthorizationResult> SignInAsync(){ ...}public void NotifyCallback(Intent intent){ ...}
El método SignInAsync es, como habrás adivinado, un método asíncrono para registrar a un usuario. Esto devuelve la clase AuthorizationResult
que escribiste antes. El método NotifyCallback
por otro lado es para que la actividad que llama, una vez que ha regresado de la página externa de inicio de sesión, llame de nuevo al proveedor de autorización y le haga saber que ha terminado. El método de inicio de sesión lo he dividido en varias subrutinas, y tiene el siguiente aspecto:
AuthorizationServiceConfiguration serviceConfiguration = await AuthorizationServiceConfiguration.FetchFromUrlAsync(ConfigurationEndpoint);BuildAuthorizationRequest(serviceConfiguration);BuildCompletedIntent(serviceConfiguration);return await RequestAuthorization();
En esto se ha definido la configuración del servicio, se ha construido la solicitud de autorización y la intención de llamar una vez que se ha completado la autorización, y luego se espera la solicitud de autorización. Para construir la solicitud de autorización es como sigue:
private void BuildAuthorizationRequest(AuthorizationServiceConfiguration serviceConfiguration){ AuthorizationRequest.Builder builder = new AuthorizationRequest.Builder( serviceConfiguration, Configuration.ClientId, ResponseTypeValues.Code, Uri.Parse(Configuration.LoginRedirectUri)); builder.SetScope(string.Join(" ", Scopes)); authorizationRequest = builder.Build();}
El trabajo de este método es abstraer el trabajo de AuthorizationRequest.Builder
y crear la solicitud. A continuación hay que construir el Intent
para una vez que la operación se ha completado:
private void BuildCompletedIntent(AuthorizationServiceConfiguration serviceConfiguration){ Intent intent = new Intent(context, typeof(MainActivity)); intent.PutAuthStateExtra(new AuthState()); completedIntent = PendingIntent.GetActivity(context, authorizationRequest.GetHashCode(), intent, 0);}
La «intención» que se quiere realizar aquí es volver a su MainActivity
con un nuevo AuthState adjunto. Por último en este flujo es tratar con la ejecución de la solicitud:
private async Task<AuthorizationResult> RequestAuthorization(){ authorizationService.PerformAuthorizationRequest(authorizationRequest, completedIntent); await taskCompletionSource.Task; return new AuthorizationResult() { AccessToken = authorizationState?.AccessToken, IdentityToken = authorizationState?.IdToken, IsAuthorized = authorizationState?.IsAuthorized ?? false, RefreshToken = authorizationState?.RefreshToken, Scope = authorizationState?.Scope };}
Como PerformAuthorizationRequest
es síncrono y devuelve void, el código espera sobre el miembro taskCompletionSource
, sabiendo que sólo se establecerá una vez que se haya recuperado una respuesta. En este mismo punto sabes que el estado de autorización será rellenado (si todo ha tenido éxito), y por lo tanto puedes devolver sus valores como parte del AuthorizationResult.
El segundo método público NotifyCallback
, como he mencionado antes, es lo que quieres que la clase MainActivity
devuelva la llamada, una vez que tu completedIntent
anterior se ejecute. En este método quieres verificar la respuesta, actualizar el estado apropiadamente, y si tiene éxito, realizar una solicitud de intercambio de tokens:
if (!intent.TryGetAuthStateFromExtra(out authorizationState)){ taskCompletionSource.SetResult(false); return;}AuthorizationResponse authorizationResponse = AuthorizationResponse.FromIntent(intent);AuthorizationException authorizationException = AuthorizationException.FromIntent(intent);authorizationState.Update(authorizationResponse, authorizationException);if (authorizationException != null){ taskCompletionSource.SetResult(false); return;}authorizationService.PerformTokenRequest(authorizationResponse.CreateTokenExchangeRequest(), ReceivedTokenResponse);
Aquí puedes ver que en los casos de fallo he puesto el resultado de taskCompletionSource a false, y eso desbloqueará el método RequestAuthorization
de arriba. Además, el método PerformTokenRequest
toma un delegado, ReceivedTokenResponse
, para ser ejecutado una vez que haya completado. Este método es el siguiente:
private void ReceivedTokenResponse(TokenResponse tokenResponse, AuthorizationException authorizationException){ authorizationState.Update(tokenResponse, authorizationException); taskCompletionSource.SetResult(true);}
En este punto deberías tener todos los datos de autorización que necesitas, y así puedes actualizar el estado apropiadamente (donde encontrarás los valores a devolver del método sign in) y establecer el resultado para desbloquear la tarea taskCompletionSource
.
Implementa la autenticación en tu interfaz de Xamarin
Como limpieza, si lo deseas, no dudes en eliminar todas las referencias a la «barra de acción flotante» (también escrita como «FAB») dentro de los archivos de clase/axml de la actividad principal, ya que son una carga innecesaria en esta fase.
Para permitir que el usuario inicie sesión, ahora necesitas implementar esta funcionalidad en la interfaz de usuario. Dado el diseño por defecto, la forma más intuitiva de hacer esto sería añadir un elemento al menú de desbordamiento en la esquina superior derecha. Puedes hacerlo editando el archivo menu_main.xml
en la carpeta ‘Resources -> menu’, y añadiendo este elemento como hijo de la etiqueta menu
:
<item android:id="@+id/action_signin" android:orderInCategory="100" android:title="@string/action_signin" app:showAsAction="never" />
Con este código has creado una nueva opción con un título que se definirá en el archivo de recursos de cadena. Como se mencionó antes en Android es la mejor práctica para poner todo el texto de cara al usuario en el archivo de recursos de cadena. Para declarar estos datos, edita el archivo strings.xml
en la carpeta ‘Resources -> values’ y añade estas líneas:
<string name="action_signin">Sign In</string><string name="welcome_message_format">Hi, %1$s!</string>
Aquí no sólo he declarado una cadena para el botón ‘Sign In’, sino que también he añadido arriba una cadena para un mensaje de bienvenida al usuario una vez que haya iniciado sesión. El equivalente al código C# de this string would be
«¡Hola, {0}!»`, donde el marcador de posición es de tipo string.
Nota que con todas las actualizaciones de estos archivos basados en Recursos, la clase Resource.designer.cs se regenerará automáticamente con nuevos IDs para cada objeto que hayas creado, que pueden ser referenciados dentro de tu código. Si esto no funciona para un archivo en particular, entonces selecciónelo en el Explorador de Soluciones y mire la ventana de Propiedades. Asegúrese de que la propiedad CustomTool
se establece en el valor MSBuild:UpdateGeneratedFiles
, ya que esto es probable que falta y la prevención del archivo de diseño de reconocerlo como un recurso.
A continuación, añadir un ProgressBar
al archivo de diseño existente activity_main.axml
:
<ProgressBar android:id="@+id/signin_progress" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_gravity="center" android:paddingBottom="12dp" android:visibility="gone" />
Este ProgressBar
(o spinner, como es el caso), tiene un ID que puede hacer referencia con el código, y se establece para sentarse alrededor del centro de la pantalla. La visibilidad está configurada como desaparecida por ahora, pero una vez que tu código de autorización se esté ejecutando puedes configurarlo como visible e informar al usuario de que la aplicación está ocupada.
Ahora que tienes un botón para abrir la autenticación y un spinner de progreso para informar al usuario de que la aplicación está ocupada, es el momento de utilizarlos. Dentro de tu clase MainActivity
añade la siguiente propiedad al atributo Activity
(encima de la cabecera de la clase):
LaunchMode = LaunchMode.SingleTask
Esta propiedad asegura que sólo haya una instancia de la clase MainActivity
, y no sigas abriendo otras nuevas. Una vez que hayas hecho esto, añade una variable miembro estática para el AuthorizationProvder
que escribiste arriba y crea una instancia de él dentro del override existente del método OnCreate
. Tenga en cuenta que esto debe hacerse después del código existente dentro del método:
private static AuthorizationProvider authorizationProvider;protected override void OnCreate(Bundle savedInstanceState){ ... authorizationProvider = new AuthorizationProvider(this);}
A continuación, anule el método OnNewIntent
. El propósito de esto es cuando se crea una nueva intención de esta clase (es decir, cuando el signo externo en la ventana vuelve), se llama el método NotifyCallback
de la AuthorizationProvider
. También se incluye en esto una comprobación rápida para asegurarse de que es el flujo esperado:
protected override void OnNewIntent(Intent intent){ base.OnNewIntent(intent); if (intent != null && intent.Data.Path.Equals("/callback", StringComparison.OrdinalIgnoreCase)) { authorizationProvider.NotifyCallback(intent); }}
Ahora añade el código detrás del elemento de menú que has añadido. En el override existente del método OnOptionsItemSelected
, añade una sentencia if
con una llamada a un nuevo método que se encargará del proceso de sign in de la siguiente manera:
if (id == Resource.Id.action_signin){ OnSignInAttempted(); return true;}
Este nuevo método empezará haciendo visible el ProgressBar
que has añadido hace unos momentos; para recuperar cualquier componente del archivo de diseño, utiliza el método genérico FindViewById
e introduce el ID del componente como argumento. Después de esto, haz una llamada al método SignInAsync
y espera su resultado. Una vez devuelta la llamada, se verifica que el resultado esté autorizado. Si esta autorización ha fallado por cualquier motivo, aparece un diálogo de error, y el spinner de progreso desaparece de nuevo. Dejaré de detallar el caso de éxito por ahora ya que todavía necesitas un lugar al que ir en esa instancia:
private async void OnSignInAttempted(){ ProgressBar signInProgress = FindViewById<ProgressBar>(Resource.Id.signin_progress); signInProgress.Visibility = ViewStates.Visible; AuthorizationResult authorizationResult = await authorizationProvider.SignInAsync(); if (!string.IsNullOrWhiteSpace(authorizationResult.AccessToken) && authorizationResult.IsAuthorized) { // Placeholder: Success case } else { // Display an error to the user AlertDialog authorizationErrorDialog = new AlertDialog.Builder(this) .SetTitle("Error!") .SetMessage("We were unable to authorize you.") .Create(); authorizationErrorDialog.Show(); signInProgress.Visibility = ViewStates.Gone; }}
Cuando el usuario se autentifica debes redirigirlo a la siguiente página de tu experiencia. Si recuerdas antes cada página está representada por una Actividad, por lo que ahora necesitas crear una nueva.
Para empezar, dentro de la carpeta ‘Resources -> layout’ tendrás que crear el nuevo archivo de layout de la actividad «activity_dashboard.axml». La forma más fácil de hacerlo es yendo a la opción Nuevo elemento… del menú contextual y seleccionando la plantilla ‘Android Layout’. Dentro de tu nuevo archivo de diseño añade un componente TextView simple para mostrar un texto como este:
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android" android:orientation="vertical" android:layout_width="match_parent" android:layout_height="match_parent" android:gravity="center"> <TextView android:id="@+id/welcome_message" android:textAppearance="?android:attr/textAppearanceMedium" android:layout_width="wrap_content" android:layout_height="wrap_content" android:layout_centerInParent="true" /></LinearLayout>
En este fragmento tienes un componente TextView
con un ID referenciable que está centrado en el centro de la página, del cual se mostrará un mensaje de bienvenida. A continuación creamos la correspondiente clase de actividad ‘DashboardActivity’ mediante la opción ‘New Item…’ del menú contextual del proyecto en el explorador de soluciones y seleccionando la plantilla ‘Activity’. Para enlazar esta clase con su archivo de diseño, es necesario llamar a la función SetContentView en el método OnCreate()
generado (bajo la invocación del método base heredado):
SetContentView(Resource.Layout.activity_dashboard);
Para personalizar su mensaje de bienvenida, querrá pasar el nombre del usuario a su nueva actividad. Si recuerdas antes la mejor manera de hacer esto era con Extras en la intención, y creaste una clase de extensiones para manejar esto. Como antes, añade nuevos métodos para ‘Put’ y ‘Get’ de un extra ‘name’ en el archivo IntentExtensions.cs
que creaste antes:
private const string NameKey = "Name";public static void PutNameExtra(this Intent intent, string name){ intent.PutExtra(NameKey, name);}public static string GetNameExtra(this Intent intent){ return intent.GetStringExtra(NameKey);}
Ahora usando esta funcionalidad extendida, después de la llamada a SetContentView
que hiciste en el método OnCreate()
, recupera el nombre del usuario y establece el texto del componente TextView
apropiadamente:
string name = Intent.GetNameExtra();TextView welcomeMessage = FindViewById<TextView>(Resource.Id.welcome_message);welcomeMessage.Text = Resources.GetString(Resource.String.welcome_message_format, name);
En este extracto, al recuperar la instancia TextView
se establece su valor a su mensaje de bienvenida, del cual se crea usando el equivalente de Android Resources de string.Format()
.
Con esto tu actividad de tablero está completa, y ahora necesitas llamarla. En el marcador de posición para el caso de éxito que dejé abierto desde el método OnSignInAttempted
, puedes conseguirlo añadiendo el siguiente código:
JwtSecurityTokenHandler tokenHandler = new JwtSecurityTokenHandler();JwtSecurityToken jsonToken = tokenHandler.ReadJwtToken(authorizationResult.IdentityToken);IEnumerable<Claim> claims = jsonToken?.Payload?.Claims;string name = claims?.FirstOrDefault(x => x.Type == "name")?.Value;Intent dashboardIntent = new Intent(this, typeof(DashboardActivity));dashboardIntent.PutNameExtra(name);StartActivity(dashboardIntent);Finish();
El primer bloque lee el token y recupera el nombre del usuario (si existe). En el segundo se crea un nuevo Intent
para la actividad del tablero, se almacena el nombre del usuario en este Intent
utilizando su método de extensión definido anteriormente, y luego se inicia la actividad (es decir, se navega a ella). Para evitar que el usuario vuelva a navegar a esta página después, el código termina llamando al método Finish()
.
Ejecuta tu aplicación Android
Ahora es el momento de lanzar tu aplicación usando el dispositivo que hayas elegido
Si estás depurando usando el emulador esto debería ser tan simple como pulsar F5 de que primero se abrirá y cargará el emulador, y luego el código se desplegará en él. Como nota al margen, no es necesario cerrar el emulador entre los intentos de ejecución / depuración, ya que sólo tiene que volver a desplegar la aplicación.
Si estás depurando usando un dispositivo que no se ha utilizado para este propósito antes, tendrás que configurar el dispositivo primero. Para ello, activa las opciones de desarrollador y, dentro del nuevo menú, activa la «depuración de Android» en el apartado «Depuración». Después de esto, sólo tienes que conectar el dispositivo, aceptar el diálogo en tu dispositivo confirmando que se trata de una conexión de depuración segura, y deberías ser capaz de desplegar y ejecutar tu aplicación con F5. Ten en cuenta que los dispositivos físicos tienen mayor prioridad que el emulador y cambiarán a éste como opción de depuración por defecto cuando se conecten.
Una vez que tu aplicación se haya desplegado y cargado, serás recibido por la plantilla de página única por defecto. Abre el menú de la esquina superior derecha para iniciar sesión y, una vez que hayas introducido tus datos, deberías volver a esta página con la barra de progreso giratoria antes de que se te envíe automáticamente a la página del panel con tu mensaje de bienvenida:
Aprende más sobre Xamarin, OpenID Connect y la autenticación segura
Si has seguido todos estos pasos, ya tienes una aplicación básica de Android creada con Xamarin.Android, con una autenticación de usuario totalmente funcional basada en OpenID y el servicio Okta. A partir de aquí puedes ampliar fácilmente la actividad del panel de control para implementar tu funcionalidad.
Para ver el código de este post en su totalidad dirígete a nuestra página de GitHub.
Si este tutorial le ha abierto el apetito por el desarrollo en Xamarin y le gustaría aprender más, le sugiero encarecidamente que eche un vistazo a estos otros excelentes artículos:
- Añada la gestión de identidades a su aplicación de Android
- Añada la autenticación a su aplicación de Xamarin.Forms con OpenID Connect
- Construya una aplicación para iOS y Android con Xamarin
.