⚙ Complete Integration Guide

Django HRM ×
Rocket.Chat SSO

Step-by-step guide to integrate Django as an OAuth2 Provider and Rocket.Chat as the consumer. Users log in once via Django HRM and get automatic access to Rocket.Chat.

1

Install & Configure Django OAuth Toolkit

Backend — Django Setup

1
Install the package
bash
pip install django-oauth-toolkit
2
Add to INSTALLED_APPS
python — settings.py
INSTALLED_APPS = [
    # ... your apps
    "oauth2_provider",  # ← add this
]
3
Run migrations
bash
python manage.py migrate oauth2_provider
2

Configure Settings.py

Backend — Django Setup

python — settings.py
OAUTH2_PROVIDER = {
    "SCOPES": {
        "read":  "Read scope",
        "write": "Write scope",
    },
    "ACCESS_TOKEN_EXPIRE_SECONDS":  36000,
    "REFRESH_TOKEN_EXPIRE_SECONDS": 86400,
    "ROTATE_REFRESH_TOKEN":         True,

    # ← CRITICAL: disable PKCE for Rocket.Chat compatibility
    "PKCE_REQUIRED": False,

    # Allow http for local development
    "ALLOWED_REDIRECT_URI_SCHEMES": ["https", "http"],
}
PKCE_REQUIRED: False is mandatory
Rocket.Chat does not send PKCE code challenge. Without this setting you will get error=invalid_request&error_description=Code+challenge+required
3

Register URLs

Backend — Django Setup

python — urls.py (main)
from django.urls import path, include

urlpatterns = [
    path("admin/", admin.site.urls),

    # OAuth2 built-in endpoints — MUST be before your api urls
    path("o/", include("oauth2_provider.urls",
         namespace="oauth2_provider")),  # ← gives /o/token/ /o/authorize/ etc

    # Your custom endpoints
    path("api/v2/", include("apps.account.urls")),
]
🐍
This registers these endpoints automatically:
/o/authorize/ — Authorization page
/o/token/ — Token exchange
/o/revoke_token/ — Token revocation
/o/introspect/ — Token introspection
4

Create UserInfo Endpoint

Backend — Django Setup

Rocket.Chat calls this endpoint to get user data after receiving the access token. You must create this yourself.

python — account/views.py
from oauth2_provider.views import ProtectedResourceView
from django.http import JsonResponse

class UserInfoView(ProtectedResourceView):
    """
    Rocket.Chat calls this with: Authorization: Bearer <token>
    Returns user data to create/update Rocket.Chat user profile.
    """
    def get(self, request, *args, **kwargs):
        user = request.user
        return JsonResponse({
            "sub":      str(user.pk),       # required — unique user ID
            "email":   user.email,         # required — for matching accounts
            "name":    user.get_full_name()
                       or user.username,  # display name in Rocket.Chat
            "username": user.username,     # Rocket.Chat username
        })
python — account/urls.py
from django.urls import path
from .views import UserInfoView

urlpatterns = [
    # ... your other urls
    path("o/userinfo/", UserInfoView.as_view(), name="userinfo"),
]
"sub" field is required
Rocket.Chat uses sub as the unique identifier to match/create users. Without it the integration will fail silently.
5

Create OAuth Application in Django Admin

Backend — Django Admin

Go to http://your-domain/admin/oauth2_provider/application/add/

Application Settings — Fill exactly like this
FieldValueWhy
Name Rocket.Chat SSO Any descriptive name
Client Type Confidential Server-to-server, secret is kept safe
Authorization Grant Type Authorization code Standard OAuth2 web flow
Redirect URIs https://your_company_domain.com/_oauth/hrm Must match Rocket.Chat callback exactly
Hash Client Secret ☐ UNCHECK Keep plain so you can copy it
Algorithm No OIDC support Disables PKCE requirement
Copy client_secret IMMEDIATELY after saving
After clicking Save, Django shows the plain secret once. Copy it now and store it safely. You will paste it in Rocket.Chat admin in the next step.
If secret is already hashed — regenerate via shell
bash — django shell
python manage.py shell

from oauth2_provider.models import Application
import secrets

app = Application.objects.get(name="Rocket.Chat SSO")

new_secret = secrets.token_urlsafe(40)
print("COPY THIS:", new_secret)  # ← copy immediately

app.client_secret     = new_secret
app.hash_client_secret = False
app.save()

# Verify
app.refresh_from_db()
print("Saved:", app.client_secret == new_secret)  # must be True
6

Configure Rocket.Chat OAuth

Rocket.Chat — Admin Panel

Go to Rocket.Chat Admin → OAuth → Add Custom → name it hrm

Rocket.Chat OAuth Settings
FieldValue
EnableTrue
URLhttps://api.your_company_site.com
Token Path/o/token/
Token Sent ViaHeader
Identity Token Sent ViaHeader
Identity Path/api/v2/o/userinfo/
Authorize Path/o/authorize/
Scoperead write
Param Name for access tokenaccess_token
IdKEf3f5N4JiTg9fuyRU96zcX882ZSR2Cwihbmbkfv
Secretyour_plain_secret_from_step5
Login StyleRedirect
Username fieldusername
Email fieldemail
Name fieldname
Merge UsersTrue
🚀
Button text
Set "Button Text" to something like "Login with HRM" — this appears on the Rocket.Chat login page.
7

Test The Complete Flow

