Advertisement
  1. Code
  2. Windows Phone

Añadiendo autenticación usando Azure Mobile Services

Scroll to top

Spanish (Español) translation by Ana Paulina Figueroa (you can also view the original English article)

Azure Mobile Services te permite autenticar usuarios desde tus aplicaciones de Universal Windows. En este tutorial aprenderás cómo hacer lo siguiente:

  • Agregar autenticación a una aplicación Windows Phone 8.1 WinRT usando diferentes proveedores de identidad soportados por Azure Mobile Services
  • Almacenar en caché los tokens de autenticación en el cliente
  • Obtener y almacenar detalles de perfiles de usuario en una base de datos de Azure usando secuencias de comandos en el servidor
  • Añadir ámbitos de autenticación adicionales para obtener más información del usuario desde el servidor
  • Editar la secuencia de comandos de inserción de UsersTable para insertar un registro si no existe un usuario con el mismo userId, y de lo contrario actualizar al usuario existente

Los siguientes pasos son necesarios para habilitar la autenticación en tu aplicación:

  • Registrar tu aplicación para la autenticación
  • Configurar Azure Mobile Services
  • Restringir los permisos de tabla a usuarios autenticados
  • Configurar tu aplicación para usar Azure Mobile Services
  • Añadir autenticación a la aplicación
  • Guardar en caché los tokens de autenticación en el cliente
  • Obtener información del usuario desde el servidor

Debes registrar tu aplicación con un proveedor de identidad y añadir las credenciales generadas por dicho proveedor a Azure Mobile Services. Veamos primero cómo registrar tu aplicación para iniciar sesión con una cuenta de Microsoft.

1. Registro de la aplicación

Sigue estos pasos para usar Live Connect como proveedor de autenticación para Azure Mobile Services.

Paso 1: Crea una nueva aplicación en el Dev Center

Abre un navegador y dirígete al Dev Center para aplicaciones de Windows Store. Navega a la página Submit an app (Enviar una aplicación) y haz clic en App Name (Nombre de aplicación).

Submit an app to Windows StoreSubmit an app to Windows StoreSubmit an app to Windows Store

Paso 2: Reserva un nombre para la aplicación

Reserva un App name (Nombre de aplicación) y haz clic en Save (Guardar). La aplicación aparecerá listada en Windows Store usando este nombre.

Reserve app nameReserve app nameReserve app name

Paso 3: Configura Live Services para la aplicación

En la página Services (Servicios), haz clic en Live Services dentro de Microsoft Azure Mobile Services.

Navigate to Live Services siteNavigate to Live Services siteNavigate to Live Services site

Paso 4: Escribe los valores de configuración de la aplicación

Escribe los valores de Client IDClient secret y Package security identifier (SID). Vas a necesitar estos datos posteriormente.

Note the App Settings from Live Services siteNote the App Settings from Live Services siteNote the App Settings from Live Services site

Paso 5: Configura la URI de redireccionamiento para la aplicación

Dentro de API Settings (Configuración de API), establece el siguiente valor como Redirect URI (URI de redireccionamiento) y haz clic en Save (Guardar).

1
https://<mobile_service>.azure-mobile.net/login/microsoftaccount
Configure App Settings in Live Services SiteConfigure App Settings in Live Services SiteConfigure App Settings in Live Services Site

Esto habilita la autenticación a través de una cuenta de Microsoft para tu aplicación.

2. Configura Azure Mobile Services

Una vez que hayas registrado tu aplicación con el proveedor de identidad, debes configurar Azure Mobile Services usando el Azure Management Portal.

Paso 1: Selecciona el Mobile Service para la App

Inicia sesión en el Azure Management Portal, haz clic en Mobile Services (Servicios Móviles) y selecciona tu aplicación.

Select Mobile Service in Azure Management PortalSelect Mobile Service in Azure Management PortalSelect Mobile Service in Azure Management Portal

Paso 2: Establece las configuraciones Push

Dentro de la pestaña Push introduce los valores de Client Secret y Package SID y haz clic en Save (Guardar).

Set Client Secret and Package SID for the appSet Client Secret and Package SID for the appSet Client Secret and Package SID for the app

Paso 3: Establece las configuraciones de identidad

