Recomendamos haber hecho la serie de APIs antes de leer este artículo
https://laraveles.com/series/api-con-dingo-y-oauth2/
Caso de uso
Vamos a plantear un caso de uso para este articulo, el escenario es el siguiente:
Una empresa utiliza Active Directory para administrar a sus empleados, a través de este sistema gestiona las cuentas de usuario de los computadores windows, cuentas de correo y perfiles, así como el acceso a diferentes aplicaciones que usan LDAP para autenticar y obtener información del usuario. Quieren crear una aplicación para iOS con varias funcionalidades pero la autenticación del usuario debe hacerse con las credenciales del Active directory y el API debe estar protegido con autenticación oAuth2.
¿Cómo logramos esto? bueno es algo sencillo que vamos a trabajar en este artículo.
Algo de teoría
Que es LDAP?
Según Wikipedia: Siglas de Lightweight Directory Access Protocol (en español Protocolo Ligero/Simplificado de Acceso a Directorios) que hacen referencia a un protocolo a nivel de aplicación que permite el acceso a un servicio de directorio ordenado y distribuido para buscar diversa información en un entorno de red (https://es.wikipedia.org/wiki/Protocolo_Ligero_de_Acceso_a_Directorios)
Esto suena muy técnico y no muy divertido, así que vamos a decir que LDAP es un protocolo por el cual podemos autenticarnos con un directorio activo, que es un sistema de Microsoft utilizado a menudo para la administración de usuarios en una compañía.
Que es oAuth2?
OAuth 2 es la segunda version del protocolo de autenticación OAuth creado en el 2006 (https://es.wikipedia.org/wiki/OAuth) que es utilizado a menudo para autenticación en REST APIs y otros sistemas informáticos. OAuth 2 utiliza Grant Types como mecanismos de autenticación de diferentes formas para dar un Access Token, con dichos grants podemos definir de que forma se autentica un usuario o cliente contra nuestro API. Dependiendo del mecanismo y del cliente que va a solicitar el Access Token para acceder a nuestros recursos, definimos el tipo de grant a utilizar, para eso podemos ver el siguiente diagrama publicado por Alex Bilbie en el paquete de oAuth 2 server de PHP League (http://oauth2.thephpleague.com)
Imagen tomada de http://oauth2.thephpleague.com
Implementación del servidor oAuth2
Como pre requisito para la solución del caso de uso, necesitamos tener implementado un servidor oAuth2 con Laravel, para eso los invito a que vean el workshop API de hecho en Laravel (http://hechoenlaravel.com/w/api-con-dingo-y-oauth2) y una vez tengan implementado el servidor podremos hablar de la creación del nuevo Grant.
Paquetes y extensiones php necesarias.
Para poder conectarnos al directorio activo, necesitamos instalar y activar la extension php-ldap corriendo el siguiente comando en linux
1 | sudo apt-get install php-ldap -y |
Una vez está instalada y activada la extension podemos instalar el paquete Laravel LDAP corriendo el siguiente comando en la carpeta de nuestro proyecto.
1 | composer require adldap2/adldap2-laravel |
Una vez termina la instalación del paquete añadimos los service provider y el Alias en config/app.php
1 2 3 4 5 6 7 8 | 'providers' => [ Adldap\Laravel\AdldapServiceProvider::class, Adldap\Laravel\AdldapAuthServiceProvider::class, ], 'aliases' => [ 'Adldap' => Adldap\Laravel\Facades\Adldap::class, ] |
En el archivo config/auth.php vamos a cambiar el driver por el del paquete
1 | 'driver' => 'adldap', |
Una vez instalado el paquete se debe configurar en base al directorio activo que se va a utilizar, para esto los invito a ver la documentación del paquete https://github.com/Adldap2/Adldap2-Laravel#installation
Creando el Active Directory Grant
Para crear el grant debemos crear una clase que definirá el mismo, como el mecanismo de autenticación es muy parecido al password grant podemos crear la clase que extienda el password grant y cambiamos el identificador, esta clase la podemos crear en app/Auth/Grants
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | namespace App\Auth\Grants; use League\OAuth2\Server\Grant\PasswordGrant; /** * Class ActiveDirectoryGrant * @package App\Auth\Grants */ class ActiveDirectoryGrant extends PasswordGrant { /** * Grant identifier * * @var string */ protected $identifier = 'active_directory'; } |
Ahora debemos crear una clase que se encargará de hacer la verificación de las credenciales proporcionadas en el request, esta clase la podemos ubicar en app/Auth así:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | namespace App\Auth; use Auth; class ActiveDirectoryGrantVerifier { protected $repository; /** * @param $username * @param $password * @return bool */ public function verifyAdGrant($username, $password) { if (Auth::attempt(['username' => $username, 'password' => $password])) { return Auth::user()->id; } return false; } } |
Con estas 2 clases ya podemos registrar el nuevo grant en la configuración de nuestro servidor OAuth2, para esto vamos a abrir el archivo config/oauth2.php y vamos a agregar el grant
1 2 3 4 5 6 7 8 9 10 11 12 | 'grant_types' => [ 'refresh_token' => [ 'class' => '\League\OAuth2\Server\Grant\RefreshTokenGrant', 'access_token_ttl' => 2592000, 'refresh_token_ttl' => 8640000 ], 'active_directory' => [ 'class' => '\App\Auth\Grants\ActiveDirectoryGrant', 'callback' => '\App\Auth\ActiveDirectoryGrantVerifier@verifyAdGrant', 'access_token_ttl' => 2592000 ] ], |
Probando el nuevo Grant
Por último vamos a probar nuestro nuevo grant, para eso escribiremos una prueba que obtenga un Access token proporcionando un usuario del directorio activo con el cual estamos trabajando, así como el client_id, client_secret y grant_type que en nuestro caso será active_directory
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 | /** * If the authorized client request a login with valid credentials it should respond 200 * @test */ public function itValidatesCredentials() { DB::table('oauth_clients')->insert([ 'id' => '12345', 'secret' => '12345', 'name' => 'Testing Env', 'created_at' => Carbon::now()->format('Y-m-d H:i:s'), 'updated_at' => Carbon::now()->format('Y-m-d H:i:s') ]); $this->post('/api/auth/login', [ 'grant_type' => 'active_directory', 'client_id' => "12345", 'client_secret' => "12345", 'username' => 'Fonseca Jose', 'password' => "MySuperSecretPassword" ]); $this->seeStatusCode(200); } |
Mirando la respuesta de este endpoint vemos que obtenemos un Access Token siempre y cuando las credenciales estén correctas
1 2 3 4 5 6 | { "access_token":"VqhZ8B4pngKnCxMaAYAD4BFGIPZ24kuuNdmsOK5F", "token_type":"Bearer", "expires_in":2592000, "refresh_token":"aD3qncYAknmnQltuAyOPyZP8OK2c2Qu8lwiVzdoV" } |
Espero sea de ayuda y feliz programación!