API para Login con OAuth2

En esta entrada vamos a describir como utilizar un Api para login que se ha desarrolado para una aplicación en concreto pero que puede ser incluida en cualquiera de las que desarrollan los alumnos. Utiliza un servidor REST con Laravel, usando Passport como sistema de autenticación. Desde el punto de vista de Android, sólo tenemos que usar el Api para autenticarnos y una vez autenticados para pedir información al servidor con nuestro token de autentificación. Lo explicamos aqui…

Tenemos montada un API para que nuestra aplicación Android pueda realizar un proceso de login y registro contra un servidor RestFull.

LLamando a distintas rutas del API podremos:

  • Realizar Login
  • Registrar un usuario en el sistema remoto.
  • *Realizar Logout del usuario logueado
  • *Cambiar el password del usuario logueado
  • *Pedir información del usuario logueado

El servidor como contestación a la llamada a todas las rutas nos devolverá un resultado en JSON que deberemos analizar.

Para llamar a las Rutas para Login y Registro no deberemos identificarnos via un Token de acceso.
Sin embargo para las rutas marcadas con *, Logout, Cambio de Password y Petición de información si, con lo que tendremos que enviar un Token de acceso en la cabecera de la petición HTTP.

Veamos una a una las url de servicio del API:

Login

No requiere que tengamos un Token de acceso. Nuestra aplicación enviará una solicitud HTTP al servidor con la siguiente configuración.

Solicitud: POST <servidor:puerto>/<api>/login

Parámetros de la Cabecera HTTP:

Accept: application/json

Contenido del Body

email: <email@delusuario.com>
password: <password>

Los dos campos son obligatorios. El email es el campo por el que se loguea el usuario.
El password debe coincidir con el que se dio en el registro del usuario.

Respuesta del servidor:

Respuesta en caso de Login Incorrecto

{
    "success": false,
    "message": "Unauthorised"
}

Respuesta en caso de Login Correcto

{
    "success": true,
    "size": 1,
    "data": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.e. ...",
    "message": "Access token saved for user."
}

En el campo “data” del objeto json de respuesta viene el token de acceso que hay que enviar en las peticiones con autenticación.

Trataremos esta respuesta con un objeto JSONObject, del que extraemos el campo data para almacenar el token en la aplicación. Sucesivas peticiones al servidor podemos enviar el token, con lo que el servidor tendrá constancia que somos nosotros. Si hacemos logout y volvemos a hacer login, el token recibido en el segundo login es distinto al que teníamos.

Registro

No requiere que tengamos un Token de acceso (Esto se podría modificar, por ejemplo, que sólo determinados usuarios puedan realizar registros en el sistema). Nuestra aplicación enviará una solicitud HTTP al servidor con la siguiente configuración.

Solicitud: POST <servidor:puerto>/<api>/register

Parámetros de la Cabecera HTTP:

Accept: application/json

Campos del Body

Dependerá de la aplicación en concreto, pero básicamente si no se india lo contrario serían:

email: <email del nuevo usuario>
name: <nombre del nuevo usuario>
password: <password del nuevo usuario>

Todos los campos son obligatorios.
El email debe tener formato de email y no estár repetido en el servidor.

Respuesta del servidor:

Respuestas en caso de registro incorrecto

{
    "success": false,
    "message": "Error de Validación",
    "data": {
        "email": [
            "The email must be a valid email address."
        ]
    }
}
{
    "success": false,
    "message": "Error de Validación",
    "data": {
        "email": [
            "The email has already been taken."
        ]
    }
}

Respuesta en caso de registro correcto

{
    "success": true,
    "message": "Usuario creado",
    "size": 1,
    "data": {
        "id": 13,
        "name": "nombre del usuario",
        "email": "email@delusuario.com",
        "token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9. ...."
    }
}

En el campo “data” del objeto json de respuesta viene un objeto json.
Este objeto lleva el id del nuevo usuario en el sistema, el nombre, el email y el token de acceso a usar en las peticiones que requieren de autenticación.

