Roundcube Community Forum

 

Help with Implementing SSO Login Using JWT in Roundcube (Mail-in-a-Box Setup)

Started by lexxluey, October 09, 2024, 02:35:32 AM

Previous topic - Next topic

lexxluey

Hi everyone,

I'm working on integrating a Single Sign-On (SSO) solution for Roundcube, which is part of a Mail-in-a-Box (MiaB) installation. The goal is to allow users to log in automatically using JWT tokens issued by our main app (called Weelt). Here's a brief outline of what we've done so far:

Context:
We have a suite of applications, and zeeks is the primary app. When users log into zeeks, a JWT is created and stored in a cookie (zeeks).
Roundcube is hosted as part of Mail-in-a-Box, and we want to automatically authenticate users to Roundcube when they visit, provided they already have a valid JWT in their cookie.
The JWT is signed with RS256, and the token contains the user's email, which we need to match with the user records in Roundcube's database (SQLite).

Approach:
We're using a custom Roundcube plugin that hooks into the startup and authenticate hooks.
The plugin checks for the JWT in the cookie, decodes it, verifies the signature, and extracts the user's email.
We then query the users table in the Roundcube SQLite database to find a matching user by email.
If the user exists, we attempt to pass the user details to Roundcube's authentication system, bypassing the login form.

Problem:
Despite everything being seemingly in place, the flow still redirects us to the login page. Even after decoding the JWT and finding the user in the database, Roundcube doesn't proceed with the automatic login and instead presents the login form. closest I got to a solution but none of the implementation works for me

Current Code:
public function authenticate($args)
{
    if (isset($_COOKIE['zeeks'])) {
        try {
            $jwt_token = $_COOKIE['zeeks'];
            $base64PublicKey = '...'; //
            $publicKey = base64_decode($base64PublicKey);

            // Decode the JWT (using RS256)
            $decoded = \Firebase\JWT\JWT::decode($jwt_token, new \Firebase\JWT\Key($publicKey, 'RS256'));
            $email = $decoded->email;

            // Fetch the user from the Roundcube database
            $db = new SQLite3('/home/user-data/mail/users.sqlite');
            $stmt = $db->prepare('SELECT * FROM users WHERE email = :email');
            $stmt->bindValue(':email', $email, SQLITE3_TEXT);
            $result = $stmt->execute();
            $user_record = $result->fetchArray();

            if ($user_record) {
                // Set up user data for authentication
                $args['user'] = $user_record['email'];
                $args['pass'] = ''; // Empty password for SSO
                $args['host'] = 'localhost'; // Default MiaB setup
                $args['cookiecheck'] = false;
                $args['valid'] = true;
            } else {
                rcube::raise_error([
                    'code' => 403,
                    'type' => 'php',
                    'message' => "User with email $email not found in Roundcube database"
                ], true, false);
            }
        } catch (\Exception $e) {
            rcube::raise_error([
                'code' => 403,
                'type' => 'php',
                'message' => 'Invalid JWT or user lookup failed: ' . $e->getMessage()
            ], true, false);
        }
    }

    return $args;
}

What We've Tried:
Verified the plugin is loaded (using print statements).
Confirmed that the JWT is being decoded correctly and that the user exists in the database.
Hooked into the authenticate process and attempted to override the user and pass fields.

Desired Outcome:
We want Roundcube to skip the login form and directly authenticate the user based on the JWT in the cookie, as long as the token is valid and the user exists in the database.

Setup:
Roundcube is installed as part of a Mail-in-a-Box (MiaB) server.
JWTs are issued by our app and signed with RS256.
The Roundcube installation is using SQLite as its backend.
Has anyone successfully implemented something like this? Any pointers on what might be missing or misconfigured would be greatly appreciated!

Thanks in advance for your help!


SKaero

You need to redirect any unauthenticated user to the login action. Take a look at the example autologon for an example of that: https://github.com/roundcube/roundcubemail/blob/master/plugins/autologon/autologon.php