Documentation Index
Fetch the complete documentation index at: https://developers.smarterservices.com/llms.txt
Use this file to discover all available pages before exploring further.
Implementation Guide
This guide covers how to integrate the SmarterServices IAM system into your applications, including client libraries, server-side implementation, and best practices.
Architecture Overview
The SmarterServices IAM system consists of:
- IAM Service: Central authentication and authorization service
- Client Libraries: Language-specific libraries for integration
- Policy Engine: Evaluates permissions based on policies
- Resource Providers: Services that enforce IAM decisions
Client Libraries
JavaScript/TypeScript (Browser)
Installation
npm install @smarterservices/iam-client
Basic Usage
import { IamClient } from '@smarterservices/iam-client';
// Initialize the client
const iamClient = new IamClient({
apiUrl: 'https://iam.smarterservices.com',
token: 'your-auth-token'
});
// Check permissions
const canDelete = await iamClient.authorize(
'sp:DeleteSession',
'ssrn:ss:sp::578:session/ES123456'
);
if (canDelete) {
// User has permission to delete the session
deleteSession();
}
React Integration
import React from 'react';
import { IamProtected } from '@smarterservices/iam-react';
function DeleteButton({ sessionId }) {
return (
<IamProtected
action="sp:DeleteSession"
resource={`ssrn:ss:sp::578:session/${sessionId}`}
>
<button onClick={handleDelete}>
Delete Session
</button>
</IamProtected>
);
}
Node.js (Server)
Installation
npm install @smarterservices/iam-node
Basic Usage
const { IamService } = require('@smarterservices/iam-node');
// Initialize the service
const iam = new IamService({
apiUrl: 'https://iam.smarterservices.com',
serviceKey: 'your-service-key'
});
// Middleware for Express.js
app.use('/api/sessions/:id', async (req, res, next) => {
const authorized = await iam.authorize(
req.user.token,
'sp:ReadSession',
`ssrn:ss:sp::${req.user.accountId}:session/${req.params.id}`
);
if (!authorized) {
return res.status(403).json({ error: 'Forbidden' });
}
next();
});
Python
Installation
pip install smarterservices-iam
Basic Usage
from smarterservices_iam import IamClient
# Initialize the client
iam = IamClient(
api_url='https://iam.smarterservices.com',
service_key='your-service-key'
)
# Check permissions
authorized = iam.authorize(
user_token='user-token',
action='sm:ReadUser',
resource='ssrn:ss:sm::578:user/8539699'
)
if authorized:
# User has permission
return get_user_data()
else:
raise PermissionError('Access denied')
Java
Maven Dependency
<dependency>
<groupId>com.smarterservices</groupId>
<artifactId>iam-client</artifactId>
<version>1.0.0</version>
</dependency>
Basic Usage
import com.smarterservices.iam.IamClient;
// Initialize the client
IamClient iam = new IamClient.Builder()
.apiUrl("https://iam.smarterservices.com")
.serviceKey("your-service-key")
.build();
// Check permissions
boolean authorized = iam.authorize(
userToken,
"sm:ReadAssessment",
"ssrn:ss:sm::578:assessment/AS123456"
);
if (authorized) {
// User has permission
return getAssessmentData();
} else {
throw new SecurityException("Access denied");
}
Server-Side Implementation
Express.js Middleware
const { IamService } = require('@smarterservices/iam-node');
const iam = new IamService({
apiUrl: process.env.IAM_API_URL,
serviceKey: process.env.IAM_SERVICE_KEY
});
// Generic IAM middleware
function requirePermission(action, resourceTemplate) {
return async (req, res, next) => {
try {
// Build resource string from template
const resource = resourceTemplate.replace(/\{(\w+)\}/g, (match, key) => {
return req.params[key] || req.user[key] || match;
});
const authorized = await iam.authorize(
req.user.token,
action,
resource
);
if (!authorized) {
return res.status(403).json({
error: 'Forbidden',
message: `Insufficient permissions for ${action} on ${resource}`
});
}
next();
} catch (error) {
console.error('IAM authorization error:', error);
res.status(500).json({ error: 'Authorization service unavailable' });
}
};
}
// Usage examples
app.get('/api/sessions/:id',
requirePermission('sp:ReadSession', 'ssrn:ss:sp::{accountId}:session/{id}'),
getSession
);
app.delete('/api/sessions/:id',
requirePermission('sp:DeleteSession', 'ssrn:ss:sp::{accountId}:session/{id}'),
deleteSession
);
FastAPI (Python)
from fastapi import FastAPI, Depends, HTTPException
from smarterservices_iam import IamClient
app = FastAPI()
iam = IamClient(
api_url=os.getenv('IAM_API_URL'),
service_key=os.getenv('IAM_SERVICE_KEY')
)
def require_permission(action: str, resource_template: str):
def dependency(user_token: str, **path_params):
# Build resource string
resource = resource_template.format(**path_params)
if not iam.authorize(user_token, action, resource):
raise HTTPException(status_code=403, detail="Forbidden")
return True
return dependency
@app.get("/sessions/{session_id}")
async def get_session(
session_id: str,
authorized: bool = Depends(
require_permission(
"sp:ReadSession",
"ssrn:ss:sp::{account_id}:session/{session_id}"
)
)
):
return get_session_data(session_id)
Spring Boot (Java)
@RestController
@RequestMapping("/api/sessions")
public class SessionController {
@Autowired
private IamService iamService;
@GetMapping("/{id}")
@RequirePermission(action = "sp:ReadSession", resource = "ssrn:ss:sp::{accountId}:session/{id}")
public ResponseEntity<Session> getSession(@PathVariable String id) {
return ResponseEntity.ok(sessionService.getSession(id));
}
@DeleteMapping("/{id}")
@RequirePermission(action = "sp:DeleteSession", resource = "ssrn:ss:sp::{accountId}:session/{id}")
public ResponseEntity<Void> deleteSession(@PathVariable String id) {
sessionService.deleteSession(id);
return ResponseEntity.noContent().build();
}
}
// Custom annotation
@Target(ElementType.METHOD)
@Retention(RetentionPolicy.RUNTIME)
public @interface RequirePermission {
String action();
String resource();
}
// Aspect for handling the annotation
@Aspect
@Component
public class IamAspect {
@Autowired
private IamService iamService;
@Before("@annotation(requirePermission)")
public void checkPermission(JoinPoint joinPoint, RequirePermission requirePermission) {
// Extract user token and build resource string
String userToken = getCurrentUserToken();
String resource = buildResourceString(requirePermission.resource(), joinPoint.getArgs());
if (!iamService.authorize(userToken, requirePermission.action(), resource)) {
throw new SecurityException("Access denied");
}
}
}
Frontend Integration
React Hook
import { useState, useEffect } from 'react';
import { useIamClient } from '@smarterservices/iam-react';
export function usePermission(action, resource) {
const [hasPermission, setHasPermission] = useState(false);
const [loading, setLoading] = useState(true);
const iamClient = useIamClient();
useEffect(() => {
async function checkPermission() {
try {
const authorized = await iamClient.authorize(action, resource);
setHasPermission(authorized);
} catch (error) {
console.error('Permission check failed:', error);
setHasPermission(false);
} finally {
setLoading(false);
}
}
checkPermission();
}, [action, resource, iamClient]);
return { hasPermission, loading };
}
// Usage
function SessionActions({ sessionId }) {
const { hasPermission: canDelete, loading } = usePermission(
'sp:DeleteSession',
`ssrn:ss:sp::578:session/${sessionId}`
);
if (loading) return <div>Loading...</div>;
return (
<div>
{canDelete && (
<button onClick={handleDelete}>Delete Session</button>
)}
</div>
);
}
Vue.js Directive
import { IamClient } from '@smarterservices/iam-client';
const iam = new IamClient({
apiUrl: process.env.VUE_APP_IAM_API_URL,
token: () => store.getters.authToken
});
// Custom directive
Vue.directive('iam', {
async bind(el, binding) {
const { action, resource } = binding.value;
try {
const authorized = await iam.authorize(action, resource);
if (!authorized) {
el.style.display = 'none';
}
} catch (error) {
console.error('IAM check failed:', error);
el.style.display = 'none';
}
}
});
// Usage in template
<template>
<div>
<button
v-iam="{ action: 'sp:DeleteSession', resource: `ssrn:ss:sp::578:session/${sessionId}` }"
@click="deleteSession"
>
Delete Session
</button>
</div>
</template>
Configuration
Environment Variables
# IAM Service Configuration
IAM_API_URL=https://iam.smarterservices.com
IAM_SERVICE_KEY=your-service-key
IAM_CACHE_TTL=300
IAM_TIMEOUT=5000
# Application Configuration
APP_ACCOUNT_ID=578
APP_SERVICE=sp
APP_REGION=us-east-1
Configuration File
{
"iam": {
"apiUrl": "https://iam.smarterservices.com",
"serviceKey": "${IAM_SERVICE_KEY}",
"cache": {
"enabled": true,
"ttl": 300,
"maxSize": 1000
},
"timeout": 5000,
"retries": 3
},
"application": {
"accountId": "578",
"service": "sp",
"region": "us-east-1"
}
}
Caching
Client-Side Caching
import { IamClient } from '@smarterservices/iam-client';
const iam = new IamClient({
apiUrl: 'https://iam.smarterservices.com',
token: 'your-token',
cache: {
enabled: true,
ttl: 300, // 5 minutes
maxSize: 1000
}
});
// Permissions are cached automatically
const canRead = await iam.authorize('sp:ReadSession', 'ssrn:ss:sp::578:session/ES123');
Server-Side Caching
const Redis = require('redis');
const { IamService } = require('@smarterservices/iam-node');
const redis = Redis.createClient();
const iam = new IamService({
apiUrl: 'https://iam.smarterservices.com',
serviceKey: 'your-service-key',
cache: {
get: async (key) => {
const value = await redis.get(key);
return value ? JSON.parse(value) : null;
},
set: async (key, value, ttl) => {
await redis.setex(key, ttl, JSON.stringify(value));
}
}
});
Error Handling
Client-Side Error Handling
try {
const authorized = await iam.authorize(action, resource);
if (authorized) {
// Proceed with action
} else {
// Show access denied message
showError('You do not have permission to perform this action');
}
} catch (error) {
if (error.code === 'NETWORK_ERROR') {
// Handle network issues
showError('Unable to verify permissions. Please try again.');
} else if (error.code === 'INVALID_TOKEN') {
// Handle authentication issues
redirectToLogin();
} else {
// Handle other errors
console.error('IAM error:', error);
showError('Permission check failed');
}
}
Server-Side Error Handling
app.use((error, req, res, next) => {
if (error.name === 'IamError') {
switch (error.code) {
case 'FORBIDDEN':
return res.status(403).json({
error: 'Forbidden',
message: 'Insufficient permissions'
});
case 'UNAUTHORIZED':
return res.status(401).json({
error: 'Unauthorized',
message: 'Invalid or expired token'
});
case 'SERVICE_UNAVAILABLE':
return res.status(503).json({
error: 'Service Unavailable',
message: 'Authorization service is temporarily unavailable'
});
default:
return res.status(500).json({
error: 'Internal Server Error',
message: 'Authorization check failed'
});
}
}
next(error);
});
Testing
Unit Testing
// Mock IAM client for testing
jest.mock('@smarterservices/iam-client');
const { IamClient } = require('@smarterservices/iam-client');
describe('Session Controller', () => {
beforeEach(() => {
IamClient.mockClear();
});
test('should allow access with proper permissions', async () => {
const mockAuthorize = jest.fn().mockResolvedValue(true);
IamClient.mockImplementation(() => ({
authorize: mockAuthorize
}));
const response = await request(app)
.get('/api/sessions/ES123456')
.set('Authorization', 'Bearer valid-token')
.expect(200);
expect(mockAuthorize).toHaveBeenCalledWith(
'sp:ReadSession',
'ssrn:ss:sp::578:session/ES123456'
);
});
test('should deny access without permissions', async () => {
const mockAuthorize = jest.fn().mockResolvedValue(false);
IamClient.mockImplementation(() => ({
authorize: mockAuthorize
}));
await request(app)
.get('/api/sessions/ES123456')
.set('Authorization', 'Bearer valid-token')
.expect(403);
});
});
Integration Testing
describe('IAM Integration', () => {
test('should integrate with real IAM service', async () => {
const iam = new IamClient({
apiUrl: process.env.TEST_IAM_API_URL,
token: process.env.TEST_TOKEN
});
const authorized = await iam.authorize(
'sp:ReadSession',
'ssrn:ss:sp::578:session/test-session'
);
expect(typeof authorized).toBe('boolean');
});
});
Best Practices
1. Use Least Privilege
Only request the minimum permissions needed for functionality.
2. Cache Appropriately
Cache permission results to improve performance, but ensure cache invalidation works correctly.
3. Handle Errors Gracefully
Always handle IAM service failures gracefully with appropriate fallbacks.
4. Validate Resources
Ensure resource strings are properly formatted and validated.
Monitor IAM service response times and implement circuit breakers if needed.
6. Secure Service Keys
Store service keys securely and rotate them regularly.
7. Log Security Events
Log all authorization decisions for audit and debugging purposes.
Troubleshooting
Common Issues
- Invalid Resource Format: Ensure SSRN format is correct
- Token Expiration: Handle token refresh properly
- Network Timeouts: Implement proper timeout and retry logic
- Cache Inconsistency: Ensure cache invalidation works correctly
- Permission Lag: Account for eventual consistency in permission updates
Debugging
- Enable Debug Logging: Set log level to debug for detailed information
- Check Network Connectivity: Verify IAM service is reachable
- Validate Tokens: Ensure authentication tokens are valid
- Test Policies: Use IAM policy simulator for testing
- Monitor Metrics: Track authorization success/failure rates