Autenticación con Visual Studio Online desde Java usando OAuth

El protocolo OAuth nos proporciona una manera muy fiable de autenticación, así como la posibilidad de interconectar sistemas sin tener que ceder nuestras credenciales a un tercero.

Visual Studio Online es uno de los proveedores que lo soporta, aunque con ciertas peculiaridades. En este artículo veremos cómo podemos utilizar OAuth con Java para conectarnos y autenticarnos, así las peculiaridades de VSO.

Cómo funciona OAuth2.0

Oauth se basa en dos conjuntos de clave, conocidos como key y secret, que el cliente (Aplicación A) proporciona al sistema al que se quiere conectar (Aplicación B). Veamos un ejemplo del ciclo de vida de una petición OAuth:

  1. El usuario desde Aplicación A solicita autenticar con Aplicación B
  2. Aplicación A redirige a la página de Aplicación B con la Api_Key, la url de retorno y otros parámetros como parte de la redirección.
  3. El usuario introduce sus credenciales y autoriza (o no) la cesión de datos desde la Aplicación B a la Aplicación A.
  4. La Aplicación B redirige de vuelta a la Aplicación A con la url de retorno especificada anteriormente con un código de autenticación.
  5. De lado de servidor, la Aplicación A le envía a la Aplicación B el código de autenticación, el secret, y otros parámetros.
  6. La Aplicación B devuelve un Token a la Aplicación A que identifica al usuario y que se puede empezar a utilizar para realizar peticiones.

Cliente Oauth

Vamos a crear un ejemplo de Aplicación A, es decir, un cliente OAuth. Para ello utilizaremos scribe, un componente OAuth para Java muy sencillo y muy fácil de extender para adaptar a nuestras necesidades:

OAuthService service = new ServiceBuilder()
                                  .provider(LinkedInApi.class)
                                  .apiKey(YOUR_API_KEY)
                                  .apiSecret(YOUR_API_SECRET)
                                  .build();

A partir de ese código podemos generar la URL de redirección o capturar el Token. Como Visual Studio Online no forma parte de los proveedores por defecto, crearemos nuestro propio proveedor:

@Override
public class VSOnlineApi extends DefaultApi20 {

    @Override
    public String getAccessTokenEndpoint() {
        return "https://app.vssps.visualstudio.com/oauth2/token";
    }

    @Override
    public String getAuthorizationUrl(OAuthConfig oac) {
        return String.format("https://app.vssps.visualstudio.com/oauth2/authorize?mkt=es&client_id=%s&response_type=Assertion&state=sample&scope=vso.profile&redirect_uri=%s", 
                oac.getApiKey(), oac.getCallback());
    }

    @Override
    public Verb getAccessTokenVerb() {
        return Verb.POST;
    }

    
    @Override
    public OAuthService createService(OAuthConfig config) {
        return new VSOOauthService(this, config);
    }

    @Override
    public AccessTokenExtractor getAccessTokenExtractor() {
        return new VSOTokenExtractor();
    }
}

