Windows Phone, WebAuthenticationBroker y métodos AndContinue | C#

Ya en el artículo anterior revisamos los métodos AndContinue , es hora de traerlo a la práctica y en esta ocasión lo haremos con el WebAuthenticationBroker. WebAuthenticationBroker es un componente que nos permite iniciar un flujo de autenticación con OAuth 2.0 que como espero muchos de ustedes sabrán es el protocolo mediante el cual puedes iniciar sesión en un servicio web sin que la aplicación se de por enterada del login y contraseña del usuario.

De esta forma desde una aplicación puedes conectarte por ejemplo a twitter o facebook sin que el usuario te entregue los datos directamente, en su lugar el usuario debe iniciar sesión en una pantalla web.

El WebAuthenticationBroker funciona casí identicamente en Windows Phone 8.1 o en Windows RT, la diferencia principal esta en que para el caso de Windows debemos tener muy en cuenta los dispositivos de poca memoria, por ello cuando estamos en Windows Phone debemos hacer uso de métodos AndContinue.

No he colocado "debemos" en negrita solo por coincidencia, hay que usarlos obligatoriamente en el caso de Windows Phone 8.1.

Cómo empézar?

Lo primero que hay que hacer, sino lo han hecho, es revisar este artículo:

Windows Phone - Métodos AndContinue

Para comprender de que tratan los métodos AndContinue, si ya tienen eso claro entonces entramos materia.

Crearemos un App que se autentique en facebook para obtener el token, que es la parte principal para poder acceder a todos los demas servicios de facebook.

Para ello seguiremos la siguiente secuencia:

  1. Crear una clase llamada FacebookManager, esta la utilizaremos para encapsular la lógica relacionada con la autenticación en facebook. (este artículo no habla en detalle de ese tema, pero lo explicare en uno próximo)
  2. Allí utilizaremos el WebAuthenticationBroker. para realizar el proceso de Login. Así que creamos un método llamado LoginAndContinue donde llamaremos WebAuthenticationBroker.AuthenticateAndContinue con la url de autenticación para facebook. Como deben saber [si leyeron el artículo citado arriba] esto ocasionará que nuestra app se suspenda, y será solo reactivada cuando el WebAuthenticationBroker culmine su labor.
  3. Luego de esto la App será reactivada, por lo que al final de cuentas debemos hacer que se ejecute otro método del FacebookManager que llamaremos ContinueAuthentication. Allí recibiremos el token de facebook que es nuestro objetivo.
  4. Sin embargo llegar al médtodo ContinueAuthentication requiere un paso adicional, sobre todo si tenemos en cuenta que pueden otros métodos AndContinue que hallan llevado la app a suspenderse.

Preparando la solución

La idea del código que crearemos es que sea entendible y en la medida de lo posible útil en otros escenarios.

Para ahorrarnos la mayor cantidad de código y utilizar algunas funcionalidades que vienen uncluidas en los template crearemos un proyecto de Windows Phone 8.1 - Pivot.

Crear nueva solución Windows Phone 8.1

Una vez creado procedemos a borrar los elementos mostrados, no los necesitamos.

Archivos para borrar

Ahora creamos una nueva página de tipo BasicPage

BasicPage

y la preparamos con un Button para disparar el login y un TextBlock para mostrar allí el token de facebook una vez lo hemos recibido.

Formulario Windows Phone

Para que no digan que soy envidioso, pueden ahorrarse un tiempo con la creación de esos controles, este es el código XAML.

<!--TODO: Content should be placed within the following grid-->  
<Grid Grid.Row="1" x:Name="ContentRoot" Margin="19,9.5,19,0">  
    <StackPanel>
        <Button x:Name="btnFacebookLogIn"
                Margin="50,10,50,10" 
                HorizontalAlignment="Stretch"
                Click="btnFabookLogin_Click">
            <Grid>
                <Grid.ColumnDefinitions>
                    <ColumnDefinition Width="1*"/>
                    <ColumnDefinition Width="2*"/>
                </Grid.ColumnDefinitions>

                <Image Source="ms-appx:///Assets/Images/Facebook.png"
                        Stretch="Uniform"
                        Margin="10"
                        HorizontalAlignment="Stretch"
                        VerticalAlignment="Stretch"/>
                <TextBlock Margin="10" Grid.Column="1" 
                    VerticalAlignment="Center"
                    Text="Facebook Login"/>
            </Grid>
        </Button>
        <TextBox x:Name="txtFbToken" 
                    Margin="50,0,50,0" 
                    PlaceholderText="facebook token" 
                    IsReadOnly="True" 
                    Height="340" 
                    TextWrapping="Wrap"/>
    </StackPanel>