End-to-End — Manual Test

Browser
1. Click "Login with HRM" on Rocket.Chat
Rocket.Chat redirects browser to Django authorize URL
Django
2. Django shows login form at /o/authorize/
Enter HRM username and password → click Authorize
Django
3. Django redirects back to Rocket.Chat with code
302 → https://your_company_site.com/_oauth/hrm?code=xxx&state=xxx
Rocket
4. Rocket.Chat POSTs code to /o/token/ (server-to-server)
Exchange authorization code for access token
Rocket
5. Rocket.Chat GETs /o/userinfo/ with token
Fetches user email, name, username from Django
Done
6. User is logged into Rocket.Chat ✓
Account created/matched using email from userinfo response
Test userinfo endpoint manually:
bash
# First get a token via password grant (for testing only)
curl -X POST https://api.your_company_site.com/o/token/ \
  -d "grant_type=password" \
  -d "username=admin@gmail.com" \
  -d "password=your_password" \
  -d "client_id=SFsdfsdfsdfdsfsdfsdfdsfwerwerwerwerwerwe" \
  -d "client_secret=your_plain_secret"

# Then test userinfo with returned access_token
curl https://api.your_company_site.com/api/v2/o/userinfo/ \
  -H "Authorization: Bearer ACCESS_TOKEN_HERE"
Expected response: {"sub": "42", "email": "admin@gmail.com", "name": "...", "username": "..."}
8

Frontend — Login Button

Frontend — Any Framework

If you have a custom frontend (React, Vue, etc.) that needs to trigger the OAuth flow:

javascript
// Build the authorize URL and redirect user
function loginWithHRM() {
  const params = new URLSearchParams({
    response_type: "code",
    client_id:     "KEf3f5N4JiTg9fuyRU96zcX882ZSR2Cwihbmbkfv",
    redirect_uri:  "https://your_company_site.com/_oauth/hrm",
    scope:         "read write",
    state:         generateState(),  // random string for CSRF protection
  });

  window.location.href =
    `https://api.your_company_site.com/o/authorize/?${params}`;
}

function generateState() {
  return Math.random().toString(36).substring(2, 15);
}
For Rocket.Chat SSO
You don't need this if you're only integrating Rocket.Chat. The "Login with HRM" button is automatically added to the Rocket.Chat login page when you configure Step 6. This is only needed for a separate custom frontend.
9

Frontend — Handle Callback

Frontend — Custom App Only

javascript — callback page
// On your redirect_uri page, extract the code
const params = new URLSearchParams(window.location.search);
const code  = params.get("code");
const state = params.get("state");

// Exchange code for token via your backend
const response = await fetch("/api/v2/account/oauth/callback/", {
  method: "POST",
  headers: { "Content-Type": "application/json" },
  body: JSON.stringify({ code, state }),
});

const { access_token } = await response.json();
localStorage.setItem("token", access_token);
window.location.href = "/dashboard";
python — backend callback view
import requests
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework.permissions import AllowAny

class OAuthCallbackView(APIView):
    permission_classes = [AllowAny]

    def post(self, request):
        code = request.data.get("code")

        # Exchange code for token
        token_response = requests.post(
            "https://api.your_company_site.com/o/token/",
            data={
                "grant_type":    "authorization_code",
                "code":          code,
                "redirect_uri":  "https://your-frontend.com/callback",
                "client_id":     "KEf3f5N4JiTg9fuyRU96zcX882ZSR2Cwihbmbkfv",
                "client_secret": "your_plain_secret",
            }
        )
        return Response(token_response.json())
!

Common Errors & Fixes

Troubleshooting Reference

invalid_client
Cause: client_id or client_secret is wrong, or secret is hashed
→ Uncheck "Hash client secret", regenerate via shell, copy plain value
invalid_grant
Cause: Authorization code expired (codes last seconds), or redirect_uri mismatch
→ Get fresh code, ensure redirect_uri matches exactly in both Django admin and request
Code challenge required
Cause: PKCE is enabled by default in django-oauth-toolkit v2+
→ Add PKCE_REQUIRED: False in OAUTH2_PROVIDER settings
NoReverseMatch: oauth2_provider
Cause: oauth2_provider URLs not included in urls.py
→ Add path("o/", include("oauth2_provider.urls", namespace="oauth2_provider"))
401 on /o/userinfo/
Cause: Wrong Authorization header or expired token
→ Use format Authorization: Bearer <token>, check token expiry
__str__ returned NoneType
Cause: Model __str__ method returns None when a field is NULL
→ Use return self.name or f"Model #{self.pk}" in every model

Integration Checklist

Click to mark done

Backend
django-oauth-toolkit installed and in INSTALLED_APPS
PKCE_REQUIRED: False added to OAUTH2_PROVIDER settings
oauth2_provider URLs registered in urls.py with correct namespace
UserInfoView created and returning sub, email, name, username
OAuth Application created in Django admin
Hash client secret UNCHECKED
Plain client_secret copied and stored
Redirect URI set to https://your_company_site.com/_oauth/hrm
All models have __str__ methods with fallbacks
Migrations applied
Rocket.Chat
Custom OAuth added with name "hrm"
Enable set to True
Token Path set to /o/token/
Identity Path set to /api/v2/o/userinfo/
Authorize Path set to /o/authorize/
Client ID and Secret pasted correctly
Merge Users enabled
"Login with HRM" button visible on login page