Trataremos esta respuesta con un objeto JSONObject en la aplicación. Accederemos a su campo data y podremos crear un objeto en la aplicación con los datos que recibimos o lo que queramos. El campo data->token tiene el token que se le da al usuario en el registro. Estamos suponiendo que cualquier usuario se puede registrar, con lo que en el momento que se registra es como si se hubiera logueado, por lo que recibe un token que guardamos en el sistema. Si hubiésemos cambiado el sistema y sólo el usuario admin puede registrar usuarios, no tendría sentido recibir un token de acceso.

Logout

Requiere un Token de acceso. Nuestra aplicación enviará una solicitud HTTP al servidor con la siguiente configuración.

Solicitud: GET <servidor:puerto>/<api>/logout

Parámetros de la Cabecera HTTP:

Accept: application/json
Authorization: Bearer <token>

La clave Authorization tiene el valor “Bearer” seguida de un espacio y el token que recibimos en el login o el registro

Sin Body

Respuesta del servidor:

Respuesta cuando el token que enviamos en la cabecera no es válido

{
    "message": "Unauthenticated."
}

Mensaje cuando el token que enviamos en la cabecera es válido y realiza el logout.

{
    "success": true,
    "message": "User logged out",
    "size": 1,
    "data": {
        "id_usser": <id del usuario logged out>
    }
}

Tratamos esta respuesta en la aplicación con un objeto JSONObject. Ante una respuesta correcta, realmente no tenemos más que eliminar el token de acceso que teníamos guardado para que sucesivas llamadas no tengan éxito por ir con un token vacío.

Cambio de Password

Requiere un Token de acceso. Nuestra aplicación enviará una solicitud HTTP al servidor con la siguiente configuración.

Solicitud: POST <servidor:puerto>/<api>/pwdchange

Parámetros de la Cabecera HTTP:

Accept: application/json
Authorization: Bearer <token>

La clave Autorization tiene el valor “Bearer” seguida de un espacio y el token que recibimos en el login o el registro

Contenido del Body

Accept: application/json
Authorization: Bearer <token>

La clave Authorization tiene el valor “Bearer” seguida de un espacio y el token que recibimos en el login o el registro

Respuesta del servidor:

Respuesta cuando el token que enviamos en la cabecera no es válido

{
    "message": "Unauthenticated."
}

Mensaje cuando el token que enviamos en la cabecera es válido y realiza el logout.

{
    "success": true,
    "message": "Password cambiado",
    "size": 1,
    "data": {
        "id": <id>,
        "name": "nombre del usuario",
        "email": "email@delusuario.com"
    }
}

Trataremos esta respuesta en la aplicación con un objeto JSONObject. Realmente no haremos nada más puesto que no guardamos el password del usuario en la aplicación.

Información de usuario, /details

Este end-point o ruta, sirve como ejemplo de cualquier respuesta formateada en JSON sin que corresponda a un objeto del modelo de negocio. Contrastar el json que devuelven las rutas /details y la ruta /user/8 por ejemplo, vemos que ambas devuelven la misma información del usuario, sin embargo esta ruta (/details) devuelve más campos en un formato no estandar de modelo de datos u objeto de negocio, como sí lo hace la ruta /users/8.

Como programadores de front-end tendremos que adaptarnos a la respuesta que envía el servidor, aunque ambas son en formato JSON, de la response de la ruta /users/8 puedo cargar un POJO (Plain Old Java Object) del objeto JSON, sin embargo la respuesta de eta ruta /details no parece estar pensada para ser insertada en un POJO directamente, pues lleva campos (success, size, message) que no llevaría el objeto usuario en un modelo de datos de negocio. o en formato Objeto (JSON)

Requiere un Token de acceso. Nuestra aplicación enviará una solicitud HTTP al servidor con la siguiente configuración.

Solicitud: POST <servidor:puerto>/<api>/details

Parámetros de la Cabecera HTTP:

Accept: application/json
Authorization: Bearer <token>

La clave Autorization tiene el valor “Bearer” seguida de un espacio y el token que recibimos en el login o el registro

Sin Body

Respuesta del servidor:

Respuesta cuando el token que enviamos en la cabecera no es válido

{
    "message": "Unauthenticated."
}

Mensaje cuando el token que enviamos en la cabecera es válido y sirve la info.

