Feb 21
  • english
  • german

There are a lot of extensions out there which modify the TYPO3 login somehow. Some of them user an LDAP server, some use a X.509 certificate. What I am missing, is the possibilty to use a webservice. Let's assume this webservice already exist, so we won't discuss it here. Let's start in the kickstarter: (you have to download this extension from TER, install it and then choose "create new Extension" in the upper left corner of the extension manager). There you can enter the genral information for your extension and then create a new service. The service type needs to be "auth" and the subtype depends on the functionality the service shall provide. In our case, the service should only request the userdata to prevent us from storing it in the TYPO3 databse. Additionally it should be able to actually login a user. For these functionalities, we need the followig subtypes: "getUserFE,authUserFE,getGroupsFE". For priority and quality, we need a value higher that the normal login service (50), so I chose 60 for both. That was it with the kickstarter. Now save and install the extension and let's start. (further information about the auth service)
Our service class is located in typo3conf/ext/EXTENSIONNAME/sv1/class.tx_XXX_sv1.php. There we have to make our first change: the class needs to extend "tx_sv_authbase" instead of "t3lib_svbase". Now we can start implementing.

The init method actually doesn't need to do much:

function init() {
	$this->writeDevLog = true;
	return parent::init();
}

Of course, we have to write some more methods. During login, the method getGroups is called first to check which usergroups are there. You may insert another webservice call here to retrieve the groups from the server, but I want to keep the example as simple as possible, so we just return dummy data:

function getGroups() {
	$group = array('uid' => array(1),
		'title' => array('nogroup'),
		'pid' => array(0)
	);
	return $group;
}

You will recognize the structure: it is an array containing another 3 arrays which contain the actual data. In the next step the method getUser is called to check whether the user account exists. Normally it was designed to first retrieve the userdata to validate it afterwards but due to fact we have a webservice involved, this would be really insecure because all the data would be sent before the user is even logged in. So we will only get back the userdata if the password was correct. Stupidly, TYPO3 checks the data again with the fe_users-table after we have done it. Therefor we need to store (at least) the id and the username in the local database:

function getUser() {
	$user = false;
	if ($this->ogin['status']=='login' AND $this->login['uident'])	{
		$user = $this->getUserFromWebservice($this->login['uname'], $this->login['uident']);
		if(!is_array($user)) {
			// Failed login attempt (no username found)
		} else {
			//write userdata to internal db
			//DON'T FORGET TO UTF8_DECODE ALL STRING PROPERTIES HERE! (I skip this part in the example)
			$resCount = $GLOBALS['TYPO3_DB']->exec_SELECTquery('uid', 'fe_users', 'uid = '.$user['uid']);
			//leave out password and other critical data!
			$userForDb = array(
				'uid'	=> $user['uid'],
				'username' 	=> $user['username']
			);
			if($GLOBALS['TYPO3_DB']->sql_num_rows($resCount) == 1) {
				//update
				$res = $GLOBALS['TYPO3_DB']->exec_UPDATEquery('fe_users', 'uid = '.$userForDb['uid'], userForDb);
			} else {
				//insert
				$res = $GLOBALS['TYPO3_DB']->exec_INSERTquery('fe_users', $userForDb);
			}
			//if internal db operation failed, login also does
			if(!$res) {
				return false;
			}
		}
	}
	return $user;
}

The actual call to the webservice has been encapsulted here because SOAP only understands utf8 encoded strings and if the username or password contains an umlaut, a SOAP-Fault would be returned. For this reason, we need to utf8-encode all data and the server then has to decode it:

function getUserFromWebservice($username, $password) {
	try {
		//connect
		$soapclient = new SoapClient('ENTER_YOUR_WSDL_PATH_HERE', array('trace' => 1));
		//construct data
		$data = new stdClass();
		$data->username = utf8_encode($username);
		$data->password = utf8_encode($password);
		//login! (we pretend that the service method is called "loginUser")
		$response = $soapclient->loginUser($data);
	} catch(SoapFault $e) {
		trigger_error("SOAP Fault: (faultcode: {$e->faultcode}, faultstring: {$e->faultstring})", E_USER_ERROR);
	}
	//handle response
	return $this->handleWebserviceResponse($response);
}

The response handling has also been encapsulated:

function handleWebserviceResponse($responseObject) {
	if($responseObject->success) {
		return get_object_vars($responseObject->user);
	} else {
		//some error handling may be done here
		return false;
	}
}

