diff --git a/.gitattributes b/.gitattributes
index 9c2550989518412877ae7ae8bff1266ae1d0e8db..3ade8d4fc524f20243eae28b438ff3e12b93b4cd 100644
--- a/.gitattributes
+++ b/.gitattributes
@@ -1726,6 +1726,7 @@ library/Class/MultiUpload/HandlerFactory.php -text
 library/Class/MultiUpload/HandlerForm.php -text
 library/Class/MultiUpload/HandlerXhr.php -text
 library/Class/Multimedia.php -text
+library/Class/Multimedia/AuthenticateRequest.php -text
 library/Class/Multimedia/Device.php -text
 library/Class/Multimedia/DeviceGroup.php -text
 library/Class/Multimedia/DeviceHold.php -text
diff --git a/application/modules/opac/controllers/AbonneController.php b/application/modules/opac/controllers/AbonneController.php
index a301644dbe8a41a07b61747ecd33b05e087da0ad..811e2904c8a290962d44be9c181599dc6ce616a9 100644
--- a/application/modules/opac/controllers/AbonneController.php
+++ b/application/modules/opac/controllers/AbonneController.php
@@ -500,39 +500,28 @@ class AbonneController extends Zend_Controller_Action {
 		$response->auth = 0;
 		$response->until = '';
 
-		if (!($login = $this->_getParam('login'))
-				|| !($password = $this->_getParam('password'))
-				|| !($poste = $this->_getParam('poste'))) {
-			$response->error = 'MissingParameter';
+		$request = Class_Multimedia_AuthenticateRequest::newWithRequest($this->_request);
+		if (!$request->isValid()) {
+			$response->error = $request->getError();
 			$this->_response->setBody(json_encode($response));
 			return;
 		}
-		
-		if (!$user = Class_Users::getLoader()->findFirstBy(array('login' => $login))) {
-			$response->error = 'UserNotFound';
-			$this->_response->setBody(json_encode($response));
-			return;
-		}
-
-		if (($user->getPassword() !== $password)) {
-			$response->error = 'PasswordIsWrong';
-			$this->_response->setBody(json_encode($response));
-			return;
-	  }
 
-		if (!$user->isAbonnementValid()) {
-			$response->error='SubscriptionExpired';
-			$this->_response->setBody(json_encode($response));
-			return;
-    }
-		
-		foreach(array('id', 'login', 'password', 'nom', 'prenom') as $attribute) {
+		$user = $request->getUser();
+		foreach (array('id', 'login', 'password', 'nom', 'prenom') as $attribute) {
 			$response->$attribute = $user->$attribute;
 		}
 
 		$response->groupes = $user->getUserGroupsLabels();
 		$response->date_naissance = $user->getDateNaissanceIso8601();
 
+		if (null != ($device = $request->getDevice())
+			and null != ($hold = Class_Multimedia_DeviceHold::getLoader()->getCurrentHoldOfUserOnDevice($user, $device))
+		) {
+			$response->auth = 1;
+			$response->until = date('c', $hold->getEnd());
+		}
+
 		$this->_response->setBody(json_encode($response));
 	}
 
diff --git a/library/Class/Multimedia/AuthenticateRequest.php b/library/Class/Multimedia/AuthenticateRequest.php
new file mode 100644
index 0000000000000000000000000000000000000000..9397759505400b015acb556fa628da06f6a098fa
--- /dev/null
+++ b/library/Class/Multimedia/AuthenticateRequest.php
@@ -0,0 +1,114 @@
+<?php
+/**
+ * Copyright (c) 2012, Agence Française Informatique (AFI). All rights reserved.
+ *
+ * AFI-OPAC 2.0 is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE as published by
+ * the Free Software Foundation.
+ *
+ * There are special exceptions to the terms and conditions of the AGPL as it
+ * is applied to this software (see README file).
+ *
+ * AFI-OPAC 2.0 is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU AFFERO GENERAL PUBLIC LICENSE for more details.
+ *
+ * You should have received a copy of the GNU AFFERO GENERAL PUBLIC LICENSE
+ * along with AFI-OPAC 2.0; if not, write to the Free Software
+ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301  USA 
+ */
+
+class Class_Multimedia_AuthenticateRequest {
+	/** @var boolean */
+	protected $_valid = false;
+
+	/** @var string */
+	protected $_error = '';
+
+	/** @var Class_Users */
+	protected $_user;
+
+	/** @var Class_Multimedia_Device */
+	protected $_device;
+
+
+	/**
+	 * @param Zend_Controller_Request_Abstract
+	 * @return Class_Multimedia_AuthenticateRequest
+	 */
+	public static function newWithRequest($request) {
+		$instance = new self();
+		return $instance->validate($request);
+	}
+
+
+	/**
+	 * @param Zend_Controller_Request_Abstract
+	 * @return Class_Multimedia_AuthenticateRequest
+	 */
+	public function validate($request) {
+		if (!($login = $request->getParam('login'))
+			|| !($password = $request->getParam('password'))
+			|| !($poste = $request->getParam('poste'))
+			|| !($site = $request->getParam('site'))) {
+			$this->_error = 'MissingParameter';
+			return $this;
+		}
+
+		if (!$user = Class_Users::getLoader()->findFirstBy(array('login' => $login))) {
+			$this->_error = 'UserNotFound';
+			return $this;
+		}
+
+		if (($user->getPassword() !== $password)) {
+			$this->_error = 'PasswordIsWrong';
+			return $this;
+	  }
+
+		if (!$user->isAbonnementValid()) {
+			$this->_error = 'SubscriptionExpired';
+			return $this;
+    }
+
+		$this->_user = $user;
+		
+		if ($location = Class_Multimedia_Location::getLoader()->findByIdOrigine($site))
+			$this->_device = Class_Multimedia_Device::getLoader()
+				->findByIdOrigineAndLocation($poste, $location);
+
+		return $this->beValid();
+	}
+	
+
+	/** @return boolean */
+	public function isValid() {
+		return $this->_valid;
+	}
+
+
+	/** @return Class_Multimedia_AuthenticateRequest */
+	public function beValid() {
+		$this->_valid = true;
+		return $this;
+	}
+
+
+	/** @return string */
+	public function getError() {
+		return $this->_error;
+	}
+
+
+	/** @return Class_Users */
+	public function getUser() {
+		return $this->_user;
+	}
+
+
+	/** @return Class_Multimedia_Device */
+	public function getDevice() {
+		return $this->_device;
+	}
+}
+?>
\ No newline at end of file
diff --git a/library/Class/Multimedia/Device.php b/library/Class/Multimedia/Device.php
index 4670ad50eb04f29669c12f4177fd0490364b2b4a..e4d1b79898aff07b0e932b8c735f16cb30117ec3 100644
--- a/library/Class/Multimedia/Device.php
+++ b/library/Class/Multimedia/Device.php
@@ -26,9 +26,8 @@ class Multimedia_DeviceLoader extends Storm_Model_Loader {
 	 * @return Class_Multimedia_DeviceGroup
 	 */
 	public function fromJsonModelWithGroup($json_model, $device_group) {
-		$id_origine = $device_group->getLocation()->getId() . '-' . $json_model->id;
-		if (!$model = $this->findFirstBy(array('id_origine' => $id_origine)))
-			$model = $this->newInstance()->setIdOrigine($id_origine);
+		if (!$model = $this->findByIdOrigineAndLocation($json_model->id, $device_group->getLocation()))
+			$model = $this->newInstance()->setIdOrigine($this->getIdOrigineWithLocation($json_model->id, $device_group->getLocation()));
 		$model
 				->setLibelle($json_model->libelle)
 				->setOs($json_model->os)
@@ -37,6 +36,26 @@ class Multimedia_DeviceLoader extends Storm_Model_Loader {
 				->save();
 		return $model;
 	}
+
+
+	/**
+	 * @param $id int
+	 * @param $location Class_Multimedia_Location
+	 * @return Class_Multimedia_Device
+	 */
+	public function findByIdOrigineAndLocation($id, $location) {
+		return $this->findFirstBy(array('id_origine' => $this->getIdOrigineWithLocation($id, $location)));
+	}
+
+
+	/**
+	 * @param $id int
+	 * @param $location Class_Multimedia_Location
+	 * @return string
+	 */
+	public function getIdOrigineWithLocation($id, $location) {
+		return $location->getId() . '-' . (int)$id;
+	}
 }
 
 
@@ -107,4 +126,10 @@ class Class_Multimedia_Device extends Storm_Model_Abstract {
 	public function getGroupLibelle() {
 		return $this->getGroup()->getLibelle();
 	}
+
+
+	/** @return int */
+	public function getAuthDelay() {
+		return $this->getGroup()->getAuthDelay();
+	}
 }
\ No newline at end of file
diff --git a/library/Class/Multimedia/DeviceGroup.php b/library/Class/Multimedia/DeviceGroup.php
index c4fa27b60decac7d98dfcdcebcf6297ffe3f580c..0988bf516fcec17d21638f4e469530993c19456a 100644
--- a/library/Class/Multimedia/DeviceGroup.php
+++ b/library/Class/Multimedia/DeviceGroup.php
@@ -79,4 +79,10 @@ class Class_Multimedia_DeviceGroup extends Storm_Model_Abstract {
 		}
 		return $holdables;
 	}
+
+
+	/** @return int */
+	public function getAuthDelay() {
+		return $this->getLocation()->getAuthDelay();
+	}
 }
\ No newline at end of file
diff --git a/library/Class/Multimedia/DeviceHold.php b/library/Class/Multimedia/DeviceHold.php
index 30bd68a3cfec323f634bf2f8748b6f746cb47d91..a7d711fb8386d7fa74ebdbe6d4ff6f60ea3cc1ed 100644
--- a/library/Class/Multimedia/DeviceHold.php
+++ b/library/Class/Multimedia/DeviceHold.php
@@ -69,6 +69,28 @@ class Multimedia_DeviceHoldloader extends Storm_Model_Loader {
 	}
 
 
+	/**
+	 * @param $user Class_Users
+	 * @param $device Class_Multimedia_Device
+	 * @return Class_Multimedia_DeviceHold
+	 */
+	public function getCurrentHoldOfUserOnDevice($user, $device) {
+		$min_start = $start = time();
+		$min_start -= 60 * $device->getAuthDelay();
+		$holds = $this->findAll($this->getTable()->select()
+			                       ->where('id_user = ' . $user->getId())
+			                       ->where('id_device = ' . $device->getId())
+			                       ->where('start >= ' . $min_start)
+			                       ->where('start <= ' . $start));
+
+		if (count($holds) == 0)
+			return null;
+
+		$this->cacheInstance($holds[0]);
+		return $holds[0];
+	}
+
+
 	/**
 	 * @param $start int
 	 * @param $end int
diff --git a/library/Class/Multimedia/Location.php b/library/Class/Multimedia/Location.php
index a5b136b6f5004bf0ee3846b5b9e96142cad35860..ca9e7eeb49de3549f4a7ffc9685b0d09ad6e73e6 100644
--- a/library/Class/Multimedia/Location.php
+++ b/library/Class/Multimedia/Location.php
@@ -63,11 +63,20 @@ class Multimedia_LocationLoader extends Storm_Model_Loader {
 	 * @return Class_Multimedia_Location
 	 */
 	public function fromJsonModel($json_model) {
-		if (!$model = $this->findFirstBy(array('id_origine' => (int)$json_model->id)))
+		if (!$model = $this->findByIdOrigine($json_model->id))
 			$model = $this->newInstance()->setIdOrigine((int)$json_model->id);
 		$model->setLibelle($json_model->libelle)->save();
 		return $model;
 	}
+
+
+	/**
+	 * @param int
+	 * $return Class_Multimedia_Location
+	 */
+	public function findByIdOrigine($id) {
+		return $this->findFirstBy(array('id_origine' => (int)$id));
+	}
 }
 
 
