Etat des lieux

Dans un projet récent nous avions une problématique d’authentification depuis des applications mobiles en utilisant une API REST conçu sur mesure.
Le contexte du projet était le suivant :
– Framework symfony2
– Utilisation de FOSUserBundle pour la gestion des utilisateur
– Utilisation de FOSFaceBookBundle pour l’intégration avec Facebook.

Tout ceci pour permettre aux utilisateurs de se connecter au back-office.

Le but de cet article n’est pas de faire un tutoriel sur l’installation et configuration de FOSUserBundle (disponible et déjà très bien expliqué ici ), ni de son intégration avec FOSFacebookBundle ( tout est également expliqué ici).

En plus de ce site web, nous avions une application mobile (android & iphone) devant récupérer certaines données depuis une API REST (avec le bundle FOSRestBundle).
C’est là que FOSOAuthBundle entre en jeux.

Nous avons choisi d’utiliser un token d’authentification (avec une durée de validité limitée) pour tous les appels entre le mobile et l’API REST.
Ce n’est pas le but direct d’OAuth (qui comme son nom l’indique est là pour faire une authentification oauth2), ce bundle en fait beaucoup trop pour notre besoin.
Cependant, à terme ce projet doit évoluer, il est possible qu’il prenne de l’importance et que nous ayons donc pleinement besoin de ce bundle.
C’est pourquoi nous l’avons installé dans le projet, tout en limitant sont utilisation.

Communication entre le mobile et l’API

Voici un résumé des échanges entres l’application mobile et l’api :

| Application mobile |
<<————————->>
| API |
Demande de token (avec login + mdp)
 —————————>>
Authentification de l’utilisateur
Stockage
<<—————————
envoie d’un token
Demande de données (avec token)
—————————>>
Vérification du token
Récupération des données
<<—————————  
Envois des données

Voici un aperçu du service qui reçoit la demande de token (avec le login et le mdp de l’utilisateur):

[php]

// Création d’un token d’authentification "classique"
//et "validation" de celui ci
$token = new UsernamePasswordToken($username, $password, ‘main’);
$authToken = $this->authManager->authenticate($token);

//utilisation des services fournit par FOSOAuthBundle
//pour créer un "acces_token" à durée limitée
$client = $this->oauthClientManager->createClient();
$this->oauthClientManager->updateClient($client);

$this->oauthServer->setVariable(OAuth2OAuth2::CONFIG_ACCESS_LIFETIME, $lifetime);

$access_token = $this->oauthServer->createAccessToken($client, $authToken->getUser());

[/php]

Authentification avec le facebook connect et l’api

Dernière étape,  faire une demande de token à l’api en authentifiant l’utilisateur gràce à Facebook connect.

Pour se faire, l’application mobile et le site web partage la même application facebook.
L’application mobile fais donc une demande d’autorisation classique en utilisant les api Facebook. Celle-ci récupère une token d’authentification de la part de facebook.
C’est ce token que l’on passe à l’api dans l’appel de demande de token.

Voici le code pour l’authentification facebook dans notre service :

[php]

// utilisation des services de FOSFacebookBundle
//pour authentifier ou créer l’utilisateur avec le FConnect
$token = new FacebookUserToken(‘main’ );
$this->facebookApi->setAccessToken($facebook_token);
$authToken = $this->facebookAuth->authenticate($token);

[/php]

Voici le code complet de la création d’un token d’authentification par login/mdp ou facebook connect :

[php]

public function authenticate ($username,$password,$fb_token,$lifetime){
$access_token = "";
try {
if ($fb_token != null ){
$authToken = $this->facebookAuth($fb_token);
}else if ($username != null && $password != null ){
$authToken = $this->userAuth($username,$password);
}else {
$message = $this->translator->trans(‘error.auth.unknown’, array(), ‘ApiBundle’);
throw new Exception($message,400);
}

$client = $this->oauthClientManager->createClient();
$this->oauthClientManager->updateClient($client);

$this->oauthServer->setVariable(OAuth2OAuth2::CONFIG_ACCESS_LIFETIME, $lifetime);
$access_token = $this->oauthServer->createAccessToken($client, $authToken->getUser());

} catch (Exception $ex){
$this->logger->err("Erreur d’authentification ".$ex->getMessage());
throw new Exception($ex->getMessage(),$ex->getCode());
}

return $access_token;
}

/**
*
* @param type $username
* @param type $password
* @return type
* @throws AuthenticationException
*/
public function userAuth($username,$password){

try {
$token = new UsernamePasswordToken($username, $password, ‘main’);
$authToken = $this->authManager->authenticate($token);
}catch (SymfonyComponentSecurityCoreExceptionAccountStatusException $ex){
$message = $this->translator->trans(‘error.auth.user_disabled’, array(), ‘ApiBundle’);
throw new AuthenticationException($message,406,$ex);
}catch (Exception $ex){
$message = $this->translator->trans(‘error.auth.user’, array(), ‘ApiBundle’);
throw new AuthenticationException($message,403,$ex);
}
return $authToken;
}

/**
*
* @param type $facebook_token
* @return type
* @throws AuthenticationException
*/
public function facebookAuth($facebook_token){
try {
$token = new FacebookUserToken(‘main’ );
$this->facebookApi->setAccessToken($facebook_token);

$authToken = $this->facebookAuth->authenticate($token);
}catch (Exception $ex){
$message = $this->translator->trans(‘error.auth.facebook’, array(), ‘ApiBundle’);
throw new AuthenticationException($message, null,403,$ex);
}

return $authToken;
}

[/php]