Clave de acceso en usuarios anónimos en Symfony

Buenas,

Tengo una aplicación con dos accesos protegidos (admin y clientes) y un tercer acceso anónimo (público).

/admin
/cliente
/publico

A partir de ahora ese acceso anónimo deberá seguir siendo anónimo pero "no libre", es decir, puede entrar cualquiera pero le tiene que salir un formulario para meter una clave porque quieren separar los datos a pesar de ser "públicos". Esto es lo más peculiar de todo porque no quieren tener registrada a esa gente en su aplicación y lo que harán es distribuir una URL y una clave a la gente que quieren que acceda a los datos.

La separación de datos viene dada por una entidad de la aplicación, así que he añadido un campo clave y un campo url en esa entidad para poder filtrar por url y que quede tal que así:

/publico/acceso1
/publico/acceso2

Por la URL de acceso se a qué registro pertenece y de ahí obtengo la clave de acceso para validar. El problema que tengo es cómo manejar esta chapucilla dentro de la seguridad de symfony. ¿Alguna recomendación?

Respuestas

#1

Buen día, por lo que entiendo me dá la impresión de que necesitas crear un autenticador propio, ya que lo que deseas es autenticar a ciertos usuarios mediante una clave que asumo viene en una url.

Esto se puede resolver usando la autenticación guard de symfony (añadida en symfony 2.8 en adelante):

La idea básica del autenticador guard es poder leer las credenciales de usuario de la forma en que lo requiera la aplicación (form login, header http, $_GET, ...), y con dichas credenciales obtener un usuario (BD, array estatico, usuario dinamico, etc.), que será el que represente al usuario (el humano) conectado en la app.

Un ejemplo muy básico de lo que puedes hacer es:

class ApiKeyAuthenticator extends AbstractGuardAuthenticator
{
    private $em;
 
    public function __construct(EntityManager $em)
    {
        $this->em = $em;
    }
 
    /**
     * Se llama en cada peticion, si retorna distinto de null, 
     * se inicia el proceso de login.
     */
    public function getCredentials(Request $request)
    {
        if (!$key= $request->query->get('clave-secreta')) {
            // si no viene la clave secreta en la peticion, no hacemos nada.
            return;
        }
 
        // retornamos la clave secreta (lo cual provoca que se inicie el proceso de login).
        return $key;
    }
 
    public function getUser($key, UserProviderInterface $userProvider)
    {
        // Si este metodo retorna null, se entiende que no existe 
        // el usuario y la autenticación falla.
 
        // Consultamos el usuario por un campo key por ejemplo:
        return $this->em->getRepository('AppBundle:User')->findOneByKey($key);
 
        // En el ejemplo se usa la entidad user, pero puede ser cualquier 
        // otra o hasta un dato estatico:
        return new User('usuario con clave secreta');
    }
 
    public function checkCredentials($key, UserInterface $user)
    {
        // Acá debemos retornar true si no queremos chequear nada
        return true; // si devolvemos otra cosa que no sea true o no devolvemos nada, el login falla.
 
        // Si por ejemplo el usuario fuese el de la parte inferior del método getUser,
        // Acá validariamos que la clave secreta sea válida.
        if (in_array($key, ['123', '456', '789'])){
            return true;
        }
 
        return false;
    }
 
    public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey){}
    public function onAuthenticationFailure(Request $request, AuthenticationException $exception){}
    public function start(Request $request, AuthenticationException $authException = null){}
    public function supportsRememberMe() { return false; }
}

Como se ve el ejemplo consta de 3 métodos principales, getCredentials, getUser, y checkCredentials, cada uno con una lógica muy sencilla para poder hacer login de usuarios para casos muy peculiares.

Además hay otros métodos que no quise agregar documentar porque sería complicar más la idea, pero que en la documentación oficial explican bastante bien.

Quedaría pendiente que leyeras en la documentación como se configura la seguridad de la aplicación para usar dicho autenticador.

Espero te sea de ayuda el ejemplo, y cualquier cosa comenta por acá mismo.

Saludos!

#2

Yo también tiraría por el nuevo componente de seguridad Guard porque es muy sencillo y te permite hacer todo. En esta presentación puedes ver varios ejemplos reales de cómo implementar con Guard diferentes mecanismos ed autenticación.

A modo de referencia adicional, el bundle JmikolaAutoLoginBundle está medio relacionado, ya que permite loguearse automáticamente en una aplicación simplemente visitando un determinado enlace.