Analytics Module
The Analytics module provides methods to retrieve login and authentication analytics for your organization. Track user authentication patterns, monitor service usage, and analyze OAuth provider adoption programmatically.
Overview
All analytics methods require authentication and that the authenticated user is a member of the organization being queried. The module provides time-series data, aggregated statistics, and recent event listings.
Module: sso.analytics
Authentication: Required (JWT token must be set)
Default Behavior:
- Date-ranged methods default to the last 30 days when dates are not specified
- All dates use
YYYY-MM-DDformat - Results are returned as arrays of typed objects
Methods
getLoginTrends()
Retrieve daily login counts grouped by date. Returns a time series of authentication events useful for visualizing login activity trends over time.
Signature:
getLoginTrends(
orgSlug: string,
params?: AnalyticsQuery
): Promise<LoginTrendPoint[]>
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
orgSlug |
string | Yes | Organization slug |
params |
AnalyticsQuery |
No | Optional query parameters |
params.start_date |
string | No | Start date in YYYY-MM-DD format (defaults to 30 days ago) |
params.end_date |
string | No | End date in YYYY-MM-DD format (defaults to today) |
Returns:
Promise<LoginTrendPoint[]> - Array of login trend data points
LoginTrendPoint Interface:
interface LoginTrendPoint {
date: string; // Date in YYYY-MM-DD format
count: number; // Total number of logins on this date
}
Example:
import { SsoClient } from '@drmhse/sso-sdk';
const sso = new SsoClient({
baseURL: 'https://authos.example.com',
token: 'your-jwt-token'
});
// Get login trends for the last 30 days (default)
const trends = await sso.analytics.getLoginTrends('acme-corp');
trends.forEach(point => {
console.log(`${point.date}: ${point.count} logins`);
});
// Output:
// 2025-01-01: 45 logins
// 2025-01-02: 52 logins
// 2025-01-03: 38 logins
// Get login trends for a specific date range
const januaryTrends = await sso.analytics.getLoginTrends('acme-corp', {
start_date: '2025-01-01',
end_date: '2025-01-31'
});
console.log(`Total days with activity: ${januaryTrends.length}`);
console.log(`Total logins: ${januaryTrends.reduce((sum, p) => sum + p.count, 0)}`);
React Example:
import { useState, useEffect } from 'react';
import { SsoClient, LoginTrendPoint } from '@drmhse/sso-sdk';
function LoginTrendsChart({ orgSlug, ssoClient }: {
orgSlug: string;
ssoClient: SsoClient;
}) {
const [trends, setTrends] = useState<LoginTrendPoint[]>([]);
const [loading, setLoading] = useState(true);
useEffect(() => {
async function fetchTrends() {
try {
const data = await ssoClient.analytics.getLoginTrends(orgSlug, {
start_date: '2025-01-01',
end_date: '2025-01-31'
});
setTrends(data);
} catch (error) {
console.error('Failed to fetch login trends:', error);
} finally {
setLoading(false);
}
}
fetchTrends();
}, [orgSlug, ssoClient]);
if (loading) return <div>Loading trends...</div>;
return (
<div>
<h2>Login Trends - January 2025</h2>
<ul>
{trends.map(point => (
<li key={point.date}>
{point.date}: {point.count} logins
</li>
))}
</ul>
</div>
);
}
Throws:
SsoApiErrorwith status401- Not authenticatedSsoApiErrorwith status403- User is not a member of the organizationSsoApiErrorwith status404- Organization not found
getLoginsByService()
Get login counts grouped by service. Shows which services in your organization have the most authentication activity.
Signature:
getLoginsByService(
orgSlug: string,
params?: AnalyticsQuery
): Promise<LoginsByService[]>
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
orgSlug |
string | Yes | Organization slug |
params |
AnalyticsQuery |
No | Optional query parameters |
params.start_date |
string | No | Start date in YYYY-MM-DD format (defaults to 30 days ago) |
params.end_date |
string | No | End date in YYYY-MM-DD format (defaults to today) |
Returns:
Promise<LoginsByService[]> - Array of login counts per service
LoginsByService Interface:
interface LoginsByService {
service_id: string; // Unique identifier (UUID) of the service
service_name: string; // Display name of the service
count: number; // Total number of logins for this service
}
Example:
import { SsoClient } from '@drmhse/sso-sdk';
const sso = new SsoClient({
baseURL: 'https://authos.example.com',
token: 'your-jwt-token'
});
// Get logins by service for the last 30 days
const byService = await sso.analytics.getLoginsByService('acme-corp');
// Sort by most-used services
const sorted = byService.sort((a, b) => b.count - a.count);
console.log('Top 3 most-used services:');
sorted.slice(0, 3).forEach((service, index) => {
console.log(`${index + 1}. ${service.service_name}: ${service.count} logins`);
});
// Output:
// 1. Main Application: 1247 logins
// 2. Mobile App: 856 logins
// 3. Admin Dashboard: 234 logins
// Get logins for a specific quarter
const q1Data = await sso.analytics.getLoginsByService('acme-corp', {
start_date: '2025-01-01',
end_date: '2025-03-31'
});
const totalLogins = q1Data.reduce((sum, s) => sum + s.count, 0);
console.log(`Q1 Total Logins: ${totalLogins}`);
React Example:
import { useState, useEffect } from 'react';
import { SsoClient, LoginsByService } from '@drmhse/sso-sdk';
function ServiceUsageChart({ orgSlug, ssoClient }: {
orgSlug: string;
ssoClient: SsoClient;
}) {
const [services, setServices] = useState<LoginsByService[]>([]);
useEffect(() => {
async function fetchData() {
const data = await ssoClient.analytics.getLoginsByService(orgSlug);
// Sort by usage
setServices(data.sort((a, b) => b.count - a.count));
}
fetchData();
}, [orgSlug, ssoClient]);
const totalLogins = services.reduce((sum, s) => sum + s.count, 0);
return (
<div>
<h2>Service Usage (Last 30 Days)</h2>
<p>Total Logins: {totalLogins}</p>
<table>
<thead>
<tr>
<th>Service</th>
<th>Logins</th>
<th>Percentage</th>
</tr>
</thead>
<tbody>
{services.map(service => (
<tr key={service.service_id}>
<td>{service.service_name}</td>
<td>{service.count}</td>
<td>{((service.count / totalLogins) * 100).toFixed(1)}%</td>
</tr>
))}
</tbody>
</table>
</div>
);
}
Throws:
SsoApiErrorwith status401- Not authenticatedSsoApiErrorwith status403- User is not a member of the organizationSsoApiErrorwith status404- Organization not found
getLoginsByProvider()
Get login counts grouped by OAuth provider. Shows which authentication providers (GitHub, Google, Microsoft) are being used by your users.
Signature:
getLoginsByProvider(
orgSlug: string,
params?: AnalyticsQuery
): Promise<LoginsByProvider[]>
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
orgSlug |
string | Yes | Organization slug |
params |
AnalyticsQuery |
No | Optional query parameters |
params.start_date |
string | No | Start date in YYYY-MM-DD format (defaults to 30 days ago) |
params.end_date |
string | No | End date in YYYY-MM-DD format (defaults to today) |
Returns:
Promise<LoginsByProvider[]> - Array of login counts per OAuth provider
LoginsByProvider Interface:
interface LoginsByProvider {
provider: 'github' | 'google' | 'microsoft'; // OAuth provider name
count: number; // Total number of logins using this provider
}
Example:
import { SsoClient } from '@drmhse/sso-sdk';
const sso = new SsoClient({
baseURL: 'https://authos.example.com',
token: 'your-jwt-token'
});
// Get logins by provider for the last 30 days
const byProvider = await sso.analytics.getLoginsByProvider('acme-corp');
byProvider.forEach(provider => {
console.log(`${provider.provider}: ${provider.count} logins`);
});
// Output:
// github: 1523 logins
// google: 892 logins
// microsoft: 234 logins
// Calculate provider adoption percentages
const total = byProvider.reduce((sum, p) => sum + p.count, 0);
console.log('\nProvider Adoption:');
byProvider.forEach(provider => {
const percentage = ((provider.count / total) * 100).toFixed(1);
console.log(`${provider.provider}: ${percentage}%`);
});
// Get provider usage for a specific month
const decemberData = await sso.analytics.getLoginsByProvider('acme-corp', {
start_date: '2024-12-01',
end_date: '2024-12-31'
});
const mostPopular = decemberData.reduce((prev, current) =>
current.count > prev.count ? current : prev
);
console.log(`Most popular provider in December: ${mostPopular.provider}`);
React Example:
import { useState, useEffect } from 'react';
import { SsoClient, LoginsByProvider } from '@drmhse/sso-sdk';
function ProviderDistribution({ orgSlug, ssoClient }: {
orgSlug: string;
ssoClient: SsoClient;
}) {
const [providers, setProviders] = useState<LoginsByProvider[]>([]);
useEffect(() => {
async function fetchData() {
const data = await ssoClient.analytics.getLoginsByProvider(orgSlug);
setProviders(data);
}
fetchData();
}, [orgSlug, ssoClient]);
const totalLogins = providers.reduce((sum, p) => sum + p.count, 0);
const providerColors: Record<string, string> = {
github: '#333',
google: '#4285F4',
microsoft: '#00A4EF'
};
return (
<div>
<h2>OAuth Provider Distribution</h2>
<div className="provider-stats">
{providers.map(provider => {
const percentage = ((provider.count / totalLogins) * 100).toFixed(1);
return (
<div
key={provider.provider}
className="provider-card"
style={{ borderLeft: `4px solid ${providerColors[provider.provider]}` }}
>
<h3>{provider.provider}</h3>
<p className="count">{provider.count} logins</p>
<p className="percentage">{percentage}%</p>
</div>
);
})}
</div>
</div>
);
}
Throws:
SsoApiErrorwith status401- Not authenticatedSsoApiErrorwith status403- User is not a member of the organizationSsoApiErrorwith status404- Organization not found
getRecentLogins()
Get the most recent login events for your organization. Returns detailed information about individual authentication events, useful for monitoring real-time activity and security auditing.
Signature:
getRecentLogins(
orgSlug: string,
params?: AnalyticsQuery
): Promise<RecentLogin[]>
Parameters:
| Parameter | Type | Required | Description |
|---|---|---|---|
orgSlug |
string | Yes | Organization slug |
params |
AnalyticsQuery |
No | Optional query parameters |
params.limit |
number | No | Maximum number of login events to return (defaults to 10) |
Returns:
Promise<RecentLogin[]> - Array of recent login events, ordered by most recent first
RecentLogin Interface:
interface RecentLogin {
id: string; // Unique identifier (UUID) of the login event
user_id: string; // Unique identifier of the user who logged in
service_id: string; // Unique identifier (UUID) of the service
provider: string; // OAuth provider used (github, google, microsoft)
created_at: string; // ISO 8601 timestamp of when the login occurred
}
Example:
import { SsoClient } from '@drmhse/sso-sdk';
const sso = new SsoClient({
baseURL: 'https://authos.example.com',
token: 'your-jwt-token'
});
// Get the 10 most recent logins (default)
const recentLogins = await sso.analytics.getRecentLogins('acme-corp');
console.log('Recent Authentication Activity:');
recentLogins.forEach(login => {
const timestamp = new Date(login.created_at).toLocaleString();
console.log(`${timestamp} - User ${login.user_id} via ${login.provider}`);
});
// Output:
// 1/15/2025, 2:32:18 PM - User user_123abc via github
// 1/15/2025, 2:28:45 PM - User user_456def via google
// 1/15/2025, 2:15:22 PM - User user_789ghi via microsoft
// Get the 50 most recent logins for detailed monitoring
const extended = await sso.analytics.getRecentLogins('acme-corp', {
limit: 50
});
// Analyze provider distribution in recent activity
const providerCounts: Record<string, number> = {};
extended.forEach(login => {
providerCounts[login.provider] = (providerCounts[login.provider] || 0) + 1;
});
console.log('Recent activity by provider:', providerCounts);
// Output: { github: 25, google: 18, microsoft: 7 }
React Example (Real-time Activity Feed):
import { useState, useEffect } from 'react';
import { SsoClient, RecentLogin } from '@drmhse/sso-sdk';
function ActivityFeed({ orgSlug, ssoClient }: {
orgSlug: string;
ssoClient: SsoClient;
}) {
const [logins, setLogins] = useState<RecentLogin[]>([]);
const [refreshInterval, setRefreshInterval] = useState(10000); // 10 seconds
useEffect(() => {
async function fetchRecentLogins() {
const data = await ssoClient.analytics.getRecentLogins(orgSlug, {
limit: 20
});
setLogins(data);
}
// Initial fetch
fetchRecentLogins();
// Poll for updates
const interval = setInterval(fetchRecentLogins, refreshInterval);
return () => clearInterval(interval);
}, [orgSlug, ssoClient, refreshInterval]);
const formatTime = (timestamp: string) => {
const date = new Date(timestamp);
const now = new Date();
const diffMs = now.getTime() - date.getTime();
const diffMins = Math.floor(diffMs / 60000);
if (diffMins < 1) return 'Just now';
if (diffMins < 60) return `${diffMins}m ago`;
if (diffMins < 1440) return `${Math.floor(diffMins / 60)}h ago`;
return date.toLocaleDateString();
};
const providerIcons: Record<string, string> = {
github: '🐙',
google: '🔍',
microsoft: '🪟'
};
return (
<div className="activity-feed">
<div className="feed-header">
<h2>Recent Authentication Activity</h2>
<button onClick={() => setRefreshInterval(i => i === 10000 ? 30000 : 10000)}>
{refreshInterval === 10000 ? 'Slow refresh' : 'Fast refresh'}
</button>
</div>
<ul className="login-list">
{logins.map(login => (
<li key={login.id} className="login-item">
<span className="provider-icon">
{providerIcons[login.provider]}
</span>
<div className="login-details">
<strong>User {login.user_id}</strong>
<span className="provider-name">via {login.provider}</span>
</div>
<time className="login-time">
{formatTime(login.created_at)}
</time>
</li>
))}
</ul>
</div>
);
}
Security Monitoring Example:
// Monitor for suspicious activity (e.g., rapid logins from different providers)
async function monitorSuspiciousActivity(
orgSlug: string,
ssoClient: SsoClient
) {
const recentLogins = await ssoClient.analytics.getRecentLogins(orgSlug, {
limit: 100
});
// Group logins by user
const userLogins: Record<string, RecentLogin[]> = {};
recentLogins.forEach(login => {
if (!userLogins[login.user_id]) {
userLogins[login.user_id] = [];
}
userLogins[login.user_id].push(login);
});
// Check for suspicious patterns
Object.entries(userLogins).forEach(([userId, logins]) => {
// Multiple providers in short time
const uniqueProviders = new Set(logins.map(l => l.provider));
if (uniqueProviders.size >= 2 && logins.length > 5) {
console.warn(`Suspicious: User ${userId} used ${uniqueProviders.size} providers in ${logins.length} logins`);
}
// High frequency logins
if (logins.length > 10) {
const first = new Date(logins[logins.length - 1].created_at);
const last = new Date(logins[0].created_at);
const diffMinutes = (last.getTime() - first.getTime()) / 60000;
if (diffMinutes < 5) {
console.warn(`Suspicious: User ${userId} logged in ${logins.length} times in ${diffMinutes.toFixed(1)} minutes`);
}
}
});
}
Throws:
SsoApiErrorwith status401- Not authenticatedSsoApiErrorwith status403- User is not a member of the organizationSsoApiErrorwith status404- Organization not found
Type Definitions
AnalyticsQuery
Query parameters for analytics methods.
interface AnalyticsQuery {
start_date?: string; // Start date in YYYY-MM-DD format
end_date?: string; // End date in YYYY-MM-DD format
limit?: number; // Maximum number of results (for getRecentLogins)
}
Common Patterns
Building Analytics Dashboards
import { SsoClient } from '@drmhse/sso-sdk';
async function fetchAllAnalytics(orgSlug: string, ssoClient: SsoClient) {
// Fetch all analytics data in parallel
const [trends, byService, byProvider, recent] = await Promise.all([
ssoClient.analytics.getLoginTrends(orgSlug),
ssoClient.analytics.getLoginsByService(orgSlug),
ssoClient.analytics.getLoginsByProvider(orgSlug),
ssoClient.analytics.getRecentLogins(orgSlug, { limit: 20 })
]);
return { trends, byService, byProvider, recent };
}
// Usage in a React dashboard
function AnalyticsDashboard({ orgSlug, ssoClient }) {
const [analytics, setAnalytics] = useState(null);
useEffect(() => {
fetchAllAnalytics(orgSlug, ssoClient).then(setAnalytics);
}, [orgSlug, ssoClient]);
if (!analytics) return <div>Loading...</div>;
return (
<div className="dashboard">
<LoginTrendsChart data={analytics.trends} />
<ServiceUsageChart data={analytics.byService} />
<ProviderDistribution data={analytics.byProvider} />
<ActivityFeed data={analytics.recent} />
</div>
);
}
Custom Date Ranges
import { SsoClient } from '@drmhse/sso-sdk';
// Helper to format dates as YYYY-MM-DD
function formatDate(date: Date): string {
return date.toISOString().split('T')[0];
}
// Last 7 days
const lastWeek = {
start_date: formatDate(new Date(Date.now() - 7 * 24 * 60 * 60 * 1000)),
end_date: formatDate(new Date())
};
const weeklyTrends = await sso.analytics.getLoginTrends('acme-corp', lastWeek);
// Current month
const now = new Date();
const monthStart = new Date(now.getFullYear(), now.getMonth(), 1);
const thisMonth = {
start_date: formatDate(monthStart),
end_date: formatDate(now)
};
const monthlyData = await sso.analytics.getLoginsByService('acme-corp', thisMonth);
// Last quarter
const quarterEnd = new Date();
const quarterStart = new Date(quarterEnd);
quarterStart.setMonth(quarterStart.getMonth() - 3);
const quarterlyProvider = await sso.analytics.getLoginsByProvider('acme-corp', {
start_date: formatDate(quarterStart),
end_date: formatDate(quarterEnd)
});
Error Handling
import { SsoClient, SsoApiError } from '@drmhse/sso-sdk';
async function fetchAnalyticsSafely(orgSlug: string, ssoClient: SsoClient) {
try {
const trends = await ssoClient.analytics.getLoginTrends(orgSlug);
return { success: true, data: trends };
} catch (error) {
if (error instanceof SsoApiError) {
if (error.status === 403) {
return {
success: false,
error: 'You do not have permission to view analytics for this organization'
};
}
if (error.status === 404) {
return {
success: false,
error: 'Organization not found'
};
}
}
return {
success: false,
error: 'Failed to fetch analytics data'
};
}
}
Related Documentation
- API Analytics Reference - Backend API endpoints
- Organizations Module - Organization management
- Services Module - Service configuration
- Authentication Flows Guide - Understanding authentication