Dentro de la pestaña Identity (Identidad) establece el valor de Client ID. Además agrega los valores de Client Secret y Package SID si aún no se encuentran configurados.

Configure Microsoft account settingsConfigure Microsoft account settingsConfigure Microsoft account settings

Ahora estás listo para usar una cuenta de Microsoft para la autenticación en tu aplicación usando Azure Mobile Services.

3. Restringe los permisos de tabla

Usando el Azure Management Portal podemos establecer los permisos de tabla para restringir el acceso a solamente los usuarios que hayan iniciado sesión.

Paso 1: Selecciona la UsersTable

Elige la tabla para la que quieras cambiar los permisos dentro de la pestaña Data (Datos) en el Azure Management Portal. En este tutorial vamos a modificar los permisos para UsersTable.

Paso 2: Establece los permisos de tabla

Dentro de la pestaña Permissions (Permisos), establece el valor Only authenticated users (Solamente usuarios autenticados) para todos los permisos y haz clic en Save (Guardar).

Restrict table permissions to authenticated usersRestrict table permissions to authenticated usersRestrict table permissions to authenticated users

Cuando tu aplicación de Windows Store intenta acceder a esta tabla se produce una excepción no controlada con el código de estado 401 (No autorizado). Esto ocurre debido a que la aplicación intenta acceder a Azure Mobile Services como un usuario no autenticado.

4. Configura la aplicación para usar Mobile Services

A continuación necesitamos configurar la aplicación Windows Phone 8.1 WinRT para usar Azure Mobile Services.

Paso 1: (Opcional) Asocia tu aplicación con la tienda

Este paso solamente aplica para el proveedor de inicio de sesión de cuentas de Microsoft. Al registrar la información del paquete de la aplicación en Windows Store con Mobile Services, el cliente puede volver a usar los detalles de inicio de sesión de Microsoft para proporcionar una experiencia de inicio de sesión única.

Haz clic con el botón derecho en el proyecto dentro del Solution Explorer (Explorador de Soluciones), selecciona Store (Tienda) y haz clic en Associate App with the Store (Asociar aplicación con la tienda). En el asistente Associate Your App with the Windows Store (Asocia tu aplicación con Windows Store), haz clic en Sign in (Iniciar sesión) e inicia sesión con tu cuenta de Microsoft. Selecciona la aplicación que registraste anteriormente y elige Associate (Asociar) para asociarla con la tienda.

Associate app with storeAssociate app with storeAssociate app with store

En ese momento la información de registro necesaria para Windows Store es añadida al manifiesto de la aplicación.

Paso 2: Añade el SDK de Windows Azure Mobile Services

A continuación agrega el SDK de Windows Azure Mobile Services usando el administrador de paquetes NuGet.

Add Windows Azure Mobile Services SDK to the appAdd Windows Azure Mobile Services SDK to the appAdd Windows Azure Mobile Services SDK to the app

Paso 3: Añade Azure Mobile Services como un Connected Service

El servicio móvil que creaste en el Azure Management Portal debe vincularse a la aplicación. Haz clic con el botón derecho en el proyecto dentro del Solution Explorer (Explorador de Soluciones) y elige Connected Services (Servicios Conectados) dentro de Add (Añadir).

En el cuadro de diálogo Service Manager (Administrador de Servicios) que aparece, elige el servicio móvil que creaste anteriormente y haz clic en OK. Esto agrega una instancia de un servicio móvil en App.xaml.cs.

Connect the Mobile Service with your appConnect the Mobile Service with your appConnect the Mobile Service with your app

Paso 4: Agrega una clase para UsersTable

Define una clase UsersTable cuyos miembros dato representen a las columnas de la tabla. Necesitarás agregar una referencia a la biblioteca Json.NET en tu aplicación para usar la clase JsonProperty.

1
class UsersTable
2
{
3
    [JsonProperty(PropertyName = "id")]
4
    public string Id { get; set; }
5
6
    [JsonProperty(PropertyName = "userId")]
7
    public string UserID { get; set; }
8
9
    [JsonProperty(PropertyName = "user_email")]
10
    public string Email { get; set; }
11
12
    [JsonProperty(PropertyName = "profile_picture")]
13
    public string ProfilePicture { get; set; }
14
15
    [JsonProperty(PropertyName = "display_name")]
16
    public string DisplayName { get; set; }
17
18
}

