Autenticación personalizada con Couchbase Mobile
Couchbase Mobile extiende Couchbase a la periferia, gestionando y sincronizando datos de forma segura desde cualquier nube a cualquier dispositivo móvil. Couchbase Mobile cuenta con una base de datos integrada -Couchbase Lite- con SQL y búsqueda de texto completo para JSON, sincronización peer-to-peer integrada y seguridad end-to-end desde la nube hasta el extremo.
Couchbase Mobile también cuenta con una pasarela web segura -Sync Gateway- que permite el acceso y la sincronización de datos a través de la web y admite múltiples métodos de autenticación. Uno de estos métodos es la autenticación personalizada en la que un App Server gestiona la autenticación con un sistema externo.
Esta entrada del blog le guiará a través del flujo de autenticación personalizada con ejemplos de cómo implementar el código del servidor de aplicaciones y el código de la aplicación móvil. Un OpenLDAP pero el flujo se aplica a cualquier proveedor de autenticación externo.
El servidor de aplicaciones de ejemplo es una aplicación node.js sencilla y los ejemplos de código de la aplicación móvil utilizan el módulo SDK para Android de Couchbase Lite.
Requisitos previos
Este blog asume familiaridad con Couchbase Server, Sync Gateway, y Couchbase Lite.
Necesitarás una instalación operativa de Sync Gateway 2.5 y Couchbase Server EE 6.x. Si es necesario, los siguientes enlaces pueden ponerte en marcha rápidamente:
Todo el código de este blog está disponible en el siguiente repositorio Git: https://github.com/dugbonsai/sg-custom-auth
Flujo de autenticación personalizado
El siguiente diagrama muestra un ejemplo de autenticación personalizada.

