Skip to main content

User Management

Learning Objectives​

  • Describe the xScaler authentication flow end-to-end
  • Explain the difference between Cognito tokens and xScaler JWTs
  • List all user roles and their permissions
  • Create an organisation and invite team members
  • Generate and manage API keys for tenants

Concepts​

Identity Model​

xScaler has two distinct identity models:

Identity TypeUsed ByMechanismPurpose
Human usersBrowser sessionsJWT (HS256, 30-min TTL)Portal access
OTel collectorsData ingestionAPI keys (xag_ prefix)Telemetry push
OTel agentsOpAMP channelEnrollment tokens (xse_)Agent registration

Authentication Flow​

Human Users (Browser)​

JWT Claims:

{
"sub": "xs_org_abc123def456",
"email": "user@example.com",
"org_id": "xs_org_abc123def456",
"role": "admin",
"exp": 1718800000
}

Key properties:

  • Algorithm: HS256 — symmetric signing key (JWT_SIGNING_KEY env var)
  • TTL: 30 minutes — short-lived for security
  • Storage: HttpOnly cookie — not accessible to JavaScript (XSS protection)
  • Renewal: Portal-web silently re-authenticates before expiry

Local Development Auth (No Cognito)​

For local development without AWS Cognito, portal-api provides a direct signup/login:

# Sign up
curl -s -X POST https://portal.xscalerlabs.com/auth/signup \
-H "Content-Type: application/json" \
-d '{"email":"admin@example.com","password":"Dev123!","name":"Admin"}' | jq .

# Login
curl -s -X POST https://portal.xscalerlabs.com/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"admin@example.com","password":"Dev123!"}' | jq .

API Key Authentication​

API keys are used by OTel collectors to push telemetry data. They never expire unless explicitly revoked.

Key Format​

xag_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
│ └─────────────────────────────────
│ Random 32-character suffix
└── Prefix: identifies key type
xag_ = agent/collector API key
xse_ = enrollment token

Key Storage (Security)​

API keys are never stored in plaintext. Only the SHA-256 hash is stored:

-- api_keys table
INSERT INTO api_keys (id, tenant_id, key_hash, prefix, display_name)
VALUES (
'key_abc123',
'xs_payment_abc12345',
sha256('xag_a1b2c3d4e5f6...'), -- SHA-256 hash only
'xag_a1b2', -- First 8 chars for display
'k8s-prod-daemonset'
);
Key Recovery

API keys are shown once at creation time. If you lose the key, you must create a new one and revoke the old one. xScaler cannot recover the original key value.

Authentication Flow (API Key)​


Organisation and Role Model​

Roles​

RoleDescriptionCan Do
ownerOrganisation creatorEverything including deletion
adminOrganisation adminAll operations except org deletion
memberStandard userCreate/manage tenants, view all
viewerRead-onlyView only — no create/modify/delete

Entity Formats​

EntityFormatExample
Organisation IDxs_org_<32-lower-hex>xs_org_a1b2c3d4e5f6g7h8i9j0k1l2m3n4o5p6
Tenant IDxs_<orgslug>_<8-char-lower-base32>xs_payment_ab3cd4ef
API keyxag_<random-32>xag_a1b2c3d4e5f6...
Enrollment tokenxse_<random-32>xse_a1b2c3d4e5f6...

Hands-On Exercise​

Exercise 1.3 — Create an Organisation and Invite Members​

# Set base URL
export PORTAL_BASE="https://portal.xscalerlabs.com"

# 1. Sign up and get a JWT token
RESPONSE=$(curl -s -X POST $PORTAL_BASE/auth/signup \
-H "Content-Type: application/json" \
-d '{"email":"alice@example.com","password":"Training123!","name":"Alice"}')

echo $RESPONSE | jq .
export JWT_TOKEN=$(echo $RESPONSE | jq -r '.token')

# 2. Get your organisation details
curl -s $PORTAL_BASE/org \
-H "Authorization: Bearer $JWT_TOKEN" | jq .

# 3. Invite a colleague
curl -s -X POST $PORTAL_BASE/org/members/invite \
-H "Authorization: Bearer $JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"email":"bob@example.com","role":"admin"}' | jq .

# 4. List members
curl -s $PORTAL_BASE/org/members \
-H "Authorization: Bearer $JWT_TOKEN" | jq .

Exercise 1.4 — Create a Tenant and API Key​

# 1. Create a tenant
TENANT=$(curl -s -X POST $PORTAL_BASE/tenants \
-H "Authorization: Bearer $JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"display_name":"Payment Service Production","environment":"prod"}')

echo $TENANT | jq .
export TENANT_ID=$(echo $TENANT | jq -r '.id')
echo "Tenant ID: $TENANT_ID"

# 2. Create an API key for the tenant
KEY=$(curl -s -X POST $PORTAL_BASE/tenants/$TENANT_ID/keys \
-H "Authorization: Bearer $JWT_TOKEN" \
-H "Content-Type: application/json" \
-d '{"display_name":"k8s-prod-collector"}')

echo $KEY | jq .
export API_KEY=$(echo $KEY | jq -r '.key')
echo "API Key: $API_KEY"
echo "SAVE THIS KEY — it will not be shown again!"

Validation​

  • curl $PORTAL_BASE/org -H "Authorization: Bearer $JWT_TOKEN" returns your organisation details
  • Tenant is listed: curl $PORTAL_BASE/tenants -H "Authorization: Bearer $JWT_TOKEN" | jq '.[].id'
  • API key was printed to terminal and saved
  • Portal UI at https://portal.xscalerlabs.com shows your tenant in the dashboard

Troubleshooting​

401 Unauthorized on portal-api requests

JWT tokens expire after 30 minutes. Re-authenticate:

export JWT_TOKEN=$(curl -s -X POST $PORTAL_BASE/auth/login \
-H "Content-Type: application/json" \
-d '{"email":"alice@example.com","password":"Training123!"}' | jq -r '.token')
403 Forbidden when inviting members

Only owner and admin roles can invite members. Verify your role:

curl -s $PORTAL_BASE/org/members \
-H "Authorization: Bearer $JWT_TOKEN" | jq '.[] | select(.email=="alice@example.com") | .role'
API key creation returns 404

Verify the tenant ID is correct and belongs to your organisation:

curl -s $PORTAL_BASE/tenants \
-H "Authorization: Bearer $JWT_TOKEN" | jq '.[].id'

Key Takeaways​

Session 1.2 Summary
  • Human users authenticate via AWS Cognito → exchange for xScaler JWT (HS256, 30-min TTL)
  • JWTs are stored in HttpOnly cookies — not accessible to JavaScript
  • OTel collectors authenticate with xag_ API keys — stored as SHA-256 hashes
  • API keys are shown once at creation — save them immediately
  • Organisation → Tenants → API Keys is the three-level hierarchy
  • Four roles: owner > admin > member > viewer

← Previous: Platform Introduction
Next: Observability Fundamentals →