mirror of
https://github.com/friendica/friendica
synced 2025-04-25 03:50:12 +00:00
OpenWebAuth moved to a separate class / Improved authentication handling
This commit is contained in:
parent
b3c7e96b73
commit
55cec6c61d
14 changed files with 595 additions and 410 deletions
|
@ -31,21 +31,14 @@ use Friendica\Core\Logger;
|
|||
use Friendica\Core\Protocol;
|
||||
use Friendica\Core\Renderer;
|
||||
use Friendica\Core\Search;
|
||||
use Friendica\Core\System;
|
||||
use Friendica\Core\Worker;
|
||||
use Friendica\Database\DBA;
|
||||
use Friendica\DI;
|
||||
use Friendica\Network\HTTPClient\Client\HttpClientAccept;
|
||||
use Friendica\Network\HTTPClient\Client\HttpClientOptions;
|
||||
use Friendica\Network\HTTPClient\Client\HttpClientRequest;
|
||||
use Friendica\Network\HTTPException;
|
||||
use Friendica\Network\HTTPException\InternalServerErrorException;
|
||||
use Friendica\Protocol\Activity;
|
||||
use Friendica\Protocol\Diaspora;
|
||||
use Friendica\Security\PermissionSet\Entity\PermissionSet;
|
||||
use Friendica\Util\DateTimeFormat;
|
||||
use Friendica\Util\HTTPSignature;
|
||||
use Friendica\Util\Network;
|
||||
use Friendica\Util\Proxy;
|
||||
use Friendica\Util\Strings;
|
||||
|
||||
|
@ -696,218 +689,6 @@ class Profile
|
|||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Process the 'zrl' parameter and initiate the remote authentication.
|
||||
*
|
||||
* This method checks if the visitor has a public contact entry and
|
||||
* redirects the visitor to his/her instance to start the magic auth (Authentication)
|
||||
* process.
|
||||
*
|
||||
* Ported from Hubzilla: https://framagit.org/hubzilla/core/blob/master/include/channel.php
|
||||
*
|
||||
* The implementation for Friendica sadly differs in some points from the one for Hubzilla:
|
||||
* - Hubzilla uses the "zid" parameter, while for Friendica it had been replaced with "zrl"
|
||||
* - There seem to be some reverse authentication (rmagic) that isn't implemented in Friendica at all
|
||||
*
|
||||
* It would be favourable to harmonize the two implementations.
|
||||
*
|
||||
* @return void
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function zrlInit()
|
||||
{
|
||||
$my_url = DI::userSession()->getMyUrl();
|
||||
$my_url = Network::isUrlValid($my_url);
|
||||
|
||||
if (empty($my_url) || DI::userSession()->getLocalUserId()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$addr = $_GET['addr'] ?? $my_url;
|
||||
|
||||
$arr = ['zrl' => $my_url, 'url' => DI::args()->getCommand()];
|
||||
Hook::callAll('zrl_init', $arr);
|
||||
|
||||
// Try to find the public contact entry of the visitor.
|
||||
$contact = Contact::getByURL($my_url, null, ['id', 'url', 'gsid']);
|
||||
if (empty($contact)) {
|
||||
Logger::info('No contact record found', ['url' => $my_url]);
|
||||
return;
|
||||
}
|
||||
|
||||
if (DI::userSession()->getRemoteUserId() && DI::userSession()->getRemoteUserId() == $contact['id']) {
|
||||
Logger::info('The visitor is already authenticated', ['url' => $my_url]);
|
||||
return;
|
||||
}
|
||||
|
||||
$gserver = DBA::selectFirst('gserver', ['url', 'authredirect'], ['id' => $contact['gsid']]);
|
||||
if (empty($gserver) || empty($gserver['authredirect'])) {
|
||||
Logger::info('No server record found or magic path not defined for server', ['id' => $contact['gsid'], 'gserver' => $gserver]);
|
||||
return;
|
||||
}
|
||||
|
||||
// Avoid endless loops
|
||||
$cachekey = 'zrlInit:' . $my_url;
|
||||
if (DI::cache()->get($cachekey)) {
|
||||
Logger::info('URL ' . $my_url . ' already tried to authenticate.');
|
||||
return;
|
||||
} else {
|
||||
DI::cache()->set($cachekey, true, Duration::MINUTE);
|
||||
}
|
||||
|
||||
Logger::info('Not authenticated. Invoking reverse magic-auth', ['url' => $my_url]);
|
||||
|
||||
// Remove the "addr" parameter from the destination. It is later added as separate parameter again.
|
||||
$addr_request = 'addr=' . urlencode($addr);
|
||||
$query = rtrim(str_replace($addr_request, '', DI::args()->getQueryString()), '?&');
|
||||
|
||||
// The other instance needs to know where to redirect.
|
||||
$dest = urlencode(DI::baseUrl() . '/' . $query);
|
||||
|
||||
if ($gserver['url'] != DI::baseUrl() && !strstr($dest, '/magic')) {
|
||||
$magic_path = $gserver['authredirect'] . '?f=&rev=1&owa=1&dest=' . $dest . '&' . $addr_request;
|
||||
|
||||
Logger::info('Doing magic auth for visitor ' . $my_url . ' to ' . $magic_path);
|
||||
System::externalRedirect($magic_path);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the visitor cookies (see remote_user()) for the given handle
|
||||
*
|
||||
* @param string $handle Visitor handle
|
||||
*
|
||||
* @return array Visitor contact array
|
||||
*/
|
||||
public static function addVisitorCookieForHandle(string $handle): array
|
||||
{
|
||||
$a = DI::app();
|
||||
|
||||
// Try to find the public contact entry of the visitor.
|
||||
$cid = Contact::getIdForURL($handle);
|
||||
if (!$cid) {
|
||||
Logger::info('Handle not found', ['handle' => $handle]);
|
||||
return [];
|
||||
}
|
||||
|
||||
$visitor = Contact::getById($cid);
|
||||
|
||||
// Authenticate the visitor.
|
||||
DI::userSession()->setMultiple([
|
||||
'authenticated' => 0,
|
||||
'visitor_id' => $visitor['id'],
|
||||
'visitor_handle' => $visitor['addr'],
|
||||
'visitor_home' => $visitor['url'],
|
||||
'my_url' => $visitor['url'],
|
||||
'remote_comment' => $visitor['subscribe'],
|
||||
]);
|
||||
|
||||
DI::userSession()->setVisitorsContacts($visitor['url']);
|
||||
|
||||
$a->setContactId($visitor['id']);
|
||||
|
||||
Logger::info('Authenticated visitor', ['url' => $visitor['url']]);
|
||||
|
||||
return $visitor;
|
||||
}
|
||||
|
||||
/**
|
||||
* Set the visitor cookies (see remote_user()) for signed HTTP requests
|
||||
*
|
||||
* @param array $server The content of the $_SERVER superglobal
|
||||
* @return array Visitor contact array
|
||||
* @throws InternalServerErrorException
|
||||
*/
|
||||
public static function addVisitorCookieForHTTPSigner(array $server): array
|
||||
{
|
||||
$requester = HTTPSignature::getSigner('', $server);
|
||||
if (empty($requester)) {
|
||||
return [];
|
||||
}
|
||||
return Profile::addVisitorCookieForHandle($requester);
|
||||
}
|
||||
|
||||
/**
|
||||
* OpenWebAuth authentication.
|
||||
*
|
||||
* Ported from Hubzilla: https://framagit.org/hubzilla/core/blob/master/include/zid.php
|
||||
*
|
||||
* @param string $token
|
||||
*
|
||||
* @return void
|
||||
* @throws \Friendica\Network\HTTPException\InternalServerErrorException
|
||||
* @throws \ImagickException
|
||||
*/
|
||||
public static function openWebAuthInit(string $token)
|
||||
{
|
||||
$a = DI::app();
|
||||
|
||||
// Clean old OpenWebAuthToken entries.
|
||||
OpenWebAuthToken::purge('owt', '3 MINUTE');
|
||||
|
||||
// Check if the token we got is the same one
|
||||
// we have stored in the database.
|
||||
$visitor_handle = OpenWebAuthToken::getMeta('owt', 0, $token);
|
||||
|
||||
if ($visitor_handle === false) {
|
||||
return;
|
||||
}
|
||||
|
||||
$visitor = self::addVisitorCookieForHandle($visitor_handle);
|
||||
if (empty($visitor)) {
|
||||
return;
|
||||
}
|
||||
|
||||
$arr = [
|
||||
'visitor' => $visitor,
|
||||
'url' => DI::args()->getQueryString()
|
||||
];
|
||||
/**
|
||||
* @hooks magic_auth_success
|
||||
* Called when a magic-auth was successful.
|
||||
* * \e array \b visitor
|
||||
* * \e string \b url
|
||||
*/
|
||||
Hook::callAll('magic_auth_success', $arr);
|
||||
|
||||
$a->setContactId($arr['visitor']['id']);
|
||||
|
||||
DI::sysmsg()->addInfo(DI::l10n()->t('OpenWebAuth: %1$s welcomes %2$s', DI::baseUrl()->getHost(), $visitor['name']));
|
||||
|
||||
Logger::info('OpenWebAuth: auth success from ' . $visitor['addr']);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns URL with URL-encoded zrl parameter
|
||||
*
|
||||
* @param string $url URL to enhance
|
||||
* @param bool $force Either to force adding zrl parameter
|
||||
*
|
||||
* @return string URL with 'zrl' parameter or original URL in case of no Friendica profile URL
|
||||
*/
|
||||
public static function zrl(string $url, bool $force = false): string
|
||||
{
|
||||
if (!strlen($url)) {
|
||||
return $url;
|
||||
}
|
||||
if (!strpos($url, '/profile/') && !$force) {
|
||||
return $url;
|
||||
}
|
||||
if ($force && substr($url, -1, 1) !== '/') {
|
||||
$url = $url . '/';
|
||||
}
|
||||
|
||||
$achar = strpos($url, '?') ? '&' : '?';
|
||||
$mine = DI::userSession()->getMyUrl();
|
||||
|
||||
if ($mine && !Strings::compareLink($mine, $url)) {
|
||||
return $url . $achar . 'zrl=' . urlencode($mine);
|
||||
}
|
||||
|
||||
return $url;
|
||||
}
|
||||
|
||||
/**
|
||||
* Get the user ID of the page owner.
|
||||
*
|
||||
|
|
Loading…
Add table
Add a link
Reference in a new issue