Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Way to Automatically Login - Embedding Chat in existing CRM #4318

Closed
chodges opened this issue Sep 15, 2016 · 32 comments
Closed

Way to Automatically Login - Embedding Chat in existing CRM #4318

chodges opened this issue Sep 15, 2016 · 32 comments

Comments

@chodges
Copy link

chodges commented Sep 15, 2016

Your Rocket.Chat version: 0.39.0

I have a very straightforward question (I think). We are creating a custom CRM for a specific industry. We would love to use RocketChat to allow the users to communicate with each other. As you can imagine we already have our own authentication scheme (user/pass). Is there a straightforward way to do 3 things:

  1. Embed the chat into our site (I guess we can use iframe)
  2. Auto-create the user account in Rocket Chat when a new user is added to the CRM
  3. Automatically login WITHOUT the user having to click a button or do anything else.

For example I embedded Rocket.Chat into our site using an iframe. Then I used the REST API to log the user in and got back a successful status. But the login screen still appears as if the user hasn't already been logged in. I need this to appear to be a seamless thing so that once the user logs into our CRM we can log them into Rocket.Chat.

Is this possible? If not we'll probably have to look for another solution.

Thanks.

@matthewshirley
Copy link

matthewshirley commented Sep 17, 2016

Without seeing any code it is hard to diagnose. Are you following the guide located here? https://rocket.chat/docs/administrator-guides/authentication/iframe/

Alternatively, single sign-on (SSO), SAML or OAuth is an option. You can look in the administration settings for more information.

@chodges
Copy link
Author

chodges commented Sep 20, 2016

Yes but will either SSO or SAML allow me to bypass the login screen completely? That's the goal. I want this to be a seamless integration where the user does not even know there's a separate account for the chat.

@sckoh
Copy link

sckoh commented Oct 14, 2016

@chodges did you find the answer?
I'm trying to achieve the same thing

@chodges
Copy link
Author

chodges commented Oct 14, 2016

@sckoh What I found out is that in order to achieve full seamless integration would require me to rewrite significant parts of the code to get it work the way I wanted. I'm not willing to do that and so I ended up going with another solution. For the record I think tools like this are great but they're missing a key fact that many times chat is a component of another product not the product itself.

@sckoh
Copy link

sckoh commented Oct 14, 2016

@chodges May I know what product are you using for chat now?

On Fri, 14 Oct 2016, 18:09 Chris Hodges, notifications@github.com wrote:

@sckoh https://github.com/sckoh What I found out is that in order to
achieve full seamless integration would require me to rewrite significant
parts of the code to get it work the way I wanted. I'm not willing to do
that and so I ended up going with another solution. For the record I think
tools like this are great but they're missing a key fact that many times
chat is a component of another product not the product itself.


You are receiving this because you were mentioned.
Reply to this email directly, view it on GitHub
#4318 (comment),
or mute the thread
https://github.com/notifications/unsubscribe-auth/AF_jwMvl6gCXKNtJLBEIyv3ZSLJeUWqMks5qz1THgaJpZM4J-XOu
.

@chodges
Copy link
Author

chodges commented Oct 14, 2016

@sckoh Right now I'm using CometChat. I purchased the lowest price version for $50. It does not compare to some of the features of RocketChat but the integration is flawless. That being said I'm not very happy with their customer support responsiveness but that being said the product works fairly well and it integrates. The one big upside is that CometChat have done a good job of providing numerous integration components for existing products.

@Huljo
Copy link
Contributor

Huljo commented Oct 19, 2016

+1, need this feature...

@kginstructor
Copy link

I was able to get this functionality from RocketChat's APIs/iframe integrations, though there were a few gotchas that made it tricky. But it is possible with RocketChat 0.42.0, stock.

@danielpg
Copy link

@kginstructor did you have to edit source files? can you tell which files you edited? or you did it with just client side code?

@kginstructor
Copy link

kginstructor commented Oct 27, 2016

@danielpg No source edits. All via the API stuff and creating an iframe API for the iframe integration stuff to connect to. Gotchas were correct Content-Type header on the json return and I had to send

{
    "token" : "loginToken acquired via login API",
    "loginToken" : "exact same token as above entry"
}

In order for the iframe to properly login. Apparently there are bugs in the javascript... sometimes it was looking for [var].token and other times it was looking for [var].loginToken, so I gave it both, both identical and obtained via the login API.