</Grid>  

En BasicPageFB.xaml.cs agreguen un manejador para el evento Click del botón btnFacebookLogIn dejenlo sin código, por ahora.

private void btnFabookLogin_Click(object sender, RoutedEventArgs e)  
{}

Y dado mi característico espiritu bondadoso, también les comparto el ícono de facebook [elaborado con metro studio], el fondo es transparente se ve rojo acá solo para que puedan diferenciar la forma.

facebook icon

Ahora modifiquen Appx.xaml.cs para que inicie con BasicPageFB y no con PivotPage.

Comopilamos y ejecutamos el proyecto para validar que todo este funcionando bien.

1. Crear la clase FacebookManager

En el proyecto creamos una carpeta llamada Manager, y dentro de ella creamos una clase llamada FacebookManager de la siguiente forma:

class FacebookManager  
{
    readonly string _facebookUrl = "https://www.facebook.com/dialog/oauth?client_id={app-id}&redirect_uri={redirect-uri}&scope=publish_actions&display=popup&response_type=token";
    Uri _facebookUri, _callbackUri;
    const string APP_FACEBOOK_ID = "PON AQUÍ TU FACE APP ID";

    public FacebookManager()
    {
        _callbackUri = WebAuthenticationBroker.GetCurrentApplicationCallbackUri();
        Debug.WriteLine(_callbackUri);
        _facebookUrl = _facebookUrl.Replace("{app-id}", APP_FACEBOOK_ID).Replace("{redirect-uri}", _callbackUri.AbsoluteUri);
        _facebookUri = new Uri(_facebookUrl);
    }

    public void LoginAndContinue()
    {
    }

    public string ContinueAuthentication(WebAuthenticationBrokerContinuationEventArgs args)
    {
        return string.Empty;
    }
}

Enlazar la App con facebook

El AppId lo obtenemos de facebook visitando esta página y por la opción mostrada obtienen el AppId.

Crear App de Facebook

Una vez reemplazamos app Id vamos a BasicPageFB.xaml.cs allí creamos un campo privado FacebookManager y lo instanciamos.

En el manejador del evento del botón llamamos al método LoginAndContinue

FacebookManager facebookManager = new FacebookManager();  
private void btnFabookLogin_Click(object sender, RoutedEventArgs e)  
{
    facebookManager.LoginAndContinue();
}

Warning Recuerden que el código esta expuesto de manera sencilla para que puedan aprender, sin embargo en condiciones ideales deberían utilizar MVVM y técnicas relacionadas para organizar su código.

Procedemos a ejecutar la App.

No debe haber ninguna novedad, pero si has seguido todos los pasos en el output de Visual Studio debe aparecer algo como esto:

Visual Studio Output Window

Procedemos a copiar esa Uri, en la página de la App de Facebook agregramos una nueva plataforma y seleccionamos Windows App allí aparecen dos campos, dado que esta App la estamos creando como Windows Phone 8.1 entonces debemos colocar la Uri en Windows Store ID. Si fuera una App Windows Phone 8.1 Silverlight deberiamos usar el otro campo y una Uri diferente.

La Uri debes colocarla allí copiando todo Guid desde "s-" sin inclkuir rl "/" del final quedando algo así:

Facebook Windows Platform Configuration

2. WebAuthenticationBroker para realizar el proceso de Login y recuperación del token

Ahora en FacebookManager.LoginAndContinue llamamos al método AndContinue del WebAuthenticationBroker pasandole la Url de autenticación de facebook.

public void LoginAndContinue()  
{
    WebAuthenticationBroker.AuthenticateAndContinue(_facebookUri);
}

Así que al hacer click en el botón se nos mostrará la página de inicio de sesión en facebook, y una vez lo hagamos nos perida autorización para la App.

Facebook Login and Auth

Apenas se ha lanzaso el WebAuthenticationBroker nuestra App ha entrado en estado de suspensión y según sea el caso incluso puede haberse cerrado, depende de las condiciones de memoria del dispositivo.

Cuando finalizamos el proceso de autenticación el sistema procede a activar la app nuevamente.

El método ContinueAuthentication revisa la respuesta del WebAuthenticationBroker y extrae el token de facebook asociado, no entraremos en mucho detalle al respecto pues el foco de este artículo no es facebook propiamente, eso será en otro artículo.

