CtrlK
BlogDocsLog inGet started
Tessl Logo

tessl/npm-oas

Comprehensive tooling for working with OpenAPI definitions

Pending

Quality

Pending

Does it follow best practices?

Impact

Pending

No eval scenarios have been run

Overview
Eval results
Files

security-authentication.mddocs/

Security and Authentication

Security scheme parsing, authentication handling, and user-based auth token extraction for OpenAPI operations.

Capabilities

Get Security Requirements

Retrieve security requirements for an operation with fallback to global API security.

/**
 * Get security requirements for this operation
 * @returns Array of security requirement objects (OR groups containing AND requirements)
 */
getSecurity(): SecurityRequirementObject[];

Usage Examples:

const operation = oas.operation("/users", "post");
const security = operation.getSecurity();

security.forEach((requirement, index) => {
  console.log(`Security Option ${index + 1}:`);
  Object.entries(requirement).forEach(([schemeName, scopes]) => {
    console.log(`  ${schemeName}: ${scopes.join(', ') || 'no scopes'}`);
  });
});

// Check if operation requires authentication
const requiresAuth = security.length > 0;
console.log(`Authentication required: ${requiresAuth}`);

Get Security with Type Information

Get security requirements with resolved type information and scheme details.

/**
 * Get security requirements with type information
 * @param filterInvalid - Filter out invalid/nonexistent security schemes
 * @returns Array of OR groups containing AND requirements with type info
 */
getSecurityWithTypes(
  filterInvalid?: boolean
): Array<Array<{security: KeyedSecuritySchemeObject, type: SecurityType} | false> | false>;

Usage Examples:

const securityWithTypes = operation.getSecurityWithTypes(true); // Filter invalid schemes

securityWithTypes.forEach((orGroup, index) => {
  if (!orGroup) return;
  
  console.log(`Security Option ${index + 1}:`);
  orGroup.forEach(andRequirement => {
    if (!andRequirement) return;
    
    const { security, type } = andRequirement;
    console.log(`  Type: ${type}`);
    console.log(`  Scheme: ${security._key}`);
    console.log(`  Scopes: ${security._requirements?.join(', ') || 'none'}`);
    
    // Type-specific information
    if (type === 'Bearer' && security.scheme === 'bearer') {
      console.log(`  Bearer format: ${security.bearerFormat || 'not specified'}`);
    } else if (type === 'Header' && security.in === 'header') {
      console.log(`  Header name: ${security.name}`);
    }
  });
});

Prepare Security by Type

Get security schemes organized by type for easier processing.

/**
 * Organize security schemes by type
 * @returns Object with security types as keys and arrays of schemes as values
 */
prepareSecurity(): Record<SecurityType, KeyedSecuritySchemeObject[]>;

Usage Examples:

const securityByType = operation.prepareSecurity();

// Handle different security types
if (securityByType.Bearer) {
  console.log("Bearer tokens supported:");
  securityByType.Bearer.forEach(scheme => {
    console.log(`  ${scheme._key}: ${scheme.bearerFormat || 'JWT'}`);
  });
}

if (securityByType.Header) {
  console.log("API key headers:");
  securityByType.Header.forEach(scheme => {
    console.log(`  ${scheme.name}: ${scheme._key}`);
  });
}

if (securityByType.OAuth2) {
  console.log("OAuth2 flows:");
  securityByType.OAuth2.forEach(scheme => {
    if ('flows' in scheme) {
      Object.keys(scheme.flows || {}).forEach(flow => {
        console.log(`  ${scheme._key}: ${flow} flow`);
      });
    }
  });
}

Get Authentication for User

Extract authentication credentials for a specific user from the API definition.

/**
 * Get authentication credentials for a user
 * @param user - User information with potential auth credentials
 * @param selectedApp - Specific app/key to use (optional)
 * @returns Authentication data formatted for HAR requests
 */
getAuth(user: User, selectedApp?: number | string): AuthForHAR;

Usage Examples:

// User with various auth methods
const user = {
  apiKey: "sk-1234567890abcdef",
  bearerToken: "eyJhbGciOiJIUzI1NiIs...",
  keys: [
    { name: "app1", user: "john", pass: "secret123" },
    { name: "app2", user: "jane", pass: "password456" }
  ]
};

// Get auth for user
const auth = oas.getAuth(user);
console.log("Authentication data:", auth);

// Get auth for specific app
const appAuth = oas.getAuth(user, "app1");
console.log("App-specific auth:", appAuth);

// Use in HTTP requests
const headers = {};
Object.entries(auth).forEach(([key, value]) => {
  if (typeof value === 'string') {
    headers[key] = value;
  } else if (value && typeof value === 'object' && 'user' in value) {
    // Basic auth
    const credentials = btoa(`${value.user}:${value.pass}`);
    headers['Authorization'] = `Basic ${credentials}`;
  }
});

Security Type Definitions

/** Security scheme types */
type SecurityType = 'apiKey' | 'Basic' | 'Bearer' | 'Cookie' | 'Header' | 'http' | 'OAuth2' | 'Query';

/** Security scheme with additional metadata */
interface KeyedSecuritySchemeObject extends SecuritySchemeObject {
  /** The key for the security scheme */
  _key: string;
  /** Required scopes for OAuth2 */
  _requirements?: string[];
  /** Default value for authentication */
  'x-default'?: number | string;
}

/** User information for authentication */
interface User {
  [key: string]: unknown;
  /** Array of app-specific credentials */
  keys?: {
    [key: string]: unknown;
    name: number | string;
    pass?: number | string;
    user?: number | string;
  }[];
}

