Skip to content

Component Architecture

This page provides a detailed look at each component in the Voidkey system and how they interact.

The TypeScript core library that implements the fundamental credential brokering logic.

  • Token Validation: Validates OIDC tokens against IdP JWKS endpoints
  • Provider Abstraction: Defines interfaces for identity and access providers
  • Configuration Management: Loads and validates configuration
  • Error Handling: Comprehensive error types and handling

The main orchestrator class that coordinates the credential minting workflow:

class CredentialBroker {
constructor(
private config: BrokerConfig,
private providers: {
idp: Map<string, IdpProvider>,
access: Map<string, AccessProvider>
}
) {}
async mintCredentials(request: MintRequest): Promise<Credentials> {
// 1. Validate OIDC token
// 2. Check identity configuration
// 3. Authenticate broker with its IdP
// 4. Call access provider to mint credentials
// 5. Return formatted credentials
}
}
interface IdpProvider {
name: string;
issuer: string;
audience?: string | string[];
validateToken(token: string): Promise<TokenClaims>;
getPublicKeys(): Promise<JWKSet>;
}
interface AccessProvider {
name: string;
type: string;
authenticate(brokerToken: string): Promise<void>;
mintCredentials(
config: ProviderConfig,
subject: string
): Promise<RawCredentials>;
}

Identity Providers:

  • Auth0Provider: Auth0 OIDC integration
  • GitHubActionsProvider: GitHub Actions OIDC tokens
  • KeycloakProvider: Generic Keycloak/OIDC support
  • OktaProvider: Okta identity platform

Access Providers:

  • AwsStsProvider: AWS STS AssumeRoleWithWebIdentity
  • MinioProvider: MinIO STS integration
  • GcpProvider: Google Cloud IAM
  • AzureProvider: Azure Active Directory
interface BrokerConfig {
brokerIdp: {
name: string;
issuer: string;
audience: string;
clientId: string;
clientSecret: string;
};
clientIdps: Array<{
name: string;
issuer: string;
audience?: string | string[];
jwksUri?: string;
}>;
accessProviders: Array<{
name: string;
type: string;
endpoint?: string;
region?: string;
// Provider-specific config
}>;
clientIdentities: Array<{
subject: string;
idp: string;
keys: Record<string, KeyConfig>;
}>;
}

The NestJS HTTP server that exposes the broker functionality as a REST API.

Built on NestJS for:

  • Dependency Injection: Clean, testable architecture
  • Middleware Pipeline: Request validation, logging, security
  • Modular Structure: Organized into feature modules
  • Built-in Validation: Request/response validation

Handles credential minting endpoints:

@Controller('credentials')
export class CredentialsController {
constructor(private credentialsService: CredentialsService) {}
@Post('mint')
async mintCredentials(@Body() request: MintRequestDto) {
return this.credentialsService.mint(request);
}
@Get('keys')
async getAvailableKeys(@Query('token') token: string) {
return this.credentialsService.getKeysForToken(token);
}
}

Manages configuration loading and validation:

@Module({
providers: [
{
provide: CONFIG_TOKEN,
useFactory: async () => {
const config = await loadConfig(process.env.CONFIG_PATH);
validateConfig(config);
return config;
},
},
],
exports: [CONFIG_TOKEN],
})
export class ConfigModule {}

Initializes and manages provider instances:

@Injectable()
export class ProvidersService implements OnModuleInit {
private idpProviders = new Map<string, IdpProvider>();
private accessProviders = new Map<string, AccessProvider>();
async onModuleInit() {
await this.initializeProviders();
}
private async initializeProviders() {
// Load and instantiate providers based on config
}
}

Security Middleware:

  • CORS configuration
  • Helmet for security headers
  • Rate limiting
  • Request ID generation

Validation Pipe:

app.useGlobalPipes(new ValidationPipe({
whitelist: true,
forbidNonWhitelisted: true,
transform: true,
}));

Mint credentials for specified keys:

interface MintRequest {
oidcToken: string;
keys: string[];
}
interface MintResponse {
credentials: Record<string, any>;
expiresAt: string;
}

List available keys for a token:

interface KeysResponse {
subject: string;
keys: Array<{
name: string;
provider: string;
description?: string;
}>;
}

The Go command-line interface for interacting with the broker.

Built with Cobra for:

  • Command Structure: Clear command hierarchy
  • Flag Management: Consistent flag handling
  • Help Generation: Automatic help text
  • Completion: Shell completion support