5. Agrega autenticación a la aplicación

A continuación añadiremos la autenticación de usuario antes de solicitar cualquier recurso desde el servicio móvil.

Paso 1: Declara variables globales

Declara una variable global miembro en la clase MainPage para almacenar al usuario autenticado.

1
private MobileServiceUser user;

Paso 2: Define el método AuthenticateAsync

Vamos a añadir un método que lleve a cabo el proceso de autenticación. El método LoginAsync recibe al proveedor de identidad como parámetro y administra el proceso de autenticación.

1
private async System.Threading.Tasks.Task AuthenticateAsync()
2
{
3
    user = await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount);
4
}

Paso 3: Administra la respuesta durante la activación de la aplicación

En Windows Phone 8.1 necesitas administrar la respuesta del WebAuthenticationBroker. Debemos añadir un método OnActivated en App.xaml.cs para gestionar dicha respuesta.

1
protected override void OnActivated(IActivatedEventArgs args)
2
{
3
    #if WINDOWS_PHONE_APP
4
    if (args.Kind == ActivationKind.WebAuthenticationBrokerContinuation)
5
    {
6
        App.MobileService.LoginComplete(args as WebAuthenticationBrokerContinuationEventArgs);
7
    }
8
    #endif
9
10
    base.OnActivated(args);
11
}

Si el método OnActivated ya existe, simplemente agrega el código #if...#endif anterior. Toma en cuenta que el método LoginAsync debe ser llamado después de haber invocado al método OnNavigated y después de que el evento Loaded de la página haya sido activado.

Paso 4: Agrega un botón de inicio de sesión

Agrega un botón de inicio de sesión al archivo MainPage.xaml de tu aplicación y ejecuta un método para autenticar al usuario cuando el botón sea presionado.

1
<Button Name="ButtonLogin" Click="ButtonLogin_Click" Visibility="Visible">Sign in</Button>

Cuando se haga clic en el botón, ejecuta el método AuthenticateAsync y oculta el botón de inicio de sesión si la autenticación se llevó a cabo con éxito.

1
private async void ButtonLogin_Click(object sender, RoutedEventArgs e)
2
{
3
    // Login the user and then load data from the mobile service.

4
    await AuthenticateAsync();
5
6
    // Hide the login button and load items from the mobile service.

7
    this.ButtonLogin.Visibility = Windows.UI.Xaml.Visibility.Collapsed;
8
}

Paso 5: Maneja las excepciones en el método AuthenticateAsync

Nuestro método AuthenticateAsync administra la autenticación de los usuarios, pero podemos añadir código para gestionar excepciones y alternar los flujos. Debemos actualizar la función para ejecutar el método LoginAsync iterativamente hasta que user no sea null. Cuando la autenticación sea exitosa mostraremos el UserId del usuario autenticado. Una vez que el usuario haya iniciado sesión correctamente la aplicación deberá ejecutarse sin errores.

1
private async System.Threading.Tasks.Task AuthenticateAsync()
2
{
3
    while (user == null)
4
    {
5
        string message;
6
        try
7
        {
8
            user = await App.MobileService.LoginAsync(MobileServiceAuthenticationProvider.MicrosoftAccount);
9
            message = string.Format("You are now signed in - {0}", user.UserId);
10
        }
11
        catch (InvalidOperationException)
12
        {
13
            message = "Login Required";
14
        }
15
16
        var dialog = new MessageDialog(message);
17
        dialog.Commands.Add(new UICommand("OK"));
18
        await dialog.ShowAsync();
19
    }
20
}

6. Guarda en caché los tokens de autenticación en el cliente

La función AuthenticateAsync requiere que el cliente se ponga en contacto tanto con el proveedor de identidad como con el servicio móvil cada vez que la aplicación sea iniciada. Eso no es muy eficiente. Además, si muchos usuarios emplean la aplicación al mismo tiempo es posible que te encuentres con problemas de carga.

Para remediar este problema podemos guardar el token de autenticación en caché. Podemos intentar usar el token de autenticación que se encuentra en caché y regresar al flujo de autenticación predeterminado si el token ya no es válido.

