The last time I did this I wrote a very small extension for Flarum that:
- adds an API route. This API route would be used by Laravel as a pre-authentication request. That endpoint then returns a token for Laravel for a specific user based by the input provided by Laravel.
- adds a forum route that users from Laravel are redirected to with that token so that it matches the token against a user and logs them in.
Disclaimer: the following snippets are from a pre-stable Flarum install.
the api controller
<?php
namespace App\Http\Controllers\Auth;
use Flarum\User\AssertPermissionTrait;
use Flarum\User\RegistrationToken;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response\JsonResponse;
class FlagrowCallbackController implements RequestHandlerInterface
{
use AssertPermissionTrait;
public function handle(ServerRequestInterface $request): ResponseInterface
{
$actor = $request->getAttribute('actor');
$this->assertAdmin($actor);
$email = array_get($request->getParsedBody(), 'email');
$token = RegistrationToken::generate(
'flagrow',
$email,
[],
(array) $request->getParsedBody()
);
$token->save();
return new JsonResponse([
'token' => $token->token
]);
}
}
the forum controller
<?php
namespace App\Http\Controllers\Auth;
use Flarum\Api\Client;
use Flarum\Foundation\Application;
use Flarum\Http\AccessToken;
use Flarum\Http\Exception\TokenMismatchException;
use Flarum\Http\SessionAuthenticator;
use Flarum\User\Event\LoggedIn;
use Flarum\User\RegistrationToken;
use Flarum\User\User;
use Psr\Http\Message\ResponseInterface;
use Psr\Http\Message\ServerRequestInterface;
use Psr\Http\Server\RequestHandlerInterface;
use Zend\Diactoros\Response\EmptyResponse;
use Zend\Diactoros\Response\RedirectResponse;
class FlagrowReturnedController implements RequestHandlerInterface
{
/**
* @var SessionAuthenticator
*/
private $authenticator;
/**
* @var Client
*/
private $api;
/**
* @var Application
*/
private $app;
public function __construct(SessionAuthenticator $authenticator, Client $api, Application $app)
{
$this->authenticator = $authenticator;
$this->api = $api;
$this->app = $app;
}
public function handle(ServerRequestInterface $request): ResponseInterface
{
/** @var \Illuminate\Session\Store $session */
$session = $request->getAttribute('session');
$token = array_get($request->getQueryParams(), 'token');
/** @var RegistrationToken $token */
$token = RegistrationToken::findOrFail($token);
$user = User::where('email', $token->identifier)->firstOrFail();
$accesstoToken = AccessToken::generate($user->id, 3600);
$accesstoToken->save();
$this->authenticator->logIn($session, $user->id);
event(new LoggedIn($user, $accesstoToken));
return new RedirectResponse($this->app->url());
}
}
The next step would be to build the things on the side of Laravel.
laravel controller to redirect user to flarum
<?php
namespace App\Http\Controllers\Web\Connect;
use App\Events\Forum\ForumUserCreated;
use App\Http\Controllers\Controller;
use App\Services\Forum;
use App\User;
use GuzzleHttp\Exception\ClientException;
use Illuminate\Http\Request;
use Illuminate\Validation\UnauthorizedException;
use Zend\Diactoros\Response\EmptyResponse;
class ForumController extends Controller
{
/**
* @var Forum
*/
private $forum;
public function __construct(Forum $forum)
{
$this->forum = $forum;
}
public function __invoke(Request $request)
{
logger("attempted log in to forum", $request->toArray());
/** @var User $user */
$user = $request->user('web');
if (! $user) {
throw new UnauthorizedException;
}
$forumUser = null;
try {
$forumUser = $this->forum->findUserByUsername($user->nickname);
} catch (ClientException $e) {
if ($e->getResponse()->getStatusCode() !== 404) {
throw $e;
}
}
if (! $forumUser) {
$forumUser = $this->forum->createActivatedUser([
'email' => $user->email,
'username' => $user->nickname,
'password' => str_random(40),
]);
logger("User with mail address {$user->email} created on forum");
event(new ForumUserCreated($user, $forumUser));
} else if ($forumUser) {
logger("User with mail address {$user->email} already existed");
} else {
throw new \InvalidArgumentException("Cannot retrieve or create forum user");
}
$token = $this->forum->preAuth($user->email, $user->nickname);
if ($token) {
return redirect(config('forum.url') . '/authed/flagrow/' . $token);
}
return new EmptyResponse(401);
}
}
The Forum
class seems to be a simple wrapper around the deprecated flagrow/flarum-api-client
now adopted by Maicol07, see https://discuss.flarum.org/d/24880