diff --git a/tests/application/modules/opac/controllers/AbonneControllerMultimediaTest.php b/tests/application/modules/opac/controllers/AbonneControllerMultimediaTest.php
index 5b6575aedcd1c00b3f7e029cec614199f573e145..48d48b2d7cae828b2a4fdcd776cec0a45bc58412 100644
--- a/tests/application/modules/opac/controllers/AbonneControllerMultimediaTest.php
+++ b/tests/application/modules/opac/controllers/AbonneControllerMultimediaTest.php
@@ -111,9 +111,16 @@ class AbonneControllerMultimediaAuthenticateTest extends AbstractControllerTestC
 	}
 
 
+	/** @test */
+	public function withoutSiteShouldReturnErrorMissingParameter() {
+		$json = $this->getJson('/abonne/authenticate/login/laurent/password/poste/1');
+		$this->assertEquals('MissingParameter', $json->error);
+	}
+
+
 	/** @test */
 	public function getAbonneZorkShouldReturnErrorUserNotFound() {
-		$json= $this->getJson('/abonne/authenticate/login/zork/password/toto/poste/1');
+		$json= $this->getJson('/abonne/authenticate/login/zork/password/toto/poste/1/site/1');
 		$this->assertEquals("UserNotFound", $json->error);
 		
 	}