1
private async System.Threading.Tasks.Task AuthenticateAsync(String provider)
2
{
3
    string message;
4
5
    // Use the PasswordVault to securely store and access credentials.

6
    PasswordVault vault = new PasswordVault();
7
    PasswordCredential credential = null;
8
9
    while (credential == null)
10
    {
11
        try
12
        {
13
            // Try to get an existing credential from the vault.

14
            credential = vault.FindAllByResource(provider).FirstOrDefault();
15
        }
16
        catch (Exception)
17
        {
18
            // When there is no matching resource an error occurs, which we ignore.

19
        }
20
21
        if (credential != null)
22
        {
23
            // Create a user from the stored credentials.

24
            user = new MobileServiceUser(credential.UserName);
25
            credential.RetrievePassword();
26
            user.MobileServiceAuthenticationToken = credential.Password;
27
28
            // Set the user from the stored credentials.

29
            App.MobileService.CurrentUser = user;
30
31
            try
32
            {
33
                // Try to return an item now to determine if the cached credential has expired.

34
                await App.MobileService.GetTable<TodoItem>().Take(1).ToListAsync();
35
            }
36
            catch (MobileServiceInvalidOperationException ex)
37
            {
38
                if (ex.Response.StatusCode == System.Net.HttpStatusCode.Unauthorized)
39
                {
40
                    // Remove the credential with the expired token.

41
                    vault.Remove(credential);
42
                    credential = null;
43
                    continue;
44
                }
45
            }
46
        }
47
        else
48
        {
49
            try
50
            {
51
                // Login with the identity provider.

52
                user = await App.MobileService.LoginAsync(provider);
53
54
                // Create and store the user credentials.

55
                credential = new PasswordCredential(provider, user.UserId, user.MobileServiceAuthenticationToken);
56
                vault.Add(credential);
57
            }
58
            catch (MobileServiceInvalidOperationException ex)
59
            {
60
                message = "You must log in. Login Required";
61
            }
62
        }
63
        message = string.Format("You are now logged in - {0}", user.UserId);
64
        var dialog = new MessageDialog(message);
65
        dialog.Commands.Add(new UICommand("OK"));
66
        await dialog.ShowAsync();
67
    }
68
}

La función AuthenticateAsync modificada intenta usar las credenciales almacenadas en PasswordVault para acceder al servicio móvil. La siguiente secuencia de eventos ocurre:

  • Si existen credenciales presentes, éstas se recuperan desde PasswordVault.
  • Se envía una consulta simple para verificar que el token no haya expirado.
  • Si el servidor responde con el código de estado 401, entonces regresamos al flujo de autenticación predeterminado.
  • Si PasswordVault no contiene credenciales regresamos al flujo de autenticación predeterminado.

Toma en cuenta que la aplicación busca tokens de autenticación que hayan expirado durante el inicio de sesión. Sin embargo los tokens pueden expirar después de la autenticación, mientras el usuario esté usando la aplicación. Una publicación en el blog de MSDN explica cómo gestionar una situación de ese tipo.

7. Obtén la información del usuario

Los objetos del cliente no exponen toda la información del usuario, pero a través del servidor podemos obtener todos los datos que necesitamos. El objeto User, que se envía a todas las secuencias de comandos, tiene una función getIdentities que devuelve un objeto con datos específicos del proveedor. Este puede ser usado para consultar la información del usuario. Para un usuario autenticado con una cuenta de Microsoft, el objeto es devuelto invocando a la función user.getIdentities.

1
{ 
2
    "microsoft":{ 
3
        "userId":"MicrosoftAccount:my-actual-user-id", 
4
        "accessToken":"the-actual-access-token" 
5
    } 
6
} 

Para obtener la información del usuario debemos enviar una solicitud a https://apis.live.net/v5.0/me/ pasando el token de acceso como parámetro. La cantidad de información disponible desde los proveedores para las secuencias de comandos del usuario es limitada. Este es el resultado de la solicitud al punto final /me:

1
{ 
2
  "id": "my-live-id", 
3
  "name": "Vivek Maskara", 
4
  "first_name": "Vivek", 
5
  "last_name": "Maskara", 
6
  "link": "https://profile.live.com/", 
7
  "gender": null, 
8
  "locale": "en_US", 
9
  "updated_time": "2015-03-10T16:03:43-08:00" 
10
}