- El usuario de la aplicación móvil intenta iniciar sesión con sus credenciales a través de una interfaz REST expuesta por el App Server.
- El App Server autentica al usuario contra un servidor OpenLDAP.
- Si el usuario no está autenticado, se le devuelve a la interfaz de inicio de sesión.
- Si el usuario está autenticado, App Server obtiene la información del usuario de Sync Gateway a través de una interfaz REST.
- Si el usuario existe en Sync Gateway, vaya al paso 8.
- Si el usuario no existe en Sync Gateway, el App Server crea un nuevo usuario en Sync Gateway a través de una interfaz REST.
- Si el usuario no se creó en Sync Gateway, App Server devuelve un error y el usuario regresa a la interfaz de usuario de inicio de sesión.
- Si el usuario se creó en Sync Gateway, App Server crea una nueva sesión para el usuario en Sync Gateway a través de una interfaz REST.
- Si la sesión no se creó en Sync Gateway, App Server devuelve un error y el usuario regresa a la interfaz de usuario de inicio de sesión.
- Si la sesión se creó en Sync Gateway, App Server devuelve el ID de sesión.
- La aplicación móvil almacena el ID de sesión y crea un replicador de Couchbase Lite utilizando el ID de sesión.
- Si la replicación falla debido a una sesión caducada en Sync Gateway, el usuario vuelve a la interfaz de inicio de sesión.
Configuración de OpenLDAP
Si ya tiene configurado un servidor LDAP puede saltarse esta sección. Si no tiene un servidor LDAP configurado, estas instrucciones le guiarán a través de la configuración de OpenLDAP en Ubuntu 18.04.
- Crear un fichero LDIF (ldap_data.ldif) con información para rellenar la base de datos LDAP (pulse aquí para obtener más información sobre los archivos LDIF). Las instrucciones anteriores para configurar OpenLDAP también contienen instrucciones para crear el archivo LDIF para rellenar la base de datos LDAP. En este ejemplo, el usuario mobileuser (a partir de la línea 13) se utilizará cuando se autentique desde la aplicación móvil.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 |
dn: ou=People,dc=example,dc=com objectClass: organizationalUnit ou: People dn: ou=Groups,dc=example,dc=com objectClass: organizationalUnit ou: Groups dn: cn=department,ou=Groups,dc=example,dc=com objectClass: posixGroup cn: subgroup gidNumber: 5000 dn: uid=mobileuser,ou=People,dc=example,dc=com objectClass: inetOrgPerson objectClass: posixAccount objectClass: shadowAccount uid: mobileuser sn: User givenName: Mobile cn: Mobile User displayName: mobileuser uidNumber: 10000 gidNumber: 5000 userPassword: password gecos: Mobile User loginShell: /bin/bash homeDirectory: /home/mobileuser |
- Añade los datos iniciales a la base de datos OpenLDAP:
|
1 |
$ ldapadd -x -D cn=admin,dc=example,dc=com -W -f ldap_data.ldif |
- Pruebe la configuración buscando mobileuser en el directorio OpenLDAP:
|
1 |
$ ldapsearch -x -LLL -b dc=example,dc=com 'uid=mobileuser' |
Verás la información para mobileuser:
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 |
dn: uid=mobileuser,ou=People,dc=example,dc=com objectClass: inetOrgPerson objectClass: posixAccount objectClass: shadowAccount uid: mobileuser sn: User givenName: Mobile cn: Mobile User displayName: mobileuser uidNumber: 10000 gidNumber: 5000 gecos: Mobile User loginShell: /bin/bash homeDirectory: /home/mobileuser |
Implementación de App Server
El App Server se implementa como una aplicación node.js. El código completo del App Server está disponible en auth.js en el siguiente repositorio Git: sg-custom-auth. El App Server utiliza el paquete passport-ldapauth para la autenticación LDAP. Los siguientes fragmentos de código destacan la llamada para autenticar contra el servidor OpenLDAP y las llamadas a la API REST de Sync Gateway.
El App Server debe gestionar las llamadas a POST /login. Este endpoint se llama desde la aplicación móvil con las credenciales de usuario almacenadas en el cuerpo de la solicitud en nombre de usuario y contraseña. El App Server utiliza estas credenciales para autenticarse en el servidor OpenLDAP.
El valor de searchBase debe coincidir con los componentes de dominio definidos en su servidor LDAP. dc=example,dc=com se utilizó anteriormente, por lo que aquí se utilizan los mismos valores (línea 12).
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
var express = require('express'), passport = require('passport'), bodyParser = require('body-parser'), LdapStrategy = require('passport-ldapauth'), curl = require('curl-request'); var syncGatewayEndpoint = 'https://<Sync Gateway Host>:4985/<db>'; var app = express(); var OPTS = { server: { url: 'ldap://<OpenLDAP Host>:389', searchBase: 'dc=example,dc=com', // MUST match LDAP directory searchFilter: '(uid={{username}})' } }; passport.use(new LdapStrategy(OPTS)); app.use(bodyParser.json()); app.use(bodyParser.urlencoded({extended: false})); app.use(passport.initialize()); app.post('/login', passport.authenticate('ldapauth', {session: false}), function(req, res) { // user successfully authenticated against LDAP } |
Los siguientes pasos se ejecutarán si el usuario está autenticado.
Obtener información de usuario de Sync Gateway
Obtener información de usuario de Sync Gateway llamando a GET /{db}/_usuario/{nombre}. Si el usuario existe en Sync Gateway, el código de respuesta es 200. Si el usuario no existe en Sync Gateway, el código de respuesta es 404.
|
1 2 3 4 5 6 7 8 9 10 |
getUser = new(curl); getUser.setHeaders(['Content-Type: application/json']) .get(syncGatewayEndpoint + '/_user/' + req.body.username) .then(({statusCode, body, headers}) => { if (statusCode == 404) { // status == 404: user does not exist } else if (statusCode == 200) { // status == 200: user exists } }) |
Si el usuario no existe en Sync Gateway, cree un nuevo usuario
Cree un nuevo usuario en Sync Gateway llamando a POST /{db}/_user/. Si el usuario se ha creado correctamente en la puerta de enlace Sync, el código de respuesta es 201.
|
1 2 3 4 5 6 7 8 9 10 11 |
postUser = new(curl); postUser.setHeaders(['Content-Type: application/json']) .setBody('{"name": "' + req.body.username + '","password": "' + req.body.password + '"}') .post(syncGatewayEndpoint + '/_user/') .then(({statusCode, body, headers}) => { if (statusCode == 201) { // status == 201: success } else { // user could not be created } }) |
Crear una sesión para el usuario en Sync Gateway
Cree una sesión para el usuario en Sync Gateway llamando a POST /{db}/sesión. En este ejemplo, la sesión expirará transcurridos 30 minutos. Si la sesión se ha creado correctamente en Sync Gateway, el código de respuesta es 200. La respuesta JSON contiene los siguientes valores:
session_idel ID de la nueva sesión. La aplicación móvil lo utilizará para la autenticación con Sync Gateway.
caduca en: marca de tiempo cuando expira la sesión. No se utiliza en la aplicación móvil.
nombre_de_la_galleta: nombre de la cookie de sesión. No se utiliza en la aplicación móvil.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 |
resBody = {statusCode: 200}; postSession = new(curl); postSession.setHeaders(['Content-Type: application/json']) .setBody('{"name": "' + request.body.username + '","ttl": 1800}') .post(syncGatewayEndpoint + '/_session') .then(({statusCode, body, headers}) => { if (statusCode == 200) { // status == 200: success resBody.session_id = body.session_id; resBody.expires = body.expires; resBody.cookie_name = body.cookie_name; // send success response back to client response.send(JSON.stringify(resBody)); } else { // session could not be created } }) |
Pruebas de autenticación
Para probar la autenticación desde el App Server, inícielo utilizando el siguiente comando:
|
1 |
$ node auth.js |
Cuando se inicie, verás lo siguiente:
|
1 |
Listening on port 8080 |
Puede probar el servidor de aplicaciones utilizando un simple comando curl como el siguiente (o Postman, etc.):
|
1 |
$ curl -d "username=mobileuser&password=password" -X POST https://<AppServer host>:8080/login |
Verá un resultado similar al siguiente:
|
1 |
{"statusCode":200,"session_id":"c149012eaa8d0cf15b1b4110cf0a2fec259ef726","expires":"2019-08-01T13:47:56.311076773Z","cookie_name":"SyncGatewaySession"} |
Si el usuario no existe en LDAP, la respuesta será:
|
1 |
Unauthorized |
La salida del App Server tendrá este aspecto:
|
1 2 3 4 5 |
mobileuser has been authenticated with LDAP creating user mobileuser in Sync Gateway user mobileuser created in Sync Gateway create session for user mobileuser created session for mobileuser |
Implantación de aplicaciones móviles
Todo lo que queda por hacer es realizar las llamadas apropiadas desde la aplicación móvil al servidor de aplicaciones y a la pasarela de sincronización. La aplicación móvil utiliza el Marco de volea para realizar la llamada REST al App Server. Los siguientes fragmentos de código destacan la llamada al App Server y el código de Couchbase Lite para autenticar usando el session_id.
Autenticar al usuario móvil
Autenticar usuario con App Server llamando POST /login/
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 |
JSONObject reqBody = new JSONObject(); reqBody.put("username", <user supplied username>); reqBody.put("password", <user supplied password>); String url = <App Server host>:8080/login; RequestQueue queue = Volley.newRequestQueue(<context>); JsonRequest<JSONObject> jsonRequest = new JsonObjectRequest( Request.Method.POST, url, reqBody, new Response.Listener<JSONObject>() { @Override public void onResponse(JSONObject response) { // get session_id from response try { String sessionID = response.getString("session_id"); // Store session_id } catch (JSONException je) { // Handle exception } } }, new Response.ErrorListener() { @Override public void onErrorResponse(VolleyError error) { // authentication failed } }); queue.add(jsonRequest); |
Crear una réplica única
Cree una replicación única utilizando el identificador de sesión guardado (línea 13) y vuelva a autenticarse si la sesión de Sync Gateway ha caducado.
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 |
String syncGatewayEndpoint = "ws://<Sync Gateway Host>:4984/{db}"; URI url = null; try { url = new URI(mSyncGatewayEndpoint); } catch (URISyntaxException e) { e.printStackTrace(); return; } ReplicatorConfiguration config = new ReplicatorConfiguration(database, new URLEndpoint(url)); config.setReplicatorType(ReplicatorConfiguration.ReplicatorType.PUSH_AND_PULL); config.setContinuous(false); config.setAuthenticator(new SessionAuthenticator(sessionID)); Replicator replicator = new Replicator(config); replicator.addChangeListener(new ReplicatorChangeListener() { @Override public void changed(ReplicatorChange change) { CouchbaseLiteException error = change.getStatus().getError(); if (error != null) { if (error.getCode() == 10401) { // session expired; re-authenticate } } ... } }); replicator.start(); |
El futuro
Si no conoce Couchbase, aproveche nuestra formación gratuita en línea disponible en https://learn.couchbase.com para saber más.
Más tutoriales para móviles en la página Página web de tutoriales de Couchbase.