On the login API, the gotcha was expressing Content-Type as application/x-www-for-urlencoded. Those content types would probably have been done automatically with some HTML handlers, but I was using PHP's GuzzleHttp, which doesn't.

Just did those and it worked perfectly for me.

@Huljo
Copy link
Contributor

Huljo commented Oct 28, 2016

It worked for me. Thank you @kginstructor. No need "token" in my case. just "loginToken".

@nullx8
Copy link

nullx8 commented Dec 13, 2016

quick follow up ..

i do send the proper content type on the api file (also use php)

and return only the tokens ..
but for some reason the script stops there.

i see in the developer console that the values have been read .. but thats it.

@janakamarasena
Copy link

@kginstructor is it possible to try this same method to bypass the login screen from in an in-app browser for ios or android (using ui webview). I can get the token but no luck in bypassing the login screen using the above method.

@kginstructor
Copy link

I would assume so, @janakamarasena , they are after all browser pages. But I haven't done so myself yet.

@janakamarasena
Copy link

@kginstructor I tried it and several other ways including passing using headers but no luck. If you find a proper solution please let me know. Thank You.

@Wouter0100
Copy link

I've created an Rocket.Chat package for "Token Authentication". You'll be able to auth directly to the Realtime API with a hash (username + token). This hash should be created on a backend service, since the token is a secret. See here. I'm willing to open a PR to merge this, but I'm not sure if it's a proper way of authentication.

Currently searching how to implement this in the API, since I need the same feature in the RESTful API.

@chrbo
Copy link

chrbo commented Jan 25, 2017

@Wouter0100 I will have a look at this - nice feature! I am currently also searching how to implement an additional authentication method for the api. Does anybody give me a hint?

@ghost
Copy link

ghost commented Jan 25, 2017

