okta-mcp-server
Version:
Model Context Protocol (MCP) server for Okta API operations with support for bulk operations and caching
201 lines • 6.71 kB
JavaScript
import { faker } from '@faker-js/faker';
export class AuthSimulator {
config;
tokens;
constructor(config = {
requireAuth: true,
tokenExpiration: 3600, // 1 hour
}) {
this.config = config;
this.tokens = config.validTokens || new Map();
// Generate some default valid tokens
if (this.tokens.size === 0) {
this.generateDefaultTokens();
}
}
generateDefaultTokens() {
// API Token (SSWS)
const apiToken = {
token: '00QCjAl4MlV-WPXM0n-eiPGHlZ0y0WZgK-_8bCRjARmUAl11gkLlsEYamWLP',
type: 'SSWS',
expiresAt: Date.now() + 365 * 24 * 60 * 60 * 1000, // 1 year
userId: 'system',
};
this.tokens.set(apiToken.token, apiToken);
// OAuth Bearer tokens
const bearerTokens = [
{
token: 'eyJraWQiOiJyTzhPTGJmMFlfWVpGTF9XaVlsM1BjX0dKQ0xnU25YbTlTbGFLQUx',
type: 'Bearer',
expiresAt: Date.now() + this.config.tokenExpiration * 1000,
scopes: ['okta.users.read', 'okta.groups.read'],
clientId: '0oab8eb55Kb9jdMIr5d6',
},
{
token: 'eyJraWQiOiJBZG1pblRva2VuXzEyMzQ1Njc4OTAiLCJhbGciOiJSUzI1NiJ9',
type: 'Bearer',
expiresAt: Date.now() + this.config.tokenExpiration * 1000,
scopes: ['okta.users.manage', 'okta.groups.manage', 'okta.apps.manage'],
clientId: '0oab8eb55Kb9jdMIr5d7',
},
];
bearerTokens.forEach((token) => {
this.tokens.set(token.token, token);
});
}
validateAuth(authHeader) {
if (!this.config.requireAuth) {
return { valid: true };
}
if (!authHeader) {
return {
valid: false,
error: 'Authorization header required',
};
}
// Parse authorization header
const parts = authHeader.split(' ');
if (parts.length !== 2) {
return {
valid: false,
error: 'Invalid authorization header format',
};
}
const [type, tokenValue] = parts;
// Check token type
if (type !== 'SSWS' && type !== 'Bearer') {
return {
valid: false,
error: 'Invalid token type',
};
}
if (!tokenValue) {
return {
valid: false,
error: 'Missing token value',
};
}
// Look up token
const token = this.tokens.get(tokenValue);
if (!token) {
return {
valid: false,
error: 'Invalid token',
};
}
// Check token type matches
if (token.type !== type) {
return {
valid: false,
error: 'Token type mismatch',
};
}
// Check expiration
if (token.expiresAt < Date.now()) {
return {
valid: false,
error: 'Token expired',
};
}
return {
valid: true,
token,
};
}
validateScopes(token, requiredScopes) {
if (!token.scopes)
return true; // API tokens have all scopes
return requiredScopes.every((scope) => token.scopes.includes(scope) || token.scopes.includes('*') // Wildcard scope
);
}
// Generate a new access token
generateAccessToken(clientId, scopes, userId) {
const token = {
token: this.generateTokenString(),
type: 'Bearer',
expiresAt: Date.now() + this.config.tokenExpiration * 1000,
scopes,
clientId,
};
if (userId) {
token.userId = userId;
}
this.tokens.set(token.token, token);
return token;
}
// Generate a new API token
generateApiToken(userId) {
const token = {
token: this.generateApiTokenString(),
type: 'SSWS',
expiresAt: Date.now() + 365 * 24 * 60 * 60 * 1000, // 1 year
userId,
};
this.tokens.set(token.token, token);
return token;
}
// Revoke a token
revokeToken(tokenValue) {
return this.tokens.delete(tokenValue);
}
// Refresh a token
refreshToken(oldToken) {
const existing = this.tokens.get(oldToken);
if (!existing || existing.type !== 'Bearer') {
return null;
}
// Create new token with same properties but new value and expiration
const newToken = {
...existing,
token: this.generateTokenString(),
expiresAt: Date.now() + this.config.tokenExpiration * 1000,
};
// Remove old token and add new one
this.tokens.delete(oldToken);
this.tokens.set(newToken.token, newToken);
return newToken;
}
generateTokenString() {
// Simulate JWT-like token
const header = Buffer.from(JSON.stringify({
kid: faker.string.alphanumeric(20),
alg: 'RS256',
})).toString('base64url');
const payload = Buffer.from(JSON.stringify({
iss: 'https://dev-12345.okta.com',
iat: Math.floor(Date.now() / 1000),
exp: Math.floor((Date.now() + this.config.tokenExpiration * 1000) / 1000),
jti: faker.string.uuid(),
})).toString('base64url');
const signature = faker.string.alphanumeric(86);
return `${header}.${payload}.${signature}`;
}
generateApiTokenString() {
// Simulate Okta API token format
return `00${faker.string.alphanumeric(40)}_${faker.string.alphanumeric(8)}`;
}
// Get token info (for introspection endpoint)
introspectToken(tokenValue) {
const token = this.tokens.get(tokenValue);
if (!token) {
return {
active: false,
};
}
const active = token.expiresAt > Date.now();
return {
active,
scope: token.scopes?.join(' '),
client_id: token.clientId,
username: token.userId,
exp: Math.floor(token.expiresAt / 1000),
iat: Math.floor((token.expiresAt - this.config.tokenExpiration * 1000) / 1000),
sub: token.userId,
aud: 'api://default',
iss: 'https://dev-12345.okta.com/oauth2/default',
jti: faker.string.uuid(),
token_type: token.type,
};
}
}
//# sourceMappingURL=auth-simulator.js.map