Este proveedor consta de dos urls, la de token y la de autorización, el verbo que utilizaremos para solicitar el access token, y dos componentes, un servicio que crearemos a continuación y el extractor para procesar el código resultado de la autenticación. Con este proveedor podemos generar la URL para realizar la redirección:


 OAuthService service = new ServiceBuilder()
                           .provider(VSOnlineApi.class)
                           .apiKey("KEY")
                           .apiSecret("SECRET)
                           .callback("https://rlbisbe.dev:4567/callback")
                           .signatureType(SignatureType.QueryString)
                           .build();

Por otra parte, para poder hacer la llamada del lado de servidor y validar el token necesitaremos implementar el servicio VSOOauthService, ya que la implementación OAuth de Visual Studio Online tiene ciertas peculiaridades.

public class VSOOauthService extends OAuth20ServiceImpl {

    private final DefaultApi20 api;
    private final OAuthConfig config;
    
    public VSOOauthService(DefaultApi20 api, OAuthConfig config) {
        super(api, config);
        
        this.api = api;
        this.config = config;
    }
    
    @Override
    public Token getAccessToken(Token requestToken, Verifier verifier)
    {
      OAuthRequest request = new OAuthRequest(api.getAccessTokenVerb(), api.getAccessTokenEndpoint());
      request.addBodyParameter(VSOConstants.CLIENT_ASSERTION_TYPE, "urn:ietf:params:oauth:client-assertion-type:jwt-bearer");
      request.addBodyParameter(VSOConstants.GRANT_TYPE, "urn:ietf:params:oauth:grant-type:jwt-bearer");
      request.addBodyParameter(VSOConstants.CLIENT_ASSERTION, config.getApiSecret());
      request.addBodyParameter(VSOConstants.ASSERTION, verifier.getValue());
      request.addBodyParameter(OAuthConstants.REDIRECT_URI, config.getCallback());
      request.addHeader("Content-type", "application/x-www-form-urlencoded");
      config.log(request.getCompleteUrl());
      Response response = request.send();
      return api.getAccessTokenExtractor().extract(response.getBody());
    }
}

Esta clase nos permite construir una petición POST (que hemos visto antes en que nos permitirá validar el código que hemos recibido desde Visual Studio Online utilizando el secret de nuestra aplicación y las claves específicas que nos solicita la aplicación, enviar la petición y convertir el resultado en un Token para futuras peticiones.

Creando la web para conectarnos al servicio

Una vez que tenemos el servicio creado para obtener el token, vamos a crear una pequeña página para poder hacer el proceso completo. Para ello utilizaremos spark, un proyecto inspirado en Sinatra que nos permite crear aplicaciones web de una manera rápida y sencilla:


        OAuthService service = new ServiceBuilder()
                           .provider(VSOnlineApi.class)
                           .apiKey("KEY")
                           .apiSecret("SECRET")
                           .callback("https://rlbisbe.dev:4567/callback")
                           .signatureType(SignatureType.QueryString)
                           .debugStream(System.out)
                           .debug()
                           .build();
        
        get("/auth", (req, res) -> {
            res.redirect(service.getAuthorizationUrl(EMPTY_TOKEN));
            return null;
        });
        
         get("/callback", (req, res) -> {

             String code = req.queryParams("code");
             Verifier verifier = new Verifier(code);
             Token token = service.getAccessToken(EMPTY_TOKEN, verifier);
             return token;
        });

Con el sódigo anterior podemos ver dos puntos de entrada, /auth, que realiza la redirección al servicio, y /callback, que recoge el resultado de la redirección, parsea el código del resultado, solicita el token utilizando el servicio que hemos creado anteriormente y muestra el token como resultado:

Token[CODIFICADOENHEXADECIMAL, ]

Consideraciones adicionales: SSL

Visual Studio Online requiere que la url de retorno esté bajo el protocolo SSL y no admite localhost, con lo cual para pruebas lo que podemos hacer es crear un certificado autofirmado utilizando la utilidad Keytool de Java:

keytool -genkey -keyalg RSA -alias selfsigned -keystore keystore.jks -storepass password -validity 360 -keysize 2048

Además deberemos importar dicha keytool dentro de nuestro proyecto para poder utilizar HTTPS:

        SparkBase.setSecure("C:\\Users\\Roberto\\Documents\\NetBeansProjects\\OauthClientTest\\keystore.jks", "password", null, null);

Finalmente, como no admite localhost, una solución puede ser utilizar el nombre DNS de nuestro ordenador de pruebas, o bien mediante el fichero hosts crear una entrada para 127.0.0.1, en mi caso rlbisbe.dev

Documentación

Materiales del evento: «Tres Tecnologías Microsoft que no se dan durante la carrera»

El pasado viernes a la hora de comer pasé una hora con los chicos de UAM.net en la Escuela Politécnica Superior de la UAM hablando sobre las tres tecnologías Microsoft que, a mi entender, no se impartían en la carrera cuando yo era estudiante.

IMG_8990

Concretamente, en esta charla estuvimos hablando de:

  • Windows Phone, Windows 8 y el resto de las plataformas móviles
  • ASP.net MVC, Web API, verbos HTTP, APIs REST y otros lenguajes como Ruby o PHP
  • Características disponibles en Azure y qué otros proveedores nos podemos encontrar en el mercado

El vídeo

Como novedad, esta vez hemos grabado la charla y está disponible en Youtube:

Las slides

Las slides, como siempre, están disponibles a continuación:

El código

El código de los ejemplos utilizados, y que en el caso de la web es el que se despliega directamente a Azure Websites está disponible también en un repositorio de Github: 201501-demo-uam

Finalmente, los enlaces recomendados que estaban al final de la presentación los dejo aquí:

Preparar la charla ha sido un reto muy interesante, ya que era la primera vez que volvía a dar una charla en la escuela después de haber terminado la carrera, así que espero que a los asistentes (y los que la hayáis visto en diferido) os haya gustado.

Desde aquí mi agradecimiento a la organización por la posibilidad de volver a la EPS y la enhorabuena por conseguir mantener a la gente interesada en las tecnologías .NET. Nos vemos en el próximo evento!

Consumiendo APIs REST con Windows 8 [I/II] (Evento 21/03)

El pasado 21 de marzo en la Universidad de Salamanca, estuvimos hablando de diferentes APIs REST.

Durante algo más de una hora vimos cómo a través de APIs Rest podemos consumir y crear contenidos desde nuestras aplicaciones. En este primer artículo de la serie, veremos cómo consumir servicios existentes, primero por consola, y luego a través de un cliente Windows 8.

Continuar leyendo «Consumiendo APIs REST con Windows 8 [I/II] (Evento 21/03)»