Es necesario solicitar ámbitos de autenticación adicionales para obtener más información. Azure Mobile Services nos permite especificar ámbitos personalizados que se envían a los proveedores de autenticación al llevar a cabo la autenticación del lado del servidor.

El inicio de sesión solamente solicita el ámbito wl.basic de forma predeterminada. Podemos obtener más información sobre el usuario si establecemos ámbitos adicionales. A continuación vamos a solicitar un ámbito adicional desde el inicio de sesión de Microsoft.

Dentro de la pestaña Configure (Configurar) del servicio móvil establece el valor de MS_MicrosoftScope en app settings (Configuración de aplicación).

app settingsapp settingsapp settings

Como resultado de este cambio obtendremos la información adicional solicitada después de iniciar sesión nuevamente.

1
{ 
2
  "id": "my-live-id", 
3
  "name": "Vivek Maskara", 
4
  "first_name": "Vivek", 
5
  "last_name": "Maskara", 
6
  "link": "https://profile.live.com/", 
7
  "gender": null, 
8
  "emails": { 
9
    "preferred": "maskaravivek@hotmail.com", 
10
    "account": "maskaravivek@hotmail.com", 
11
    "personal": null, 
12
    "business": null 
13
  }, 
14
  "locale": "en_US", 
15
  "updated_time": "2015-03-10T16:03:43-08:00" 
16
}

Si el usuario inició sesión a través de una cuenta de Microsoft se enviará una solicitud a las APIs de Live Connect pasando el token almacenado en el objeto de identidades de usuario. Finalmente se analizará el objeto JSON que se devuelve y obtendrá los detalles del perfil del usuario.

1
user.getIdentities({
2
  success: function (identities) {
3
		var url;
4
		var oauth = null;
5
		if (identities.microsoft) {
6
			var liveAccessToken = identities.microsoft.accessToken;
7
			url = 'https://apis.live.net/v5.0/me/?method=GET&access_token=' + liveAccessToken;
8
		}
9
10
		if (url) {
11
			var requestCallback = function (err, resp, body) {
12
				if (err || resp.statusCode !== 200) {
13
					console.error('Error sending data to the provider: ', err);
14
					request.respond(statusCodes.INTERNAL_SERVER_ERROR, body);
15
				} else {
16
					try {
17
						var userData = JSON.parse(body);
18
						item.userId = user.userId;
19
						item.display_name = userData.name;
20
						item.user_email= userData.emails['account'];
21
						request.execute();
22
					} catch (ex) {
23
						console.error('Error parsing response from the provider API: ', ex);
24
						request.respond(statusCodes.INTERNAL_SERVER_ERROR, ex);
25
					}
26
				}
27
			}
28
			var req = require('request');
29
			var reqOptions = {
30
				uri: url,
31
				headers: { Accept: "application/json" }
32
			};
33
			req(reqOptions, requestCallback);
34
		} else {
35
			// Insert with default user name

36
			request.execute();
37
		}
38
	}
39
});

Ahora necesitamos modificar la secuencia de comandos de inserción para insertar un registro nuevo si no existe un usuario con el identificador userId. Si tal usuario existe debemos actualizarlo. Puedes usar una secuencia de comandos de tabla del lado del servidor para verificar si existe un registro antes de terminar la operación de inserción.

A continuación se muestra una secuencia de comandos de ejemplo que verifica si alguno de los elementos de la tabla tiene un valor userId que coincida, y de ser así no lleva a cabo una inserción.

1
function insert(item, user, request) {
2
    var table = tables.getTable('UsersTable');
3
    table.where({ 
4
       userId: user.userId
5
    }).read({
6
       success: upsertItem
7
    });
8
 
9
    function upsertItem(existingItems) {
10
        if (existingItems.length === 0) {
11
            request.execute();
12
        } else {
13
        item.id = existingItems[0].id;
14
          table.update(item, {
15
            success: function(updatedItem) {
16
                request.respond(200, updatedItem)
17
            }
18
          });
19
        }
20
     }
21
 }

Ahora podemos combinar ambas secuencias de comandos para construir nuestra secuencia de inserción final para UsersTable. Dentro de la pestaña Script (Secuencia de comandos) de UsersTable reemplaza los comandos de inserción con la siguiente secuencia de instrucciones:

1
function insert(item, user, request) {
2
    item.display_name = "<unknown>"; // default

3
    var table = tables.getTable('UsersTable');
4
    table.where({ 
5
       userId: user.userId
6
    }).read({
7
       success: upsertItem
8
    });
9
10
    function upsertItem(existingItems) {
11
        if (existingItems.length === 0) {
12
            user.getIdentities({
13
			success: function (identities) {
14
				var url;
15
				var oauth = null;
16
				if (identities.microsoft) {
17
					var liveAccessToken = identities.microsoft.accessToken;
18
					url = 'https://apis.live.net/v5.0/me/?method=GET&access_token=' + liveAccessToken;
19
				}
20
	 
21
				if (url) {
22
					var requestCallback = function (err, resp, body) {
23
						if (err || resp.statusCode !== 200) {
24
							console.error('Error sending data to the provider: ', err);
25
							request.respond(statusCodes.INTERNAL_SERVER_ERROR, body);
26
						} else {
27
							try {
28
								var userData = JSON.parse(body);
29
								item.userId = user.userId;
30
								item.display_name = userData.name;
31
								item.user_email= userData.emails['account'];
32
								request.execute();
33
							} catch (ex) {
34
								console.error('Error parsing response from the provider API: ', ex);
35
								request.respond(statusCodes.INTERNAL_SERVER_ERROR, ex);
36
							}
37
						}
38
					}
39
					var req = require('request');
40
					var reqOptions = {
41
						uri: url,
42
						headers: { Accept: "application/json" }
43
					};
44
					req(reqOptions, requestCallback);
45
				} else {
46
					// Insert with default user name

47
					request.execute();
48
				}
49
			}
50
		});
51
        } else {
52
        // we have updated the user with existing values but you could put new values here.

53
        item.id = existingItems[0].id;
54
        item.userId= existingItems[0].userId;
55
        item.display_name= existingItems[0].display_name;
56
        item.user_email= existingItems[0].user_email;
57
        item.profile_picture= existingItems[0].profile_picture;
58
          table.update(item, {
59
            success: function(updatedItem) {
60
                request.respond(200, updatedItem)
61
            }
62
          });
63
        }
64
     }
65
}

Ahora que hemos actualizado la secuencia de comandos de inserción, cualquier llamada a una operación de inserción añadirá un nuevo registro si no hay ningún usuario con un userId específico. Además, el elemento insertado se actualiza con la información de usuario userIdname además de email.

He agregado un método InsertUser que recibe un parámetro user de tipo UsersTable y lo inserta en la tabla.

1
private async Task InsertUser(UsersTable user)
2
{
3
    await peopleTable.InsertAsync(user);
4
}

Después de llamar al método AuthenticateAsync al hacer clic en el botón, ejecuto el método InsertUser para agregar al usuario a UsersTable.

1
await InsertUser(new UsersTable
2
{
3
    ProfilePicture= string.Empty
4
});

Puedes ejecutar la aplicación en el emulador para verificar que funcione. Cuando inicias sesión por segunda vez, la aplicación usa el token de autenticación en caché en vez de presentar la pantalla de inicio de sesión.

Login using a Microsoft accountLogin using a Microsoft accountLogin using a Microsoft account

Conclusión

La autenticación de usuarios para diferentes proveedores de identidad usando Azure Mobile Services es bastante simple. En este tutorial te mostré cómo puedes usar una cuenta de Microsoft para llevar a cabo la autenticación. El procedimiento para usar otros proveedores de identidad es el mismo. Solamente debe cambiarse el parámetro provider en la llamada a AuthenticateAsync. Es recomendable guardar en caché el token de autenticación para que el usuario pueda experimentar un único inicio de sesión.

Es posible solicitar ámbitos de autenticación adicionales para obtener más información del usuario. Este artículo de MSDN discute cómo es posible lograrlo para varios proveedores de identidad. No dudes en descargar los archivos fuente del tutorial como referencia. Recuerda configurar la aplicación para usar Azure Mobile Services antes de desplegarla.

Advertisement
Did you find this post useful?
Want a weekly email summary?
Subscribe below and we’ll send you a weekly email summary of all new Code tutorials. Never miss out on learning about the next big thing.
Advertisement
Looking for something to help kick start your next project?
Envato Market has a range of items for sale to help get you started.