Appearance
Security and Privacy
Security Mechanisms
Authentication Systems
JWT (JSON Web Tokens)
Oktuple uses JWT for secure, stateless authentication across the system.
Token Structure:
typescript
interface JWTPayload {
sub: string; // Subject (User ID)
iss: string; // Issuer (Oktuple)
aud: string; // Audience
exp: number; // Expiration time
iat: number; // Issued at
scope: string[]; // Permissions
org_id: string; // Organization ID
jti: string; // JWT ID (unique identifier)
}
// Token lifecycle management
class TokenManager {
async generateToken(user: User, permissions: Permission[]): Promise<string> {
const payload: JWTPayload = {
sub: user.id,
iss: "oktuple",
aud: "oktuple-users",
exp: Math.floor(Date.now() / 1000) + 60 * 60, // 1 hour
iat: Math.floor(Date.now() / 1000),
scope: permissions.map((p) => p.name),
org_id: user.organizationId,
jti: generateUUID(),
};
return jwt.sign(payload, this.secretKey, { algorithm: "HS256" });
}
async validateToken(token: string): Promise<JWTPayload> {
try {
const decoded = jwt.verify(token, this.secretKey, {
algorithms: ["HS256"],
});
return decoded as JWTPayload;
} catch (error) {
throw new Error("Invalid token");
}
}
async refreshToken(refreshToken: string): Promise<string> {
const storedToken = await this.getStoredRefreshToken(refreshToken);
if (!storedToken || storedToken.expired) {
throw new Error("Invalid refresh token");
}
const user = await this.getUserById(storedToken.userId);
const permissions = await this.getUserPermissions(user.id);
return this.generateToken(user, permissions);
}
}OAuth 2.0 Integration
Support for industry-standard OAuth 2.0 flows for third-party integrations.
Supported Flows:
typescript
enum OAuthFlow {
AUTHORIZATION_CODE = "authorization_code",
CLIENT_CREDENTIALS = "client_credentials",
REFRESH_TOKEN = "refresh_token",
IMPLICIT = "implicit",
}
interface OAuthClient {
id: string;
name: string;
redirectUris: string[];
clientSecret: string;
allowedFlows: OAuthFlow[];
scopes: string[];
active: boolean;
}
class OAuthService {
async authorizeClient(
clientId: string,
redirectUri: string,
scope: string[],
state: string
): Promise<string> {
const client = await this.validateClient(clientId, redirectUri);
const authCode = await this.generateAuthorizationCode({
clientId: client.id,
scope,
state,
expiresAt: new Date(Date.now() + 10 * 60 * 1000), // 10 minutes
});
return this.buildRedirectUrl(redirectUri, authCode, state);
}
async exchangeCodeForToken(
code: string,
clientId: string,
clientSecret: string
): Promise<AccessToken> {
const authCode = await this.validateAuthorizationCode(code, clientId);
const client = await this.validateClientCredentials(clientId, clientSecret);
const accessToken = await this.generateAccessToken({
userId: authCode.userId,
clientId: client.id,
scope: authCode.scope,
});
await this.invalidateAuthorizationCode(code);
return accessToken;
}
}Multi-Factor Authentication (MFA)
Enhanced security through multiple authentication factors.
MFA Implementation:
typescript
interface MFAConfig {
enabled: boolean;
methods: MFAMethod[];
backupCodes: string[];
lastUsed: Date;
}
enum MFAMethod {
TOTP = "totp", // Time-based One-Time Password
SMS = "sms", // SMS verification
EMAIL = "email", // Email verification
AUTHENTICATOR_APP = "authenticator_app", // Google Authenticator, etc.
}
class MFAService {
async setupTOTP(userId: string): Promise<{ secret: string; qrCode: string }> {
const secret = speakeasy.generateSecret({
name: "Oktuple",
issuer: "oktuple.com",
});
await this.storeMFASecret(userId, secret.base32);
const qrCode = await this.generateQRCode(secret.otpauth_url);
return {
secret: secret.base32,
qrCode,
};
}
async verifyTOTP(userId: string, token: string): Promise<boolean> {
const secret = await this.getMFASecret(userId);
return speakeasy.totp.verify({
secret,
encoding: "base32",
token,
window: 2, // Allow 2 time steps for clock skew
});
}
async generateBackupCodes(userId: string): Promise<string[]> {
const codes = Array.from({ length: 10 }, () =>
crypto.randomBytes(4).toString("hex").toUpperCase()
);
const hashedCodes = await Promise.all(
codes.map((code) => bcrypt.hash(code, 10))
);
await this.storeBackupCodes(userId, hashedCodes);
return codes;
}
}Authorization Models
Role-Based Access Control (RBAC)
Granular permission system based on user roles and organizational context.
RBAC Implementation:
typescript
interface Role {
id: string;
name: string;
description: string;
permissions: Permission[];
organizationId: string;
inheritedFrom?: string;
active: boolean;
}
interface Permission {
id: string;
resource: string;
action: string;
conditions?: PermissionCondition[];
scope: "global" | "organization" | "project" | "user";
}
interface PermissionCondition {
field: string;
operator: "eq" | "ne" | "in" | "not_in" | "gt" | "lt" | "gte" | "lte";
value: any;
}
class RBACService {
async checkPermission(
userId: string,
resource: string,
action: string,
context: any
): Promise<boolean> {
const userRoles = await this.getUserRoles(userId);
for (const role of userRoles) {
const hasPermission = await this.roleHasPermission(
role,
resource,
action,
context
);
if (hasPermission) return true;
}
return false;
}
private async roleHasPermission(
role: Role,
resource: string,
action: string,
context: any
): Promise<boolean> {
const permission = role.permissions.find(
(p) => p.resource === resource && p.action === action
);
if (!permission) return false;
// Check conditions if they exist
if (permission.conditions && permission.conditions.length > 0) {
return this.evaluateConditions(permission.conditions, context);
}
return true;
}
private evaluateConditions(
conditions: PermissionCondition[],
context: any
): boolean {
return conditions.every((condition) => {
const fieldValue = this.getNestedValue(context, condition.field);
switch (condition.operator) {
case "eq":
return fieldValue === condition.value;
case "ne":
return fieldValue !== condition.value;
case "in":
return (
Array.isArray(condition.value) &&
condition.value.includes(fieldValue)
);
case "not_in":
return (
Array.isArray(condition.value) &&
!condition.value.includes(fieldValue)
);
case "gt":
return fieldValue > condition.value;
case "lt":
return fieldValue < condition.value;
case "gte":
return fieldValue >= condition.value;
case "lte":
return fieldValue <= condition.value;
default:
return false;
}
});
}
}