Skip to main content

Credential Provisioning

ID Wispera can programmatically create API keys at vendor APIs and wrap them as governed passports in a single operation, ensuring every provisioned credential is tracked with audit trail and policy enforcement.

Overview

The provisioning module provides a unified interface: provision at the vendor, create a passport in the vault, then manage the lifecycle (rotate, revoke, list) through the same API.
Provisioning requires admin-level credentials for the target provider. Store those admin keys inside the vault with a privilege visa type so they are also governed and audited.

Supported Providers

ProviderCreateRotateRevokeListScoped KeysExpiry
OpenAIYesYesYesYesYesNo
AWSYesYesYesYesYesYes (STS)
Google CloudYesYesYesYesYesYes (SA keys)
Azure EntraYesYesYesYesYesYes (secrets)
GitHubYesYesNoNoYesYes (1h tokens)
TwilioYesYesYesYesYesNo
SendGridYesYesYesYesYesNo
AnthropicNoNoYesYesNoNo
Anthropic is a management-only provider. API keys must be created manually at console.anthropic.com. This provider can list and disable existing keys.

Provision and Create Passport

The unified workflow: provision a key at the vendor, then create a governed passport in the vault.
import { provisionAndCreatePassport } from '@id-wispera/core';

const { credential, passport } = await provisionAndCreatePassport(vault, {
  provider: 'openai',
  name: 'Coding Agent Key',
  humanOwner: '[email protected]',
  config: {
    provider: 'openai',
    organizationId: 'org-abc123',
    projectId: 'proj-xyz',
    keyType: 'service-account',
  },
  tags: ['production', 'coding-agent'],
}, {
  type: 'api-key',
  key: 'sk-admin-...',  // Admin key for provisioning
});

if (passport) {
  console.log('Passport created:', passport.id);
  console.log('Key ID:', credential.providerKeyId);
}

Provider Examples

AWS (STS Temporary Credentials)

const { credential, passport } = await provisionAndCreatePassport(vault, {
  provider: 'aws',
  name: 'Data Pipeline Agent',
  humanOwner: '[email protected]',
  config: {
    provider: 'aws',
    roleArn: 'arn:aws:iam::123456789:role/DataPipelineRole',
    sessionName: 'id-wispera-pipeline',
    durationSeconds: 3600,
  },
  permissions: {
    provider: 'aws',
    sessionPolicy: JSON.stringify({
      Version: '2012-10-17',
      Statement: [{ Effect: 'Allow', Action: 's3:GetObject', Resource: 'arn:aws:s3:::data-bucket/*' }],
    }),
  },
  tags: ['data-pipeline', 'temporary'],
}, {
  type: 'aws-sigv4',
  accessKeyId: process.env.AWS_ACCESS_KEY_ID!,
  secretAccessKey: process.env.AWS_SECRET_ACCESS_KEY!,
});

Google Cloud (API Key)

const { credential, passport } = await provisionAndCreatePassport(vault, {
  provider: 'google-cloud',
  name: 'Translation API Key',
  humanOwner: '[email protected]',
  config: {
    provider: 'google-cloud',
    projectId: 'my-project-123',
    keyType: 'api-key',
    apiTargets: ['translate.googleapis.com'],
    allowedIps: ['203.0.113.0/24'],
  },
  tags: ['translation', 'restricted'],
}, {
  type: 'service-account',
  keyFile: process.env.GOOGLE_APPLICATION_CREDENTIALS!,
});

Google Cloud (Service Account Key)

const { credential, passport } = await provisionAndCreatePassport(vault, {
  provider: 'google-cloud',
  name: 'ML Pipeline SA Key',
  humanOwner: '[email protected]',
  config: {
    provider: 'google-cloud',
    projectId: 'ml-project',
    keyType: 'service-account-key',
    serviceAccountEmail: '[email protected]',
  },
  permissions: {
    provider: 'google-cloud',
    roles: ['roles/storage.objectViewer', 'roles/bigquery.dataViewer'],
  },
  tags: ['ml-pipeline'],
}, {
  type: 'service-account',
  keyFile: process.env.GOOGLE_APPLICATION_CREDENTIALS!,
});

Azure Entra (App Registration + Secret)

const { credential, passport } = await provisionAndCreatePassport(vault, {
  provider: 'azure-entra',
  name: 'Agent App Secret',
  humanOwner: '[email protected]',
  config: {
    provider: 'azure-entra',
    tenantId: 'your-tenant-id',
    mode: 'create-app',
    displayName: 'ID Wispera Agent',
    secretExpiryDays: 180,
  },
  permissions: {
    provider: 'azure-entra',
    graphPermissions: ['User.Read', 'Mail.Send'],
  },
  tags: ['azure', 'agent'],
}, {
  type: 'oauth2',
  clientId: process.env.AZURE_CLIENT_ID!,
  clientSecret: process.env.AZURE_CLIENT_SECRET!,
  tenantId: 'your-tenant-id',
});

GitHub (Installation Access Token)

const { credential, passport } = await provisionAndCreatePassport(vault, {
  provider: 'github',
  name: 'CI/CD Agent Token',
  humanOwner: '[email protected]',
  config: {
    provider: 'github',
    installationId: 12345678,
    repositories: ['my-org/my-repo', 'my-org/other-repo'],
  },
  permissions: {
    provider: 'github',
    permissions: {
      contents: 'read',
      pull_requests: 'write',
      issues: 'read',
    },
  },
  tags: ['ci-cd', 'github'],
}, {
  type: 'jwt',
  privateKey: process.env.GITHUB_APP_PRIVATE_KEY!,
  appId: process.env.GITHUB_APP_ID!,
});

