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
| Provider | Create | Rotate | Revoke | List | Scoped Keys | Expiry |
|---|
| OpenAI | Yes | Yes | Yes | Yes | Yes | No |
| AWS | Yes | Yes | Yes | Yes | Yes | Yes (STS) |
| Google Cloud | Yes | Yes | Yes | Yes | Yes | Yes (SA keys) |
| Azure Entra | Yes | Yes | Yes | Yes | Yes | Yes (secrets) |
| GitHub | Yes | Yes | No | No | Yes | Yes (1h tokens) |
| Twilio | Yes | Yes | Yes | Yes | Yes | No |
| SendGrid | Yes | Yes | Yes | Yes | Yes | No |
| Anthropic | No | No | Yes | Yes | No | No |
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
- Use
provisionAndCreatePassport for the unified workflow. This ensures every provisioned key immediately gets a passport with audit trail and policy enforcement.
- Store admin keys in the vault too. The keys you use for provisioning are themselves sensitive — create passports for them with
privilege visa type.
- Rotate regularly. Use
rotateCredential on a schedule. The function handles create-before-revoke to avoid downtime.
- Set expiry where supported. For AWS STS credentials, use
expiresIn to create short-lived tokens. For Azure Entra, set secretExpiryDays to limit secret lifetime.
- Tag provisioned passports. Use tags like
provisioned, production, or the provider name to make it easy to find and audit provisioned credentials.
- 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.
- GitHub tokens are short-lived. Installation access tokens expire in 1 hour. Use
rotateCredential to refresh them before expiry.
- Anthropic is management-only. Create keys manually at console.anthropic.com, then use ID Wispera to list, track, and disable them.
Next Steps