« Authentification Frankiz » : différence entre les versions

De WikiBR
(Ajout de code pour Django)
Ligne 100 : Ligne 100 :
</code>
</code>


==Avec Django==
L'interfaçage avec Django n'est pas trivial, pour le faire proprement il faudrait coder un backend d'authentification. En attendant, le code ci dessous est un peu sale mais marche. Il sauve l'username frankiz dans la session, ce qui est raisonnablement safe normalement.
<code python>
def logout(request):
    del request.session["username"]
    request.session.modified = True
    return redirect("/")
 
def fkz_answer(request):
if not "timestamp" in request.GET.keys() or not "response" in request.GET.keys() or not "hash" in request.GET.keys():
return redirect("/login_required/")
response = request.GET.get("response")
ts = request.GET.get("timestamp")
h = request.GET.get("hash")
if abs(int(timestamp()) - int(ts)) > 600:
return redirect("/login_required/")
if hashlib.md5(ts + FKZ_KEY + response).hexdigest() != h:
return redirect("/login_required/")
data = json.loads(response)
request.session['username'] = data['hruid']
return redirect("/")
def fkz_do_login(request):
ts = timestamp()
page = "http://***/fkz_answer/"
r = json.dumps(["names", "email"])
h = hashlib.md5(ts + page + FKZ_KEY + r).hexdigest()
return redirect("http://www.frankiz.net/remote?"+urlencode([('timestamp',ts),('site',page),('hash',h),('request',r)]))
</code>


==Bugs vécu==
==Bugs vécu==

Version du 3 septembre 2014 à 11:52

Frankiz permet à des sites externes (binets en particulier) d'utiliser sa base de données pour authentifier les utilisateurs.

Le webmaster du site en question doit auparavant faire une demande de mise en place de ce système en envoyant un mail à l'équipe frankiz (web@eleves.polytechnique.fr). Il doit alors donner :

  • l'url complète de la page de login de son site
  • les informations auxquelles il souhaite accéder
  • une justification de la demande (et oui, on ne partage évidemment pas notre base de données avec n'importe qui !)

Si la demande est acceptée, un web va alors inscrire ton site dans notre base de donnée de sites externes et générer une clé qu'il va te communiquer. Cette opération se déroule via l'interface d'administration de frankiz. Cette clé va te permettre d'authentifier les échanges d'informations entre frankiz et le site externe.

Ci dessous un exemple commenté de script d'authentification, à mettre sur le site externe :

Code de la page d'identification

<?php
include 'fkz_auth.php';
if(!isset($_GET['response'])){
  frankiz_do_auth();
}
$auth = frankiz_get_response();
// et voila !
// les données sont stockées dans $auth = array(key => value, ...);

Code des fonctions utilisées dans fkz_auth.php

<?php
/**
* Clé secrète fournie par l'équipe frankiz lors de l'inscription du site.
* Cette clé sert à signer les requêtes et à authentifier le site.
*/
$FKZ_KEY = "000";

function frankiz_do_auth(){
   global $FKZ_KEY;
  /**
   * Prendre le timestamp permet d'éviter le rejet de la requête
   */
  $timestamp = time();
  /**
   * url de la page de login, doit correspondre *exactement* à celle entrée dans
   * la base de données de Frankiz (définie lors de l'inscription)
   */
  $site = 'http://monsite/login';
  /**
   * Champ non utile pour l'authentification et retransmis tel quel par frankiz. 
   * Il est prévu pour pouvoir mettre en place un système de redirection après 
   * authentification, vers la page à partir de laquelle le client avait tenté de se connecter.
   */
  $location  = "..." 
  /**
   * Nature de la requête.
   * Fkz renverra ici à la fois les noms de la personne mais aussi ses droits dans différents groupes.
   * Il faut cependant que le site ait les droits sur les informations en question (à définir lors de son inscription).
   */
  $request = json_encode(array('names', 'rights', 'email', 'sport', 'promo', 'photo'));
  
  $hash = md5($timestamp . $site . $FKZ_KEY . $request);
  
  $remote  = 'https://www.frankiz.net/remote?timestamp=' . $timestamp .
      '&site=' . $site .
      '&location=' . $location .
      '&hash=' . $hash .
      '&request=' . $request;
   header("Location:" . $remote);
   exit();
}

function frankiz_get_response(){
   global $FKZ_KEY;
   // Read request
   $timestamp = (isset($_GET['timestamp']) ? $_GET['timestamp'] : 0);
   $response  = (isset($_GET['response'])  ? urldecode($_GET['response'])  : );
   $hash      = (isset($_GET['hash'])      ? $_GET['hash']      : );
   $location  = (isset($_GET['location'])  ? $_GET['location']  : );

   // Frankiz security protocol
   if (abs($timestamp - time()) > 600)
      die("Délai de réponse dépassé. Annulation de la requête");
   if (md5($timestamp . $FKZ_KEY . $response) != $hash)
      die("Session compromise.");
   
   $response = json_decode($response, true);
   $response['location'] = $location;

   // Set empty fields
   $fields = array('hruid',
     'firstname', 'lastname', 'nickname',
     'promo', 'photo', 'location');
   foreach ($fields as $k) {
      if (!isset($response[$k]))
      $response[$k] = ;
   }
   return $response;
}
?>

Avec Django

L'interfaçage avec Django n'est pas trivial, pour le faire proprement il faudrait coder un backend d'authentification. En attendant, le code ci dessous est un peu sale mais marche. Il sauve l'username frankiz dans la session, ce qui est raisonnablement safe normalement.

def logout(request):
    del request.session["username"]
    request.session.modified = True
    return redirect("/") 
 
def fkz_answer(request): 
	if not "timestamp" in request.GET.keys() or not "response" in request.GET.keys() or not "hash" in request.GET.keys(): 
		return redirect("/login_required/")
	response = request.GET.get("response")
	ts = request.GET.get("timestamp")
	h = request.GET.get("hash")

	if abs(int(timestamp()) - int(ts)) > 600:
		return redirect("/login_required/")

	if hashlib.md5(ts + FKZ_KEY + response).hexdigest() != h:
		return redirect("/login_required/")

	data = json.loads(response)
	request.session['username'] = data['hruid']
	return redirect("/")


def fkz_do_login(request):
	ts = timestamp()
	page = "http://***/fkz_answer/"
	r = json.dumps(["names", "email"])
	h = hashlib.md5(ts + page + FKZ_KEY + r).hexdigest()
	return redirect("http://www.frankiz.net/remote?"+urlencode([('timestamp',ts),('site',page),('hash',h),('request',r)]))

Bugs vécu

Si authentification fonctionne sur certains navigateurs, vérifier les variables $_GET car elles sont peut être doublement encodées. Dans ce cas, soit refaire urldecode, soit un

$s = str_replace('%22','"',$s);

Si il y a un problème avec les encodages en \umachinchose pour les caractères accentués, voila une fonction qui peut vous servir (reconversion en UTF-8) :

function jsonClean($s) {
  return preg_replace("/\\\\u([a-f0-9]{4})/e", "iconv('UCS-4LE','UTF-8',pack('V', hexdec('U$1')))",$s);
}

Avec Drupal :

L'implémentation de l'authentification avec un CMS comme Drupal n'est pas forcément évidente. Si vous avez une question, vous pouvez contacter antoine.sauvage@polytechnique.edu