I#M sure you need a brief explanation for that: the variable $responseObject is a stdClass object like the input was. It has (our service has been written that way) 3 properties: "success" is a boolean, "errorCode" is an Integer and "user" is a stdClass again with some properties (uid, username, etc). These are converted into an associative array and returned. Then (in the getUser method), the userdata is synchronized into the local fe_users table. The user which is returned by this method, is then passed to "authUser" which is normally responsible for the final login process. In our case, this is not necessarry anymore because the user has already been authenticated. Therefor it is enough to just return true:

function authUser($user) {
	return true;
}

That's it! To test it, quickly install the "newloginbox" extension and try to log in. Please comment on this post if you have any questions. (Download the class)

15 Antworten zu “TYPO3-Login via webservice”

  1. Grundi sagt:

    Da mir das WordPress beim speichern immer die schließenden Klammern (>) in Entities umwandelt (>), und ich keine Lust habe das jedesmal wieder per Hand zu ändern, gibt es den gesamten Code auch als Download am Ende des Posts. Also bitte verzeiht mir kleine Fehler die durch das Speichern des Posts reingekommen sind – Danke!

  2. Torben Dam sagt:

    Nach SOO lange suchen, und 3 Tage.. endlich finde ich hier eine Brauchbare Dokumentation wie’s mit getUser geht.

    DANKE !

    Es fehlt bestimmt Dokumentation über AUTH in Typo3.

  3. Grundi sagt:

    Hallo Torben.

    Freut mich, dass dir der Post geholfen hat. Zugegeben habe ich das aber auch nicht selbst durch probieren rausgefunden, sondern mir eine der LDAP-Extensions heruntergeladen um zu sehen wie es dort gemacht wird. Da habe ich dann alles nötige gefunden. Die Doku zu dem Thema ist, wie du schon sagtest, eher dürftig, aber das ist bei TYPO3 ja öfter mal so (ich möchte mich da selbst nicht ausschließen, meine Extension hat gar keine ^^).

  4. Thomas sagt:

    Hi,
    ich versuche ebenfalls eine Authentifizierung über ein propriäters System zu ermöglichen und stelle fest, dass der Service zu spät geladen wird!

    Die t3libdiv::makeInstanceService() sucht (mehrmals) über t3lib_extMgm::findService() einen geeigneten Service für die Authentifizierung, allerdings findet der t3lib_extMgm::addService() Aufruf meiner Extension zu spät statt, danach gibt es keine findService() Aufrufe mehr?!
    Meinen Service habe ich in der Extensionliste ganz nach vorn gestellt.

    Ich nutze 4.2.1 – irgendeine Idee dazu?

    Grüße,
    Thomas

  5. Thomas sagt:

    Kleines Update: Der Kickstarter legt den addService() Aufruf in die ext_tables.php, er muss allerdings in die ext_localconf.php! Dann klappts auch mit dem rechtzeitigen laden! Juhu!
    Grüße,
    t

  6. Sebastian sagt:

    Hi,
    super Artikel, hat mir sehr geholfen beim entwickeln eines Webservices.
    Mir hat es noch sehr geholfen die DevLog Messages der UserAuthend Klasse zu lesen.
    Anschalten kann man diese über den folgenden Eintrag in der ext_localconf.php:
    $GLOBALS['TYPO3_CONF_VARS']['SC_OPTIONS']['t3lib/class.t3lib_userauth.php']['writeDevLog'] = 1;
    danke und grüsse,
    sep

  7. Uwe sagt:

    Hallo,
    finde die Klasse sehr interessant. Ich möchte sie nutzen um über die Flash Remoting Extension (ryzy_flash_rm) ein Login zu ermöglichen.
    Frage: Wie erstellt man den Webservice der die Methode Login bereitstellt?
    Und Grundsätzlich frage ich mich, weshalb man nicht über das $GLOBALS-Objekt nicht eine methode ansprechen kann, z.B. loginUser($username,$password).

  8. Grundi sagt:

    Hi Uwe.

    Ich habe deine Struktur noch nicht ganz verstanden, wie arbeiten Webservice, Webseite und Flash genau zusammen? Wie man einen Webservice erstellt, kannst du mit “webservice php tutorial” googeln oder in ein Fachbuch schauen, da steht das mit ziemlich ausführlichen Beispielen meist gut erklärt.

    Falls du sonst noch Hilfe brauchst, kannst du mich gerne kontaktieren.

    $GLOBALS ist nicht wirklich ein Objekt, sondern nur eine Sammlung von verschiedenen Dingen, die man sich sonst erst “holen” müsste. Die Methode ist allerdings etwas antiquiert und sollte nicht wirklich verwendet werden. Zusätzlich muss eine neue Loginmethode auch erst am Core registriert werden, sonst kommt das TYPO3 ja ganz durcheinander und sonst könnte auch “jeder” (irgendwo in seinem Quellcode) solche Aktionen durchführen, was der Sicherheit der Anwendung sicher nicht zuträglich sein würde ;-)

    Die (nötige) Struktur mag am Anfang etwas komplex erscheinen, da man nicht alles ausimplementieren muss, relativiert sich das aber auch wieder…

    Ich hoffe, du kommst damit erstmal etwas weiter.
    Viele Grüße,
    Michael

  9. andy kreicy sagt:

    das ffunktioniert ja alles grossartig – ich schreibe den user in die db, bleibe aber nicht eingeloggt – sondern bekomme einen error.
    erst beim zweiten login bin ich eingeloggt als fe_user.

    was muss ich machen um auch schon beim ersten login als FE user angemeldet zu sein?

    lg aus wien
    andy

  10. andy kreicy sagt:

    habs schon!
    // CODE

    if (!$GLOBALS['TSFE']->fe_user) {
    if (!isset($GLOBALS['TYPO3_DB']) || !$GLOBALS['TYPO3_DB']->link) {
    tslib_eidtools::connectDB();
    }
    $GLOBALS['TSFE']->fe_user = tslib_eidtools::initFeUser();
    }
    $GLOBALS['TSFE']->fe_user->checkPid=0; //do not use a particular pid
    $info= $GLOBALS['TSFE']->fe_user->getAuthInfoArray();
    $user=$GLOBALS['TSFE']->fe_user->fetchUserRecord($info['db_user'],$uname);
    $GLOBALS['TSFE']->fe_user->createUserSession($user);
    $GLOBALS['TSFE']->fe_user->user = $user;

    damit forced man den eingeloggten zustand!

  11. Peter sagt:

    Hi,
    habe mich genau an obigen Code gehalten, leider bekomme ich nur bei aktivierten error-messages:
    Fatal error: Class ‘sdtClass’ not found in …… on line 124.
    Wer kann mir bitte weiterhelfen (TYPO3 4.4.4)
    lg
    peter

  12. Grundi sagt:

    Hallo Peter.

    stdClass ist Teil von PHP. Wenn die bei dir nicht geht, dann ist wohl was mit dem PHP im argen. Welche PHP-Version nutzt du denn? Weitere Infos gibts auch unter http://php.net/manual/en/reserved.classes.php

    VG,
    Michael

  13. Peter sagt:

    Hi Michael,
    sorry hat eh funktioniert, muss natürlich “stdClass” statt “sdtClass” heißen – habs aus deinem Download der Klasse, da hast du einen kleinen Druckfehler ;)

    Authentifizierung müsste passen, jetzt hab ich nur noch das Problem, dass ich aus dem wsdl File mittels __soapCall Funktionen auslesen müßte und auf der Website als Content darstellen soll.
    Habe sonst nur eine Endpoint-URL und ein Beispiel-Request für das Service GetContractList, welches in xml geschrieben ist. Leider hab ich sowas noch nie gemacht und wäre über alle Tipps sehr dankbar.
    lg peter

  14. Thomas sagt:

    wie würde es denn funktionieren, wenn ich auf einer anderen Seite als Benutzer eingeloggt bin und über einen Link auf eine Typo3 Seite verweise, auf der ich nun als FE User angemeldet werden soll?

    Würde es funktionieren den Benutzernamen und das Passwort über die URL mit zu geben? (ob das sicher ist oder nicht wäre erstmal egal!)

    Ich verfüge hier nicht über einen Webservice… hätte hierzu jemand eine Idee?

  15. Grundi sagt:

    Hallo Thomas. Ja, auch das geht. Du muss die Parameter “user”, “pass” und “logintype” (Wert = “login”) an eine beliebiege TYPO3-Seite übergeben. Das Login übernimmt der Core, eine sinnvolle Ausgabe kannst du mit dem felogin Plugin (ist Standard) erzeugen.

    Sicher machst du es, indem du eine SSL-Verbindung verwendest.

Einen Kommentar schreiben