@chrbo, I think this (https://dev.twitter.com/oauth/reference/get/oauth/authorize) is a good model. Google use this model too.

@chrbo
Copy link

chrbo commented Jan 25, 2017

@jm4rc05 Hi - yes it will be an authentication based on a oauth access token. We currently evaluate RocketChat for an project we are using an identity server using OpenID Connect / OAuth2. Everything is working fine - RocketChat is configured to use the identity server with oauth. But we cannot login a user programatically with an oauth token. Currently we have to use the form based flow.

My question was: does anybody know how to implement an additional authentication method for the Rocket.Chat API.

Where are the currently available authentication methods configured?
How can i add an additional authentication method without changing the Rocket.Chat code?

Many thanks

@c-thomasFR
Copy link

Could someone create a small index.php page to see the full method because i'm a bit lost. I also want to integrate rocket chat on my website with automaticall login. Thanks for your help

@evaletolab
Copy link

@c-thomasFR 👍

@salj76
Copy link

salj76 commented Mar 16, 2017

Hi can any one tell me how the process works or if it is possible using kginstructor's solution if I am using SAML for authentication. Specifically how I can get the token would the parent app call RC's login?

@Maado
Copy link

Maado commented Mar 22, 2017

@kginstructor Thanks that works for me fine

@kginstructor
Copy link

Here is the exact index.php I am using: (keep in mind, this is php)

<?php

// in our app, this loads the configs/autoloader/etc
require_once '../../rts_main.php';
header('Access-Control-Allow-Origin: '.$_SERVER['HTTP_ORIGIN']);
header('Access-Control-Allow-Credentials: true');
header('Content-Type: application/json');

try {
    // \Cmn\User::Current() pulls the user from our app
    if(\Cmn\User::Current() === null) {
        throw new \Exceptions\Authorization("You are not authorized to see this!");
    }
    $me = new \Cmn\Chat\Rocket\UserBased();
    $me->Login(\Cmn\User::Current());
    // Opted to just echo json directly rather than build an array and json_encode it. 
    echo "{\n \"userId\":\"{$me->UserID()}\", \"loginToken\":\"{$me->Token()}\", \"token\":\"{$me->Token()}\" \n}";
}catch(\Exception $ex) {
    \Command\Log::Record($ex);
    header('HTTP/1.0 401 Not logged in.');
}

the \Cmn\Chat\Rocket\UserBased class:

<?php

namespace Cmn\Chat\Rocket;

/**
 * Description of UserBased
 *
 * @author jay
 */
class UserBased extends Base {
    protected $_token;
    protected $_userId;
    protected $_cache;
    public static function GetAuth($user, $password) {
        try {
            $c = new Base("/api/login", null, ['user'=>$user, 'password'=>$password]);
            $r = $c->SendPost();
            return $r->data;
        } catch(\Exception $ex) {
            \Command\Log::Record($ex);
            return (object)['error'=>$ex];
        }
    }
    public function Token() { return $this->_token; }
    public function UserID() { return $this->_userId; }
    /**
     * 
     * @return AuthCache
     */
    public function Cache() { return $this->_cache; }
    /**
     * 
     * @param \Cmn\User $user
     * @return UserBased
     */
    public static function GetByUser(\Cmn\User $user) { return static::GetByPassword($user); }
    /**
     * 
     * @param \Cmn\User $user
     * @return UserBased
     */
    public static function GetByPassword(\Cmn\User $user) {
        $class = get_called_class();
        $c = new $class();
        return $c->Login($user);
    }
    public function Login(\Cmn\User $user, $refresh = false) {
        $this->GetCache($user);
        if($refresh || !$this->_cache) {
            $this->UpdateCache($user);
        }
        $this->_token = $this->_cache->rs_token;
        $this->_userId = $this->_cache->rs_userid;
        $this->AddHeader('X-Auth-Token', $this->_token);
        $this->AddHeader('X-User-Id', $this->_userId);
        return $this;
    }
    public function UpdateCache(\Cmn\User $user) {
        $auth = static::GetAuth($user->email, substr($user->password, 0, 17));
        if(isset($auth->error)) {
            return $this->_updateUser($user);
        }
        if(!$this->_cache) {
            $this->_cache = new AuthCache();
            $this->_cache->id = $user->email;
        }
        $this->_cache->rs_userid = $auth->userId;
        $this->_cache->rs_token  = $auth->authToken;
        $this->_cache->date_generated = date('Y-m-d H:i:s');
        return $this->_cache->Save();
    }
    public function GetCache(\Cmn\User $user) {
        if($this->_cache) { return $this->_cache; }
        try {
            $this->_cache = new AuthCache($user->email);
        } catch (\Exceptions\ItemNotFound $ex) {
            $this->_cache = null;
        }
        return $this->_cache;
    }
    protected function _updateUser(\Cmn\User $user) {
        //For now, we are going to assume the player doesn't exist
        $a = new \Cmn\Chat\Rocket\AdminBot();
        $a->CreateUser($user);
        return $this->UpdateCache($user);
        //throw new \Exceptions\NotImplemented();
    }
}

which extends the \Cmn\Chat\Rocket\Base class:

<?php

namespace Cmn\Chat\Rocket;

/**
 * Description of Base
 *
 * @author jay
 */
class Base {
    const ROCKET_URI = 'http://your.rocket.chat.install';
    protected $_client;
    protected $_headers;
    protected $_body;
    protected $_endPoint;
    public function __construct($endpoint = '/api/version', array $headers = null, array $body = null) {
        $this->_headers = $headers ? $headers : [];
        $this->_body = $body ? $body : [];
        $this->_endPoint = $endpoint;
        $this->_client = new \GuzzleHttp\Client();
    }
    public function Body() { return $this->_body; }
    public function AddBody($name, $value = null) {
        if(is_array($name)) {
            $this->_body = array_merge($this->_body, $name);
        } else {
            $this->_body[$name] = $value;
        }
        return $this;
    }
    public function EndPoint($endPoint = false) {
        if($endPoint) { 
            $this->_endPoint = $endPoint;
            return $this;
        }
        return $this->_endPoint;
    } 
    public function Headers() { return $this->_headers; }
    public function AddHeader($name, $value = null) {
        if(is_array($name)) {
            $this->_headers = array_merge($this->_headers, $name);
        } else {
            $this->_headers[$name] = $value;
        }
        return $this;
    }
    public function SendPost(array $headers = null, array $body = null) {
        $this->AddHeader('Content-Type', 'application/x-www-form-urlencoded');
        if($headers) { $this->AddHeader($headers); }
        if($body) { $this->AddBody($body); }
        $b = [];
        foreach($this->Body() as $name => $value) { $b[] = "{$name}={$value}"; }
        return json_decode($this->SendRequest('POST', $this->EndPoint(), ['headers' => $this->Headers(), 'body' => implode("&", $b)]));
    }
    public function SendJSON(array $headers = null, array $body = null) {
        $this->AddHeader('Content-type', 'application/json');
        if($headers) { $this->AddHeader($headers); }
        if($body) { $this->AddBody($body); }
        return json_decode($this->SendRequest('POST', $this->EndPoint(), ['headers' => $this->Headers(), 'json' => $this->Body()]));
    }
    public function SendRequest($method, $endpoint, $options) {
        /* @var $r \GuzzleHttp\Psr7\Response */
        $r = $this->_client->request($method, static::ROCKET_URI.$endpoint, $options);
        $t = $r->getBody();
        $buffer = "";
        while(!$t->eof()) {
            $buffer .= $t->read(4096);
        }
        return $buffer;
    }
}

You'll notice that I am using GuzzleHttp, so you have to have that somewhere loaded (our autoloader falls back to composer, so we just used composer to install that). You'll also notice I cache the auth response which mostly works well, except in certain circumstances where the cache invalidates for some reason in RocketChat and you have to get a new one. but this should be enough to get you pointed in the right direction (or at least the direction that I headed down, right or not :p )

@robertwessen
Copy link

header('Access-Control-Allow-Origin:` '.$_SERVER['HTTP_ORIGIN']); header('Access-Control-Allow-Credentials: true');

potentially off-topic, my apologies...but doesn't this CORS config permit any script on any origin the ability to retrieve your user's RC login tokens?

@kginstructor
Copy link

Yeah, @robertwessen , it sure does. I have several servers hitting it, and since the entire project is still in beta, that was the fastest way to get past the XSS limitations and move on with integration. Keep in mind, if the client hitting it is not already authenticated in the other system, \Cmn\User::Current() will return null and they just get an error, so the threat of exploit was low enough, for our purposes, to not justify building out a more robust and secure solution.

@assadvirgo
Copy link

@chrbo as you mentioned in your reply to @jm4rc05

Hi - yes it will be an authentication based on a oauth access token. We currently evaluate RocketChat for an project we are using an identity server using OpenID Connect / OAuth2. Everything is working fine - RocketChat is configured to use the identity server with oauth. But we cannot login a user programatically with an oauth token. Currently we have to use the form based flow.

I'm having exactly the same issue with one of our app. We've Identity Server setup and its working fine with other apps but with Rocket.Chat as "Custom oAuth" it just doesn't work. can you suggest me a solution in this regard if you have implemented one already. Would be a great help. Thanks.

@bakhtizin
Copy link

@chrbo I am doing the same thing. Will I be wrong if I assume that the Identy Server was/is Keycloak? Now, to the topic: did you manage to find a workaround/realization for login with custom OAuth via API? Could you provide some tipps/tricks? Ideal solution would be not seeing the Rocket.Chat login page at all. :/

@tdcpepsi
Copy link

kginstructor, can I pay you to activate the single sign on for my website? I want my members to be able to log into my website but at the same time they are also logging into the chat room. My email address: tdcpepsi@yahoo.com | please reply asap....

@kginstructor
Copy link

@tdcpepsi Shot you an email. For those in this thread though, what system are you looking to integrate into? If possible, I'd like to help you in here so everyone can benefit from it and hopefully others can get their integration going as well.

@evaletolab
Copy link

evaletolab commented Feb 27, 2018

@kginstructor I'm also strongly waiting for a simple SSO integration to our opensource food marketplace & distribution project karibou.ch.
Our system is node/express.js + mongodb.
Here the code of our integration for disqus SSO:
https://github.com/karibou-ch/karibou-api/blob/master/models/users.js#L316
It would be perfect to have a similar solution. What should be added to rocket.chat to obtain similar integration?
Cheers, Olivier

@TwizzyDizzy
Copy link

@rocket-cat close

Folks, we're doing a bit cleaning up in the Github issues and by now there is so much content in this issue that it's not really possible anymore to extract what should be done.

Would you be so kind as to continue this discussion in the forums? https://forums.rocket.chat ... when it has become clear in the forum discussion what it actually is that should be implemented, feel free to open a new issue and we'll look at that :)

If you're opening a topic in the forums, it might be a good idea to link to this issue.

Cheers
Thomas

@rocket-cat rocket-cat bot closed this as completed Mar 22, 2018
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Projects
None yet
Development

No branches or pull requests