voidkey
├── mint # Mint credentials
│ ├── --keys # Specific keys to mint
│ ├── --all # Mint all available keys
│ └── --output # Output format (env, json)
├── list-keys # List available keys
├── version # Show version info
└── help # Show help
func mintCmd() *cobra.Command {
cmd := &cobra.Command{
Use: "mint",
Short: "Mint temporary credentials",
RunE: func(cmd *cobra.Command, args []string) error {
token := getOIDCToken()
client := newBrokerClient()
response, err := client.MintCredentials(token, keys)
if err != nil {
return err
}
formatter := getFormatter(outputFormat)
return formatter.Format(response)
},
}
cmd.Flags().StringSlice("keys", nil, "Keys to mint")
cmd.Flags().Bool("all", false, "Mint all available keys")
cmd.Flags().String("output", "env", "Output format")
return cmd
}

Environment Variables:

Terminal window
export AWS_ACCESS_KEY_ID="ASIATESTACCESSKEY"
export AWS_SECRET_ACCESS_KEY="testsecretkey"
export AWS_SESSION_TOKEN="testsessiontoken"

JSON Format:

{
"AWS_DEPLOYMENT": {
"AccessKeyId": "ASIATESTACCESSKEY",
"SecretAccessKey": "testsecretkey",
"SessionToken": "testsessiontoken",
"Expiration": "2024-01-15T12:00:00Z"
}
}

The CLI can be configured via:

  1. Environment Variables:
Terminal window
VOIDKEY_BROKER_URL=https://broker.example.com
VOIDKEY_OIDC_TOKEN=eyJhbGciOiJSUzI1NiIs...
VOIDKEY_OUTPUT_FORMAT=json
  1. Configuration File:
~/.voidkey/config.yaml
broker_url: https://broker.example.com
output_format: json
default_keys:
- AWS_DEPLOYMENT
- GCP_READONLY
  1. Command Flags:
Terminal window
voidkey mint \
--broker-url https://broker.example.com \
--keys AWS_DEPLOYMENT \
--output json

The Docker-based development environment for local testing.

Pre-configured identity provider with:

  • Broker Realm: For broker authentication
  • Client Realm: For client authentication
  • Test Users: Pre-created test accounts
  • Service Accounts: For broker-to-IdP auth

S3-compatible storage with:

  • STS Support: Temporary credential generation
  • Test Buckets: Pre-created storage buckets
  • Admin Console: Web UI for management
  • Configured Policies: Example IAM policies
version: '3.8'
services:
keycloak:
image: quay.io/keycloak/keycloak:latest
environment:
KEYCLOAK_ADMIN: admin
KEYCLOAK_ADMIN_PASSWORD: admin
ports:
- "8080:8080"
volumes:
- ./realms:/opt/keycloak/data/import
command: start-dev --import-realm
minio:
image: minio/minio:latest
environment:
MINIO_ROOT_USER: minioadmin
MINIO_ROOT_PASSWORD: minioadmin
MINIO_IDENTITY_OPENID_CONFIG_URL: http://keycloak:8080/realms/broker/.well-known/openid-configuration
ports:
- "9000:9000"
- "9001:9001"
command: server /data --console-address ":9001"
  1. Start Services:
Terminal window
docker-compose up -d
  1. Configure Broker:
# Use sandbox-specific configuration
brokerIdp:
issuer: "http://localhost:8080/realms/broker"
clientId: "broker-service"
clientSecret: "dev-secret"
  1. Run Tests:
Terminal window
# Integration tests against sandbox
npm run test:integration
sequenceDiagram
    participant CLI as CLI Client
(Go) participant API as Broker API
(NestJS Server) participant Core as Broker Core
(TypeScript Library) participant ClientIdP as Client IdP
(GitHub/Auth0/etc) participant BrokerIdP as Broker IdP
(Keycloak/etc) participant Provider as Access Provider
(AWS STS/GCP/etc) CLI->>API: 1. POST /credentials/mint
with OIDC token API->>Core: 2. Call CredentialBroker.mintCredentials() Note over Core: Token Validation Phase Core->>ClientIdP: 3. Fetch JWKS & validate token ClientIdP-->>Core: 4. Token validation result Note over Core: Broker Authentication Phase Core->>BrokerIdP: 5. Authenticate broker (client credentials) BrokerIdP-->>Core: 6. Broker access token Note over Core: Credential Minting Phase Core->>Provider: 7. Request temporary credentials
(using broker token) Provider-->>Core: 8. Return scoped credentials Core-->>API: 9. Return formatted credentials API-->>CLI: 10. HTTP response with credentials

Each component handles errors appropriately:

  1. broker-core: Typed errors with details
  2. broker-server: HTTP status codes and error responses
  3. CLI: User-friendly error messages
  4. Sandbox: Debug-friendly logging