@@ -121,14 +128,26 @@ class AbonneControllerMultimediaAuthenticateTest extends AbstractControllerTestC
 
 	/** @test */
 	public function authenticateAbonneLaurentPasswordXXXShouldReturnWrongPassword() {
-		$json=$this->getJson('/abonne/authenticate/login/laurent/password/xxx/poste/1');
+		$json=$this->getJson('/abonne/authenticate/login/laurent/password/xxx/poste/1/site/1');
 		$this->assertEquals("PasswordIsWrong", $json->error);	
 	}
 
 	
 	/** @test */
 	public function rightAuthenticationShouldNotReturnError() {
-		$json = $this->getJson('/abonne/authenticate/login/laurent/password/afi/poste/1');
+		Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Multimedia_Location')
+				->whenCalled('findByIdOrigine')
+				->answers(Class_Multimedia_Location::getLoader()->newInstanceWithId(1));
+				
+		Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Multimedia_Device')
+				->whenCalled('findByIdOrigineAndLocation')
+				->answers(Class_Multimedia_Device::getLoader()->newInstanceWithId(1));
+				
+		Storm_Test_ObjectWrapper::onLoaderOfModel('Class_Multimedia_DeviceHold')
+				->whenCalled('getCurrentHoldOfUserOnDevice')
+				->answers(Class_Multimedia_DeviceHold::getLoader()->newInstanceWithId(333)
+					->setEnd(strtotime('2012-09-09 16:40:00')));
+		$json = $this->getJson('/abonne/authenticate/login/laurent/password/afi/poste/1/site/1');
 		$this->assertFalse(property_exists($json, 'error'));
 		return $json;
 	}