Por ahora debemos concentrarnos en el hecho de que este método se debe ejecutar una vez la app sea reactivada.

    public string ContinueAuthentication(WebAuthenticationBrokerContinuationEventArgs args)
    {
        if (args.WebAuthenticationResult.ResponseStatus == WebAuthenticationStatus.Success)
        {
            var match = Regex.Match(args.WebAuthenticationResult.ResponseData, "access_token=(?<our_token>.+?)&+");
            var token = match.Groups["our_token"];

            return token.Value;
        }

        return string.Empty;
    }

3. Suspensión y reactivación de la App

Desde el inicio de este artículo hice enfasis en crear un proyecto con base en uno de los templates de Visual Studio con el fin de reutilizar algunas funcionalidades que dichos templates traen.

De estas funcionalidades hay una que es crítica para el buen funcionamiento de una App que utilice métodos AndContinue, la funcionalidad de suspención y reactivación de la App. Si bien esta la podemos hacer a mano en un proyecto desde 0 no hay porque reinventar la rueda la funcionalidad que los templates traen ya es suficientemente buena en la amplia mayoria de escenarios.

Si queremos hacer un app robusta debemos plantearnos la posibilidad de que se use el WebAuthenticationBroker, o cualquier otro componente que utilice métodos AndContinue, en más de una ocasión.

Imaginemos que nuestra App requiere ser vinculada con

  • Facebook
  • Twitter
  • Outlook

Asumamos que cada una de estas operaciones se hacen en diferentes lugares de la App, por ende al suspenderse y luego reactivarse no solo basta con saber que fue suspendida desde un WebAuthenticationBroker sino que es necesario contar con más información.

Por ello modificaremos el método LoginAndContinue para que almacene información relacionada con el uso del WebAuthenticationBroker, especificamente nos interesa enviar continuationData un diccionario de datos donde podemos almacenar todo lo que consideremos pertinente y que nos ayude a llevar a la App al punto donde estaba justo antes de suspenderse.

public void LoginAndContinue()  
{
    var continuationData = new ValueSet();
    continuationData.Add("login-type", "facebook");

    WebAuthenticationBroker.AuthenticateAndContinue(_facebookUri,
        _callbackUri, continuationData,
        WebAuthenticationOptions.None);
}

De la suspención se encarga el sistema, se disparará el evento OnSuspending que se encuentra en la case App : App.xaml.cs, el cual por el momento luce así(no es necesario hacer nada).

private async void OnSuspending(object sender, SuspendingEventArgs e)  
{
    var deferral = e.SuspendingOperation.GetDeferral();
    await SuspensionManager.SaveAsync();
    deferral.Complete();
}

En la misma clase haremos sobre escritura (override)del método OnActivated, en el nos aseguramos de que el rootFrame este creado, para ello he creado el método CreateRootFrame() no entraremos en detalle de su lógica pero básicamente hace lo mismo que hace la app al iniciar: asegurarse que el frame principal existe y sino existe crearlo.

Seguidamente hay que restaurar el estado de la App si esta fue terminada previamente.

private void CreateRootFrame()  
{
    Frame rootFrame = Window.Current.Content as Frame;
    if (rootFrame == null)
    {
        rootFrame = new Frame();
        SuspensionManager.RegisterFrame(rootFrame, "AppFrame");
        Window.Current.Content = rootFrame;
    }
}

protected async override void OnActivated(IActivatedEventArgs args)  
{
    CreateRootFrame();

    // Restore the saved session state only when appropriate
    if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
    {
        try
        {
            await SuspensionManager.RestoreAsync();
        }
        catch { }
    }

    if (args is IContinuationActivatedEventArgs)
        throw new NotImplementedException();

    Window.Current.Activate();
}

Seguidamente, y muy importante, preguntamos si los argumentos del evento implementan IContinuationActivatedEventArgs, si esto es así quiere decir que la app fue suspendida por un método AndContinue. Intencionalmente he lanzado allí una NotImplementedException ya que esta parte apenas la comenzaremos a hacer.

Debemos hacer que la app se direccione hasta nuestro facebook manager, pero ya que somos concientes que pueden haber otros escenarios AndContinue que hayan llevado la app a suspenderse debemos validar cual de todos ellos fue antes de direccionarlo a donde corresponde.

4. Direccionando la App al escenario de continuación correcto