Esta información dependerá de la aplicación. Por defecto enviara en el data: un objeto json con el usuario (informacion de la base de datos) y algún campo más dependiente de la aplicación. Por ejemplo aquí muestra el número de libros que son del usuario y el número de libros que tiene en total, suyos y prestados de otros usuarios.

{
    "success": true,
    "message": "Detalles de usuario",
    "size": 1,
    "data": {
        "id": 8,
        "name": "admin",
        "email": "admin@admin.com",
        "propietario": 0,
        "posee": 0
    }
}

Información de usuario, /users/{id}

Este end-point o ruta, sirve como ejemplo de devolución de un objeto de negocio, en este caso un objeto usuario. Si vemos el valor de retorno cuando la solicitud es correcta, vemos que es un simple objeto JSON, con los campos que podrían ser los de un objeto java (POJO) y por lo que podríamos cargarlo directamente a un objeto java al tratar la response del servidor.

Requiere un Token de acceso. Nuestra aplicación enviará una solicitud HTTP al servidor con la siguiente configuración.

Solicitud: POST <servidor:puerto>/<api>/users/{id}

Parámetros de la Cabecera HTTP:

Accept: application/json
Authorization: Bearer <token>

La clave Autorization tiene el valor “Bearer” seguida de un espacio y el token que recibimos en el login o el registro

Sin Body

Respuesta del servidor:

Respuesta cuando el token que enviamos en la cabecera no es válido:

{
    "message": "Unauthenticated."
}

Respuesta cuando el parámetro {id} no se encuentra en el servidor:

{
    "success": false,
    "message": "No se encuentra"
}

Mensaje cuando el token que enviamos en la cabecera es válido y existe el usuario cuyo identificador es id:

Esta información dependerá de la aplicación. En este caso devuelve un objeto User, aquel cuyo identificador coincide con el que pasaos en la query, {id}. Si o existe devuelve success false como se muestra anteriormente.

{
    "id": 9,
    "name": "miguel",
    "email": "miguel@correo.com",
    "propietario": 0,
    "posee": 0
}

Podemos tratar esta respuesta como un objeto JSONObject en la aplicación o como un Objeto POJO.

Información de usuarios, /users

Este end-point o ruta, sirve como ejemplo de devolución de un conjunto de objetos de negocio, en este caso un array de objetos usuario. Si vemos el valor de retorno cuando la solicitud es correcta, vemos que es un simple array JSON donde cada elemento del array son objetos json que corresponden con un Usuario del modelo, que podrían ser los de un objeto java (POJO) y por lo que podríamos carga directamente a un array de objetoa java al tratar la response del servidor.

Requiere un Token de acceso. Nuestra aplicación enviará una solicitud HTTP al servidor con la siguiente configuración.

Solicitud: POST <servidor:puerto>/<api>/users

Parámetros de la Cabecera HTTP:

Accept: application/json
Authorization: Bearer <token>

La clave Autorization tiene el valor “Bearer” seguida de un espacio y el token que recibimos en el login o el registro

Sin Body

Respuesta del servidor:

Respuesta cuando el token que enviamos en la cabecera no es válido:

{
    "message": "Unauthenticated."
}

Respuesta cuando no hay ningún usuario en el servidor:

{
    "success": false,
    "message": "No se encuentra"
}

Mensaje cuando el token que enviamos en la cabecera es válido y existen usuarios:

Esta información dependerá de la aplicación. En este caso devuelve un objeto JSONArray, que tiene un conjunto de objetos json que coinciden con objetos del modelo (User)

[
    {
        "id": 8,
        "name": "admin",
        "email": "admin@admin.com",
        "propietario": 0,
        "posee": 0
    },
    {
        "id": 9,
        "name": "miguel",
        "email": "miguel@correo.com",
        "propietario": 0,
        "posee": 0
    },
    {
        "id": 12,
        "name": "onofre",
        "email": "onofre@gmail.com",
        "propietario": 0,
        "posee": 0
    },
    {
        "id": 13,
        "name": "onofre",
        "email": "onofres@gmail.com",
        "propietario": 0,
        "posee": 0
    }
]

Podemos tratar esta respuesta como un objeto JSONArray en la aplicación o como un array de objetos Objeto POJO.