4

I am experimenting with Drupal 8 as our customer websites. Our customers authenticate through our own authentication application at the moment which speaks to our document store (instead of MySQL) to authenticate a user and provide them with a unique session ID (JWT eventually but that's another day and conversation) which we can use to then query a REST API and get user data in any of our other self apps.

We are moving over from an old JSP based websites to drupal as our apps are now written in Symfony 3 but want our customer websites to be Drupal 8.

Here's the bit I am trying to work out. If I authenticate in our old website I want to be able to redirect to the Drupal 8 website with the session ID we have in our hand and use that to fetch a object back of our logged in user. I have this bit working fine but I now am in a position to say... Ok I have the user object back, the 3rd party service has said that session ID is valid so we know we are authenticated.

Please refer to the below flow chart. I want to be able to also authenticate in Drupal 8 manually. Is this possible (I am sure it is) and if so can someone point me in the right direction as to what I need/should be doing, API's I should be calling?

enter image description here

Thank you kindly and good day :)

Kal
  • 2,239
  • 6
  • 36
  • 74

2 Answers2

4

You should use the External Auth module.

A good exemple of how use this module is the SimpleSamlPHP Auth

1

Ok so it turned out not to be that tricky in the end. I thought I would have to extend and implement various class and create my own provider (which is probably the best practice) but for KISS sake I found another way.

Create a user first if one does not exists based on the user data I get back from my external service. Then pass that created user to the user_login_finalize method (why are a lot of methods underscored Drupal...) which then authenticated my user.

public function inbound(Request $request)
{
    // Point the guzzle client to our external session service.
    $client = new GuzzleHttpClient([
        'base_uri' => 'https://myexternalservice.com/apps/authentication/2/',
    ]);

    // Attempt to send to request with the session ID from the parameters.
    try {
        $response = $client->request('GET', 'api/v1/user/' . $request->get('session_id'));
    } catch (\Exception $e) {
        throw new \HttpException($e->getMessage());
    }

    // Convert the response to an array.
    $result = json_decode((string) $response->getBody(), true);

    // Convert our array to a user entity.
    if ($user = $this->convertResponseToUser($result['user'])) {
        try {
            // Attempt to load the user. If the user does not exist then create them first.
            if (!$assumeUser = user_load_by_mail($user->getEmail())) {
                // Create a Drupal user object.
                $assumeUser = $this->createUser([
                    'name' => $user->getFirstName() . ' ' . $user->getLastName(),
                    'mail' => $user->getEmail()
                ]);

                $assumeUser->save();
            }

            // Authenticate the user.
            user_login_finalize($assumeUser);
        } catch (\Exception $e) {
            drupal_set_message(t('An unhandled exception occurred during authentication.'), 'error');
            return $this->redirect('user.login');
        }
    }

    return $this->redirect('mymodule.route');
}
Kal
  • 2,239
  • 6
  • 36
  • 74
  • 3
    The underscored functions are procedural functions and a coding standard set forth a long time ago. Did you use External Auth for this? Where does `inbound` live? – Kevin Mar 30 '17 at 15:37
  • 2
    Just wanted to follow up here after doing this myself - if you implement your own class implementing UserAuthInterface, you do not need to call `user_login_finalize` directly if you are letting users use the core Drupal login form to login. This will happen for you. Otherwise, if you are looking for a session id in the URL or a cookie, without going to a login form, then yes you will need to make that call. – Kevin Jul 02 '17 at 17:31
  • 1
    I'm interested in trying out this solution but the same questions as Kevin crossed my mind: Was External Auth used for this? Where this code exactly lives? inside a custom module perhaps? What should be the current class name to be extended. Any additional detail greatly appreciated. I would like to start with a custom module for it but if your solution was other option i would be good to try that way. thanks in advance. I'm used to using Guzzle library and looks like an interesting way to solve it. Only need a bit more context :) – lithiumlab Jul 18 '18 at 23:08