@@ -194,25 +213,43 @@ class AbonneControllerMultimediaAuthenticateTest extends AbstractControllerTestC
 	public function laurentGroupeShoudBeAdulteAbonneAdminAndAgile($json) {
 		$this->assertEquals(array('adulte','abonne','admin_bib', 'Devs agiles'), $json->groupes);
 	}
-	
-	
+
+
+	/**
+	 * @test 
+	 * @depends rightAuthenticationShouldNotReturnError
+	 */
+	public function laurentShouldHaveHold($json) {
+		$this->assertEquals(1, $json->auth);
+	}
+
+
+	/**
+	 * @test 
+	 * @depends rightAuthenticationShouldNotReturnError
+	 */
+	public function laurentHoldShouldLastUntil16h40($json) {
+		$this->assertEquals('2012-09-09T16:40:00+02:00', $json->until);
+	}
+
+		
 	/** @test */
 	public function baptisteGroupesShouldBeMineurAbonneAndOldSchool(){
-		$json = $this->getJson('/abonne/authenticate/login/baptiste/password/afi/poste/1');
+		$json = $this->getJson('/abonne/authenticate/login/baptiste/password/afi/poste/1/site/1');
 		$this->assertEquals(array('mineur','abonne_sigb', 'Devs Oldschool'), $json->groupes);	
 	}
 	
 	
 	/** @test */
 		public function mireilleAuthenticateShouldReturnSubscriptionExpired(){
-		$json=$this->getJson('/abonne/authenticate/login/mireille/password/afi/poste/1');
+		$json=$this->getJson('/abonne/authenticate/login/mireille/password/afi/poste/1/site/1');
 		$this->assertEquals('SubscriptionExpired',$json->error);	
 	}
 	
 
 	/** @test */
 	public function arnaudGroupesShouldBeInviteAndPatrons() {
-		$json=$this->getJson('/abonne/authenticate/login/arnaud/password/lelache/poste/1');
+		$json=$this->getJson('/abonne/authenticate/login/arnaud/password/lelache/poste/1/site/1');
 		$this->assertEquals(array('invite', 'Patrons'), $json->groupes);	
 	}