/** Authentication data for HAR format */
type AuthForHAR = Record<string, number | string | { pass?: string; user?: string }>;

/** Security requirement object */
type SecurityRequirementObject = Record<string, string[]>;

Advanced Security Handling

OAuth2 Flow Analysis

const securityByType = operation.prepareSecurity();

if (securityByType.OAuth2) {
  securityByType.OAuth2.forEach(scheme => {
    if ('flows' in scheme && scheme.flows) {
      console.log(`OAuth2 scheme: ${scheme._key}`);
      
      // Authorization Code flow
      if (scheme.flows.authorizationCode) {
        const flow = scheme.flows.authorizationCode;
        console.log(`  Auth URL: ${flow.authorizationUrl}`);  
        console.log(`  Token URL: ${flow.tokenUrl}`);
        console.log(`  Scopes: ${Object.keys(flow.scopes || {}).join(', ')}`);
      }
      
      // Client Credentials flow
      if (scheme.flows.clientCredentials) {
        const flow = scheme.flows.clientCredentials;
        console.log(`  Token URL: ${flow.tokenUrl}`);
        console.log(`  Scopes: ${Object.keys(flow.scopes || {}).join(', ')}`);
      }
    }
  });
}

Multi-Factor Authentication

// Handle operations requiring multiple auth methods (AND requirements)
const securityWithTypes = operation.getSecurityWithTypes();

securityWithTypes.forEach((orGroup, orIndex) => {
  if (!orGroup || orGroup.length <= 1) return;
  
  console.log(`Multi-factor option ${orIndex + 1}:`);
  orGroup.forEach(andRequirement => {
    if (andRequirement) {
      console.log(`  Required: ${andRequirement.type} (${andRequirement.security._key})`);
    }
  });
});

Security Validation

// Validate user has required credentials
function validateUserAuth(operation: Operation, user: User): boolean {
  const security = operation.getSecurity();
  if (security.length === 0) return true; // No auth required
  
  const securityByType = operation.prepareSecurity();
  
  // Check if user satisfies any OR requirement
  return Object.entries(securityByType).some(([type, schemes]) => {
    return schemes.some(scheme => {
      switch (type) {
        case 'Bearer':
          return 'bearerToken' in user || 'authorization' in user;
        case 'Header':
          return scheme.name.toLowerCase() in user;
        case 'Query':
          return scheme.name in user;
        case 'Basic':
          return 'keys' in user && Array.isArray(user.keys);
        default:
          return false;
      }
    });
  });
}

const isValid = validateUserAuth(operation, user);
console.log(`User has valid credentials: ${isValid}`);

Custom Authentication Extensions

// Handle ReadMe-specific auth defaults
const securityByType = operation.prepareSecurity();

Object.values(securityByType).flat().forEach(scheme => {
  if ('x-default' in scheme && scheme['x-default'] !== undefined) {
    console.log(`Default value for ${scheme._key}: ${scheme['x-default']}`);
  }
});

Error Handling

Security handling manages various edge cases:

  • Missing Security Schemes: References to undefined schemes return false in arrays
  • Invalid Flows: Malformed OAuth2 flows are handled gracefully
  • Missing User Data: Authentication extraction returns empty objects for missing credentials
  • Circular References: Security scheme resolution prevents infinite loops
// Safe handling of missing security data
const operation = oas.operation("/public", "get");
const security = operation.getSecurity(); // [] for operations without security

const emptyUser = {};
const auth = oas.getAuth(emptyUser); // {} - empty auth object

// Filter invalid schemes
const validSecurity = operation.getSecurityWithTypes(true);
// Removes false entries for broken security scheme references

Integration Patterns

HTTP Client Integration

// Generate headers for HTTP clients
function generateAuthHeaders(operation: Operation, user: User): Record<string, string> {
  const auth = oas.getAuth(user);
  const headers: Record<string, string> = {};
  
  Object.entries(auth).forEach(([key, value]) => {
    if (typeof value === 'string') {
      headers[key] = value;
    } else if (value && typeof value === 'object') {
      // Handle Basic auth
      const credentials = btoa(`${value.user}:${value.pass}`);
      headers['Authorization'] = `Basic ${credentials}`;
    }
  });
  
  return headers;
}

Security Middleware

// Express middleware for API security validation
function securityMiddleware(operation: Operation) {
  return (req: Request, res: Response, next: NextFunction) => {
    const security = operation.getSecurity();
    
    if (security.length === 0) {
      return next(); // No auth required
    }
    
    const securityByType = operation.prepareSecurity();
    let isAuthenticated = false;
    
    // Check each security type
    if (securityByType.Bearer && req.headers.authorization?.startsWith('Bearer ')) {
      isAuthenticated = true;
    } else if (securityByType.Header) {
      isAuthenticated = securityByType.Header.some(scheme => 
        req.headers[scheme.name.toLowerCase()]
      );
    }
    
    if (!isAuthenticated) {
      return res.status(401).json({ error: 'Authentication required' });
    }
    
    next();
  };
}

Install with Tessl CLI

npx tessl i tessl/npm-oas

docs

analysis-metrics.md

api-definition-reduction.md

extensions-customization.md

index.md

openapi-definition-management.md

operation-discovery-analysis.md

parameter-handling-json-schema.md

request-response-management.md

schema-dereferencing-references.md

security-authentication.md

server-url-management.md

utils.md

tile.json