I'm having a big problem with Drupal 8, I want Drupal to trust a downstream reverse proxy that performs authentication against a third party provider, then passes a JWT in a X_ID_TOKEN
header.
As I didn't find any module that behaves as an authentication provider that trusts HTTP headers to authenticate the user, I developed a provider myself which is implemented as follows:
public function applies(Request $request) {
// If you return TRUE and the method Authentication logic fails,
// you will get out from Drupal navigation if you are logged in.
$trusted_host = "127.0.0.1";
$headers = $request->server->getHeaders();
$server_addr = $request->server->get('SERVER_ADDR');
return ($server_addr == $trusted_host) && $headers && $headers['X_ID_TOKEN'];
}
public function authenticate(Request $request) {
$headers = $request->server->getHeaders();
$jwt_token = explode('.', $headers['X_ID_TOKEN']);
$user_data = json_decode(base64_decode($jwt_token[1]), true);
if(!$user_data && !$user_data['email']){
return NULL;
}
$users = \Drupal::entityTypeManager()->getStorage('user')
->loadByProperties(['mail' => $user_data['email']]);
$user = reset($users);
if ($user) {
$uid = $user->id();
$rids = $user->getRoles();
$account = User::load($uid);
return $account;
}else{
/** @var \Drupal\user\Entity\User $account */
$account = User::create([
'name' => $this->generate_username($user_data),
'pass' => user_password(),
'mail' => $user_data['email'],
'init' => $user_data['email'],
'status' => 1,
]);
$account->save();
// Store the newly created account.
$this->save_user_info($account, $user_data);
return $account;
}
return NULL;
}
Then the service is registered like this:
services:
authentication.trust_openid_auth:
class: Drupal\trust_openid_auth\Authentication\Provider\OpenIDAuth
tags:
- { name: authentication_provider, provider_id: open_idauth, priority: 0, global: TRUE }
This sort of works, but the pages we are rendering contain user personal data, and they are being cached by some Drupal internal cache. This cache is "per user" thanks to the native Drupal session cookie.
Problem: this cookie seems to be randomly set (when it's not there it randomly appears when navigating). When the session cookie is not set, the cache is global and results in rendering a page that contains the personal information of the last user that successfully rendered the page (not necessarily the current user!)
I have read that the session cookie starts existing when data is attached to the PHP session, which I'm not in control of.
I'm pretty lost at this point, the two solution I thought of are:
- Tell Drupal to trust a remote Cookie (set by the downstream reverse proxy) as the session Cookie, this cookie would contain the user's JWT
- Force the Drupal session Cookie even if there is no data attached to the session, just to contextualize the cache
The first solution would be ideal but I'm not sure how to achieve it. Any help on this would be welcome!