En el proyecto, carpeta Manager creamos una nueva clase llamada ContinuationManager en esta colocaremos toda la lógica que requiramos en nuestra App que este relacionada, de manera genérica, con métodos AndContinue.

Allí definimos un método ContinueWith donde validamos que ya exista el rootframe, esto es muy importante ya que por medio de este obtendremos acceso al page actual. Si todo ha salido bien y la app se ha activado exitosamente luego del procesos de suspensión este page es el último que estaba abierto justo antes de suspenderse.

Lego evaluamos args.Kind, esta enumeración tiene más de 20 valores diferentes pero para efectos del ContinuationManager solo nos interesan las 4 listadas abajo.

class ContinuationManager  
{
    public void ContinueWith(IActivatedEventArgs args)
    {
        var rootFrame = Window.Current.Content as Frame;
        if (rootFrame == null)
            return;

        switch (args.Kind)
        {
            case ActivationKind.PickFileContinuation:
                break;
            case ActivationKind.PickFolderContinuation:
                break;
            case ActivationKind.PickSaveFileContinuation:
                break;
            case ActivationKind.WebAuthenticationBrokerContinuation:
                break;
            default:
                break;
        }
    }
}

Como es de suponerse de todas estas solo debemos implementar las que que requiera nuestra app requiera, en este caso ActivationKind.WebAuthenticationBrokerContinuation.

Allí debemos acceder a la instancia del page actual, que es lo único que tenemos de la ejecución anterior y encontrar el modo de que la app continue con el proceso que dejo interrumpido, en este caso recuperar el token de facebook.

Regresamos a la clase App, allí definimos un campo de tipo ContinuationManager y lo instanciamos, seguidamente vamos al método OnActivated y donde lanzabamos una NotImplementedException invocamos ContinuationManager.ContinueWith().

ContinuationManager _myContinuationManager = new ContinuationManager();

protected async override void OnActivated(IActivatedEventArgs args)  
{
    CreateRootFrame();

    if (args.PreviousExecutionState == ApplicationExecutionState.Terminated)
    {
        try
        {
            await SuspensionManager.RestoreAsync();
        }catch { }
    }

    if (args is IContinuationActivatedEventArgs)
        _myContinuationManager.ContinueWith(args);

    Window.Current.Activate();
}

Recordemos nuestra premisa: en un escenario robusto pueden haber varios WebAuthenticationBroker.

Así que debemos invocar un método que continue con lo que se estaba realizando, para hacer las cosas más sencillas en la carpeta Misc del proyecto creamos una interfaz con un método llamado ContinueWithWebAuthenticationBroker

interface IWebAuthenticationBrokerContinuable  
{
    void ContinueWithWebAuthenticationBroker(WebAuthenticationBrokerContinuationEventArgs args);
}

De tal forma que podamos preguntar si el Page tenia implementada esa interfaz, de ser asi sabriamos que método llamar para continuar con la operación quedando el código así.

        case ActivationKind.WebAuthenticationBrokerContinuation:
            var continuator = rootFrame.Content as IWebAuthenticationBrokerContinuable;
            if (continuator != null)
                continuator.ContinueWithWebAuthenticationBroker((WebAuthenticationBrokerContinuationEventArgs)args);
            break;
        default:
            break;

Ahora debemos modificar nuestro Page BasicPageFB para que implemente la interfaz, quedando la declaración así:

public sealed partial class BasicPageFB : Page, IWebAuthenticationBrokerContinuable  
{
///...
    public void ContinueWithWebAuthenticationBroker(Windows.ApplicationModel.Activation.WebAuthenticationBrokerContinuationEventArgs args)
    {
        txtFbToken.Text = facebookManager.ContinueAuthentication(args);
    }
}

En este método es donde debemos invocar la lógica de continuación que teniamos en facebookManager.ContinueAuthentication, así que asignaremos el resultado de este método a txtFbToken.Text.

Facebook Token

Warning Recuerden que el código esta expuesto de manera sencilla para que puedan aprender, sin embargo en condiciones ideales deberian utilizar MVVM y técnicas relacionadas para organizar su código.

Código fuente de este artículo

JuanKRuiz GitHub

Este es todo el código creado y/o modificado para que lo puedan reutilizar. Esta colgado en Gists de Githbub.

Código relacionado con el post "WebAuthenticationBroker y métodos AndContinue | C#"
Espero sus comentarios y no duden en compartirlo ;)

Comparte este artículo

comments powered by Disqus