Rocket.chat: Use Rest API with oAuth Logins

Created on 12 Jan 2017  路  25Comments  路  Source: RocketChat/Rocket.Chat

Your Rocket.Chat version: 0.48.2

We are trying to integrate RocketChat into our application via a Polymer component.

Our application uses single signon OAuth provider. RocketChat is configured to use the same OAuth provider (Keycloak) and forms-based authentication is disabled.

How can the user login to the API given this scenario?

oauth / sso rest

Most helpful comment

The typical way this gets implemented is via generation of special use API "tokens" like they use here on GitHub if you have 2FA implemented.

OAuth users should be able to "generate a new API token" that gives them a login/password pair they can use to access the API.

Again, the goal here is to be able to have API users without having to create local users - where all access to the system is brokered via an external authentication provider.

All 25 comments

Two ways:

  • Authorization header with value Bearer ${accessToken}
  • access_token as a query parameter ?access_token=${accessToken}

This is currently how how Zapier Integration does it, so it works great. Sometimes with you will unauthenticated if your accessToken has expired, if that's the case then refresh it and you'll be good to go.

@graywolf336 Does this really work? I successfully authenticated on the login endpoint of the api (/api/v1/login) with a local RocketChat users username and password (https://rocket.chat/docs/developer-guides/rest-api/authentication/login).

But i cant get authenticated with a valid access token issued by a external identity server. Form based authentication against the identity server does work.

Can you provide more informations?

@logycon Did the solution work for you?

{
  "info": {
    "version": "0.50.0-develop",
    "build": {
      "date": "2017-01-24T15:35:52.402Z",
      "nodeVersion": "v4.6.2",
      "arch": "x64",
      "platform": "linux",
      "osRelease": "2.6.32-642.13.1.el6.x86_64",
      "totalMemory": 4018343936,
      "freeMemory": 2185764864,
      "cpus": 4
    },
    "commit": {
      "hash": "d9850e8bd3530ca0759e9f5ae26daada2ac9da9e",
      "date": "Fri Jan 20 19:00:49 2017 -0200",
      "author": "Gabriel Engel",
      "subject": "Merge pull request #5671 from RocketChat/add-rest-api-registration",
      "tag": "0.49.2",
      "branch": "develop"
    },
    "GraphicsMagick": {
      "enabled": false
    },
    "ImageMagick": {
      "enabled": false
    }
  },
  "success": true
}

Many thanks!

The access token must be provided by Rocket.Chat, as Rocket.Chat must know about the access token otherwise it isn't valid.

@graywolf336 So it does not work with a configured custom oauth provider?

If Rocket.Chat doesn't know about the authToken, then I would guess no

Hi @graywolf336, would you consider reopening this as a feature request along the lines of "Support API login for OAuth users"? Right now as @chrbo says if you only allow login via OAuth, the REST API is completely non-functional. I (and I presume others) can't allow local users to our instances by policy.

@wohali Sure, although off the top of my head I'm not a 100% sure how this would be accomplished and I'll need to talk with someone who has more experience with the oAuth side of the project.

Hi,
we would need this feature as well. Is there perhaps a workaround?

Maybe of topic but would it be possible to add API Login Support for SAML as well?

We need this feature as well. Would love a work around to allow users to authenticate using OAuth and get a functional API.

Authentication with SAML via the api will be very help full for me too!
Is it in your roadmap? @engelgabriel

I guess the least intrusive implementation was if /login could be passed some information from other identity-providers alternatively to {username, password}.
@graywolf336 any thoughts already on that?

@mrsimpson if you can get access to the login tokens generated by login, you can use those for the rest api.. @RocketChat/core and I haven't discussed this in a while, so I don't remember the thoughts that came up. Any ideas on this all guys?

not sure I get the real need of this.

the idea is to point the user to the login page, then it will open the external OAuth privider's login page automatically?

The typical way this gets implemented is via generation of special use API "tokens" like they use here on GitHub if you have 2FA implemented.

OAuth users should be able to "generate a new API token" that gives them a login/password pair they can use to access the API.

Again, the goal here is to be able to have API users without having to create local users - where all access to the system is brokered via an external authentication provider.

We need this feature as well. I've been looking through the documentation for last 2 days, and there's no possibility currently available.
Please, take it into consideration!

possible use case: https://github.com/RocketChat/Rocket.Chat/issues/4318

from @sampaiodiego :

not sure I get the real need of this.
the idea is to point the user to the login page, then it will open the external OAuth privider's login page automatically?

possible scenario/use-case: https://github.com/RocketChat/Rocket.Chat/issues/4318 Iframe integration simply to be able to call OAuth login is too much for this functionality. :/

@rodrigok any chance this can be added with your changes to the rest api login logic?

Hi guys,

I want to give this issue a bump since I am also struggeling with this issue.

For reference..
I tried using an existing authentication token in an Authentication Bearer header or a get parameter as suggested by @graywolf336 but it didn't work.

What does work is setting both the X-Auth-Token and X-User-Id headers to the values of the rc_token and rc_uid cookies set by the web interface.

The documentation about Keycloak is going to be written. Please follow https://github.com/RocketChat/docs/issues/790 and fell free to help us :)

I'm just starting out with Rocketchat. Can you tell me how to obtain the URL?

The login API documentation show how to work with localhost, but what if I want to connect to a channel online like:

https://chat.hyperledger.org/channel/general

How should the URL in the API request look?

Thanks, Shefy

@graywolf336 , I'm also trying to use RocketChat with an external identity server (Keycloak).

I can login using oauth on the web interface but can't login using the REST API.

response from api/v1/settings.oauth

{
    "services": [
        {
            "_id": "9Wv3oH6PAbHgnXh5e",
            "service": "keycloak",
            "clientId": "rocket",
            "custom": true,
            "serverURL": "http://localhost:8080/auth/realms/{realm_name}",
            "tokenPath": "/protocol/openid-connect/token",
            "identityPath": "/protocol/openid-connect/userinfo",
            "authorizePath": "/protocol/openid-connect/auth",
            "scope": "openid",
            "buttonLabelText": "Funny",
            "buttonLabelColor": "#FFFFFF",
            "loginStyle": "redirect",
            "buttonColor": "#13679A",
            "tokenSentVia": "header",
            "identityTokenSentVia": "default",
            "usernameField": "preferred_username",
            "mergeUsers": true
        }
    ],
    "success": true
}

This is what I send for the login request

{
    "serviceName": "keycloak", 
    "accessToken": "token",
    "idToken": "id_token",
    "secret": "8a7eb321-06f1-4ffd-9985-828382832",
    "expiresIn": 300,
    "scope": "openid"
}

The response (401)

{
    "status": "error",
    "message": "Unexpected AccessToken service keycloak"
}

There's no documentation for which options I should set for keycloak.

I am doing this by update the source code of RocketChat and working perfectly.
First, the RocketChat still does not provide functionality for what we need. So write it my own.

RocketChat version: 0.64.1
Keycloak version : 4.3.0

Note: you should configure the keycloak and RocketChat in GUI with successful GUI OAuth login.

I am working on following JS files
1.Rocket.Chat/programs/server/packages/rocketchat_lib.js
2.Rocket.Chat/programs/server/app/app.js

Add the below code after the facebook js code end.

step 1 :
"keycloak.js":function(require,exports,module){

/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
//                                                                                                                     //
// packages/rocketchat_lib/server/oauth/keycloak.js                                                                    //
//                                                                                                                     //
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
                                                                                                                       //
let _;

module.watch(require("underscore"), {
  default(v) {
    _ = v;
  }

}, 0);
let OAuth;
module.watch(require("meteor/oauth"), {
  OAuth(v) {
    OAuth = v;
  }

}, 1);

const crypto = Npm.require('crypto');

const whitelisted = ['id', 'email', 'name', 'first_name', 'last_name', 'link', 'gender', 'locale', 'age_range'];
const FB_API_VERSION = 'v2.9';
const FB_URL = 'https://graph.keycloak.com';

const getIdentity = function (accessToken, fields, secret) {
  const hmac = crypto.createHmac('sha256', OAuth.openSecret(secret));
  hmac.update(accessToken);
  bearer_accessToken = 'Bearer '+accessToken
  try {
    keycloak_url = RocketChat.settings.get('Accounts_OAuth_Custom-Keycloak-url');
    keycloak_identitypath= RocketChat.settings.get('Accounts_OAuth_Custom-Keycloak-identity_path');
    keycloak_user_info_url = keycloak_url+keycloak_identitypath
    user_info =  HTTP.get(keycloak_user_info_url, {
      headers: {
        'Authorization' : bearer_accessToken
      }
    }).data;
    user_info['id']=user_info['sub']
    return user_info
  } catch (err) {
    throw _.extend(new Error(`Failed to fetch identity from Keycloak. ${err.message}`), {
      response: err.response
    });
  }    
};

RocketChat.registerAccessTokenService('keycloak', function (options) {
  check(options, Match.ObjectIncluding({
    accessToken: String,
    secret: String,
    expiresIn: Match.Integer,
    identity: Match.Maybe(Object)
  }));
  const identity = options.identity || getIdentity(options.accessToken, whitelisted, options.secret);
  console.log('3.identity=> '+identity)
  //console.log('4.options.accessToken=> '+options.accessToken)
  console.log('5.expiresAt=> '+new Date() + 1000 * parseInt(options.expiresIn, 10))
  const serviceData = {
    accessToken: options.accessToken,
    expiresAt: +new Date() + 1000 * parseInt(options.expiresIn, 10)
  };

  const fields = _.pick(identity, whitelisted);

  _.extend(serviceData, fields);

  return {
    serviceData,
    options: {
      profile: {
        name: identity.name
      }
    }
  };
});
/////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////

},

step 2:
load your keycloak.js at end of file (add as facebook and twitter)
require("/node_modules/meteor/rocketchat:lib/server/oauth/keycloak.js");

step 3:
add the 'keycloak' in the app.js at line 7537 (add in array element)

step 4:
add the below line after 503
user.username = user.name;

Now updation is done. let check the result

  1. get the access token from keycloak for rocket chat client
curl \
  -d "client_id=rocketchat" \
  -d  "client_secret=6f7d8691-18ce-4516-a8b6-c7cb37ddfc7a" \
  -d "username=racky" \
  -d "password=racky" \
  -d "grant_type=password" \
  -d "scope=openid email profile" \
  "http://192.168.10.115:8080/auth/realms/next/protocol/openid-connect/token"
  1. get the rocket chat user id and auth token using the accessToken
curl -H "Content-type:application/json" \
      http://192.168.10.93:3000/api/v1/login \
      -d '{ "serviceName": "keycloak", "accessToken": "eyJhbGciOiJSUzI1NiIsInR5cCIgOiAiSldUIiwia2lkIiA6ICJvbDNIMGJUcjdPRUpGQnZsUkZDcGZCdWJXemF2MGVtTEZPRXpsRzdtWk84In0","secret": "6f7d8691-18ce-4516-a8b6-c7cb37ddfc7a", "expiresIn": 200 }'

@Hudell I opened another issue #14108 for adding to custom oauth. But this issue is technically solved.

By executing:

curl -H "Content-type:application/json" \
      http://<yourserver>/api/v1/login \
      -d '{ "serviceName": "yourOauthService", "accessToken": "tokenFromOauthProvider", "expiresIn": 200 }'

You can login to Rocket.Chat.

Only ones currently supported are: Google / Facebook / Twitter

Since this is technically solved i'm going to close. If any are looking for their provider... if custom head over to: #14108 where I put a lot of details. If one of the out of the box feel free to open a PR or issue specifically for rest login via that provider.

Thanks!

Was this page helpful?
0 / 5 - 0 ratings

Related issues

brendanheywood picture brendanheywood  路  3Comments

mattlin picture mattlin  路  3Comments

karlprieb picture karlprieb  路  3Comments

sta-szek picture sta-szek  路  3Comments

djeber picture djeber  路  3Comments