// Token expires in 1 hour
console.log('Expires:', credential.expiresAt);

Twilio (API Key)

const { credential, passport } = await provisionAndCreatePassport(vault, {
  provider: 'twilio',
  name: 'SMS Agent Key',
  humanOwner: '[email protected]',
  config: {
    provider: 'twilio',
    accountSid: 'ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
    keyType: 'standard',
    friendlyName: 'ID Wispera SMS Agent',
  },
  tags: ['sms', 'twilio'],
}, {
  type: 'basic',
  username: 'ACxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
  password: process.env.TWILIO_AUTH_TOKEN!,
});

SendGrid (Scoped API Key)

const { credential, passport } = await provisionAndCreatePassport(vault, {
  provider: 'sendgrid',
  name: 'Email Agent Key',
  humanOwner: '[email protected]',
  config: { provider: 'sendgrid' },
  permissions: {
    provider: 'sendgrid',
    scopes: ['mail.send', 'mail.batch.read'],
  },
  tags: ['email', 'sendgrid'],
}, {
  type: 'api-key',
  key: process.env.SENDGRID_API_KEY!,
});

Anthropic (Management Only)

import { listProviderKeys, revokeAtSource } from '@id-wispera/core';

// List existing keys
const keys = await listProviderKeys('anthropic', {
  type: 'api-key',
  key: process.env.ANTHROPIC_ADMIN_KEY!,
});

console.log('Existing keys:', keys);

// Disable a key
const result = await revokeAtSource('anthropic', 'key-to-disable', {
  type: 'api-key',
  key: process.env.ANTHROPIC_ADMIN_KEY!,
});

console.log('Disabled:', result.success);

Provider Capabilities

const caps = getProviderCapabilities('openai');  // null if unknown provider
const providers = listProviders();               // [{ provider, capabilities }]

Key Rotation

Rotation creates a new key and returns the old key ID for later revocation.
After rotation, revoke the old key at the source to ensure it can no longer be used. The two-step pattern (rotate then revoke) avoids downtime during the switchover window.
import { rotateCredential, revokeAtSource } from '@id-wispera/core';

const rotation = await rotateCredential('openai', 'old-key-id', {
  provider: 'openai',
  name: 'Coding Agent Key (rotated)',
  humanOwner: '[email protected]',
  config: { provider: 'openai', organizationId: 'org-abc123', keyType: 'service-account' },
}, auth);

if (rotation) {
  console.log('New key:', rotation.newCredential.providerKeyId);
  console.log('Revoke old key after', rotation.recommendedRevocationDelay, 'seconds');

  // After propagation delay...
  const revoked = await revokeAtSource('openai', rotation.oldKeyId, auth);
  console.log('Old key revoked:', revoked.success);
}

Revoking at Source

Permanently disable a key at the vendor.
const result = await revokeAtSource('openai', 'key-id-to-revoke', auth);

Authentication

Each provider requires a specific auth type.
Never hard-code admin credentials in source code. Store them as passports in the vault with a privilege visa type and retrieve them at runtime.

OpenAI

Admin API key (sk-admin-...):
const auth = { type: 'api-key', key: 'sk-admin-...' };

AWS

IAM access key with SigV4 signing:
const auth = { type: 'aws-sigv4', accessKeyId: 'AKIA...', secretAccessKey: '...', region: 'us-east-1' };

Google Cloud

Service account key file or OAuth2 client credentials:
// Service account (recommended)
const auth = { type: 'service-account', keyFile: require('./service-account.json') };

// OAuth2 client credentials
const auth = { type: 'oauth2', clientId: '...', clientSecret: '...' };

Azure Entra

OAuth2 client credentials with tenant ID:
const auth = { type: 'oauth2', clientId: '...', clientSecret: '...', tenantId: 'your-tenant-id' };

GitHub

GitHub App private key + App ID:
const auth = { type: 'jwt', privateKey: fs.readFileSync('private-key.pem', 'utf8'), appId: '12345' };

Twilio

Account SID + Auth Token (Basic auth):
const auth = { type: 'basic', username: 'ACxxxx...', password: 'your-auth-token' };

SendGrid

Full-access API key (Bearer):
const auth = { type: 'api-key', key: 'SG.xxxx...' };

Anthropic

Admin API key (management only):
const auth = { type: 'api-key', key: 'sk-ant-admin-...' };

Best Practices

  1. Use provisionAndCreatePassport for the unified workflow. This ensures every provisioned key immediately gets a passport with audit trail and policy enforcement.
  2. Store admin keys in the vault too. The keys you use for provisioning are themselves sensitive — create passports for them with privilege visa type.
  3. Rotate regularly. Use rotateCredential on a schedule. The function handles create-before-revoke to avoid downtime.
  4. Set expiry where supported. For AWS STS credentials, use expiresIn to create short-lived tokens. For Azure Entra, set secretExpiryDays to limit secret lifetime.
  5. Tag provisioned passports. Use tags like provisioned, production, or the provider name to make it easy to find and audit provisioned credentials.
  6. Prefer scoped credentials. Use project-scoped keys (OpenAI), STS temporary credentials (AWS), API restrictions (Google Cloud), and fine-grained permissions (GitHub) to follow the principle of least privilege.
  7. GitHub tokens are short-lived. Installation access tokens expire in 1 hour. Use rotateCredential to refresh them before expiry.
  8. Anthropic is management-only. Create keys manually at console.anthropic.com, then use ID Wispera to list, track, and disable them.

Next Steps