team-management
SSOJet Team Management (Language-Agnostic, B2B-Ready)
Context
The user is already authenticated using SSOJet OIDC (Authorization Code flow).
This skill guides developers to implement secure, scalable team and organization management using SSOJet APIs, following best coding practices and working across any language or framework (Next.js, Golang, .NET, Node.js, etc.).
Goal of This Skill
Enable developers to build B2B SaaS team management by correctly handling:
- User ↔ Tenant relationships
- Multi-organization users
- Role-based access
- Secure backend-only management APIs
Core Architecture Rules (Mandatory)
-
Language-agnostic
- Describe flows, not frameworks
- Use pseudocode when needed
- Avoid framework-specific assumptions
-
Separation of concerns
- Frontend → UI only
- Backend → auth, tenant, team logic
- Management APIs are never called from the client
-
Token safety
- User access token → identity & context
- Management token → admin operations only
- Never expose management tokens
-
Explicit tenant context
- No “default tenant”
- Tenant ID must be selected and passed explicitly
-
Client Identification
- All Management API calls MUST include
client_idas a query parameter. - Format:
?client_id=<management_client_id>
- All Management API calls MUST include
Step-by-Step Instructions
1️⃣ Obtain a Management Token (Backend Only)
Use Client Credentials Grant with sys_owner scope.
{
"client_id": "<client_id>",
"client_secret": "<client_secret>",
"grant_type": "client_credentials",
"scope": "sys_owner"
}
Best practices
- Cache token until expiry
- Refresh automatically
- Never return this token to frontend
- Token Endpoint:
POST /api/v1/oauth2/token - Base URL: Ensure your API calls use the
/api/v1prefix (e.g.,https://api.ssojet.com/api/v1).
2️⃣ Identify the Logged-In User
From the user’s OIDC access token, extract:
sub→ user ID- Basic identity claims (email, name)
Do not assume tenant membership from the token alone.
3️⃣ Fetch User’s Tenants (Source of Truth)
To get all organizations the user belongs to, call:
GET /users/{userId}/tenants
API Reference: List User Tenants
"Returns a complete list of all tenants the user is a member of, along with their associated roles and context within each tenant."
Rules
- This API is the authoritative source for user–tenant mapping
- Do not rely only on JWT claims
- Backend must call this API using the management token
4️⃣ Tenant Selection (B2B SaaS Flow)
This is the critical "fork in the road" for B2B apps.
Scenario A: User belongs to existing tenants
- UI: Show a "Select Organization" screen or dropdown.
- Action: specific tenant ID is selected.
- Persistence: Store
tenant_idin the frontend session/cookie.
Scenario B: User has no tenants (New Sign-up)
- UI: Redirect to an "Onboarding / Create Organization" page.
- Form: Ask for "Company Name" and "Workspace URL".
- Backend Action:
-
Call Create Tenant API (
POST /tenants).{ "name": "string", "display_name": "string", "domains": [ "string" ] }API Reference: Create Tenant
-
Call Add User to Tenant API (
POST /tenants/{id}/users).{ "user_ids": ["current_user_id"] }API Reference: Add Users to Tenant
-
Call List All Roles API (
GET /roles) to find theOwnerrole ID.API Reference: List All Roles
-
Call Assign User Roles API (
POST /tenants/{id}/users/{uid}/roles).{ "role_ids": ["owner_role_id"] }API Reference: Assign User Roles
-
Auto-select the new tenant and proceed to the dashboard.
-
5️⃣ Multi-Tenant (B2B SaaS) Enforcement
Your system must support:
- One user → multiple tenants
- Different roles per tenant
- Safe tenant switching
Recommended request context
user_id
selected_tenant_id
role_in_tenant
6️⃣ Fetch Users of a Tenant (Team Listing)
To list all users in a selected tenant, use the endpoint that matches your authentication context:
A. Using User Access Token (Recommended for Front-facing APIs)
Use the JWT obtained during the OIDC login flow. This endpoint returns roles specific to the tenant context.
- Method:
GET - Endpoint:
/auth/tenants/{tenantId}/users - Header:
Authorization: Bearer {User_Access_Token}
B. Using Management Token (System/Service level)
Use this if you are performing administrative tasks without a user context.
- Method:
GET - Endpoint:
/tenants/{tenantId}/users - Header:
Authorization: Bearer {Management_Token}
Display Requirements:
- Email / Name
- Role (Extracted from the tenant's role list)
- Status (active, pending)
7️⃣ Filtering & Status Management
To provide a complete "Team Members" view, combine data from two sources:
- Active Members: Call the members listing API (above) and filter for
is_active: true. - Pending Invitations: Call
GET /tenants/{id}/invitations?status=pendingusing the Management Token.
Implementation Note: Standardize both responses into a shared model for the UI.
8️⃣ Invite New Team Members
Provide a backend endpoint that:
- Accepts email + role
- Verifies inviter’s role (Owner/Admin)
- Calls Team Member Invite API with full payload:
invitee:{ email: "..." }inviter:{ user_id: "...", email: "..." }(Required)role_ids:["..."]send_invitation_email:trueexpire_in_minutes:10080(7 days)
- Returns invite status
POST /tenants/{tenantid}/invitations
API Reference: Invite Team Member
"Generates a new onboarding invitation for a user to join the tenant workspace. This process creates a secure invitation token, associates it with the tenant, and dispatches an invitation email."
9️⃣ Remove Team Members
Allow only authorized roles to remove members. The removal logic depends on the member's status:
A. Remove Active Member
To remove an active user from the tenant, use the bulk delete endpoint:
- Method:
DELETE - Endpoint:
/tenants/{tenantid}/users - Body:
{ "user_ids": ["userId"] }
B. Remove Pending Invitation
To cancel or remove a pending invitation, use the invitation delete endpoint:
- Method:
DELETE - Endpoint:
/tenants/{tenantid}/invitations?invitation_id={invitationId}
Best practices
- Prevent accidental self-removal (last owner).
- Provide a clear confirmation dialog in the UI before proceeding.
🔟 Fetch Available Roles
Use the Roles API to:
- Fetch role list dynamically
- Assign roles during invite/update
- Avoid hardcoded role names
GET /roles
API Reference: List All Roles
"Retrieves a list of all defined roles within the system."
🎨 Frontend/UI Implementation Guidelines
To ensure a professional B2B experience, follow these UI patterns:
1. Tenant Switcher (Navigation)
- Location: Top-left of the sidebar or navbar.
- Component: Dropdown menu showing current tenant name + avatar.
- Actions:
- List other available tenants.
- "Create New Organization" button.
- "Settings" link for the current tenant.
2. Team Members List (Settings Page)
- Layout: Table view.
- Columns: User Info (Avatar+Name+Email), Role (Badge), Status (Active/Invited), Actions (Three-dot menu).
- Actions: "Edit Role", "Remove Member", "Resend Invite" (for pending).
3. Invite Member Modal
- Trigger: Primary button "Invite Member" on Team page.
- Fields:
- Email Address (Input).
- Role (Select/Dropdown) - fetched dynamically from backend.
- Feedback: Show success toast notification and refresh the list immediately.
4. Empty States
- Scenario: No members in the team (other than self).
- UI: "You are the only member. Invite your team to collaborate."
- Call to Action: Prominent "Invite Member" button.
Error Handling & Security Guidelines
The agent must always:
- Handle expired management tokens
- Return safe, user-friendly errors
- Log errors without leaking secrets
- Retry only idempotent operations
Output Expectations
The agent should:
- Explain why each step exists
- Clearly separate frontend vs backend responsibilities
- Use pseudocode instead of framework-specific code
- Work identically for Next.js, Golang, .NET, etc.
- Highlight common mistakes (wrong token, missing tenant context)
What This Skill Must Avoid
❌ Calling management APIs from frontend ❌ Assuming a single tenant per user ❌ Trusting JWT tenant data blindly ❌ Hardcoding roles or tenant IDs
TL;DR Flow
- User logs in (OIDC)
- Backend fetches user tenants →
/users/{userId}/tenants - User selects or creates tenant
- Backend lists tenant users →
/tenants/{tenantId}/users - Admin manages team (invite / remove / role update)