0

We have a Joomla site and it works smoothly. However, we need to implement SSO and we have a #__sso_identities table too, which we would like to check for.

Current algorithm (pseudo-code)

if user is found in #__users then
    log in
else
    respond with error
end if

What we would need instead:

if user is found in #__users then
    log in
else if user is found in #__sso_identities then
    do some things (here we would like to inject our SSO code for identities who are not Joomla users, but have an identity)
else
    respond with error
end if

I have seen that in plugins/authentication/joomla/src/Extension/Joomla.php

we have the method of Joomla::onUserAuthenticate:

    /**
     * This method should handle any authentication and report back to the subject
     *
     * @param   array   $credentials  Array holding the user credentials
     * @param   array   $options      Array of extra options
     * @param   object  &$response    Authentication response object
     *
     * @return  void
     *
     * @since   1.5
     */
    public function onUserAuthenticate($credentials, $options, &$response)
    {
        $response->type = 'Joomla';

        // Joomla does not like blank passwords
        if (empty($credentials['password'])) {
            $response->status        = Authentication::STATUS_FAILURE;
            $response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_EMPTY_PASS_NOT_ALLOWED');

            return;
        }

        $db    = $this->getDatabase();
        $query = $db->getQuery(true)
            ->select($db->quoteName(['id', 'password']))
            ->from($db->quoteName('#__users'))
            ->where($db->quoteName('username') . ' = :username')
            ->bind(':username', $credentials['username']);

        $db->setQuery($query);
        $result = $db->loadObject();

        if ($result) {
            $match = UserHelper::verifyPassword($credentials['password'], $result->password, $result->id);

            if ($match === true) {
                // Bring this in line with the rest of the system
                $user               = User::getInstance($result->id);
                $response->email    = $user->email;
                $response->fullname = $user->name;

                // Set default status response to success
                $_status       = Authentication::STATUS_SUCCESS;
                $_errorMessage = '';

                if ($this->getApplication()->isClient('administrator')) {
                    $response->language = $user->getParam('admin_language');
                } else {
                    $response->language = $user->getParam('language');

                    if ($this->getApplication()->get('offline') && !$user->authorise('core.login.offline')) {
                        // User do not have access in offline mode
                        $_status       = Authentication::STATUS_FAILURE;
                        $_errorMessage = $this->getApplication()->getLanguage()->_('JLIB_LOGIN_DENIED');
                    }
                }

                $response->status        = $_status;
                $response->error_message = $_errorMessage;
            } else {
                // Invalid password
                $response->status        = Authentication::STATUS_FAILURE;
                $response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_INVALID_PASS');
            }
        } else {
            // Let's hash the entered password even if we don't have a matching user for some extra response time
            // By doing so, we mitigate side channel user enumeration attacks
            UserHelper::hashPassword($credentials['password']);

            // Invalid user
            $response->status        = Authentication::STATUS_FAILURE;
            $response->error_message = $this->getApplication()->getLanguage()->_('JGLOBAL_AUTH_NO_USER');
        }
    }

I could of course implement the change we need, but that would be messing with Joomla's core code and I suppose there are better ways to do it.

Hence the question: how can I add my "else if" behavior without performing other changes on the process?

1 Answer 1

1

You would normally create your own plugin for the group Authentication and include your own function onUserAuthenticate() that has your code. Once installed you would have your plugin set in the Order to run before or after the default Joomla plugin you have pasted above to do your processing for SSO.

You are correct in not altering the core code, this is what plugins are designed to for, to add extra functionality at key points through out the normal process. The on... are those certain points where you can engage with the process.

You may find this as a useful starting point, https://docs.joomla.org/J4.x:Creating_an_Authentication_Plugin_for_Joomla

1
  • Thanks, Irata for your answer. Yesterday I was experimenting with this and created a new plugin and defined its own onUserAuthenticate too, which contained the hacky exit;, just for the sake of the experiment. My finding was that if my "normal" log in succeeded, then this was not executed at all, but if my "normal" login failed, I have seen a blank page, just as expected. So it seems that I'm on the right track and if this approach proves to be correct, then I will accept your answer. Thanks again! Commented May 31 at 9:52

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.