VCF 9.1 API Access (4): vCenter Authentication

VCF 9.1 API Access Series

This post is part of an ongoing deep-dive series on VCF 9.1 programmatic access.

Foundational Mechanics

Component Authentication & Token Exchange

Advanced Architecture, Governance & Least Privilege

Day-2 Lifecycle Automation

In this post, we will walk through how to leverage your centralized VCF VIDB API token to get access to VCF SSO-enabled vCenter. By utilizing this secure token-exchange architecture, you can execute programmatic tasks against vCenter APIs without ever needing hardcoded local vCenter credentials.

With the release of VCF 9.1, Steps 2 and 3 are not required for VIDB Access Tokens generated via the VCF SSO Federated Users’ API token. These tokens are directly endorsed by vCenter, eliminating the need to exchange them for a SAML token. However, for API tokens generated by an API client, Steps 2 and 3 are still required.

One example here for your reference:

curl --request GET \
--url https://{{vc-fqdn}}/rest/vcenter/cluster \
--header 'Authorization: Bearer {{access_token}}'

Step 1: Exchange your VIDB API token for VIDB Access Token

API Request:

curl --request POST \
--url https://{{vidb-fqdn}}/acs/t/CUSTOMER/token \
--header 'content-type: application/x-www-form-urlencoded' \
--data grant_type=urn:custom:vcf:params:oauth:grant-type:api-token \
--data 'api_token={{ssoAPIToken}}'

The API response includes an access token. Get the access token for the next step.

{
"scope": "openid profile user email group",
"access_token": "eyJ0eXAxxxxmFjY3QiOiJkemhhb8PArmZIqyg",
"token_type": "Bearer",
"expires_in": 1799
}

Step 2: Exchange VIDB Access Token for vCenter SAML Token

API Request

curl --request POST \
--url https://{{vc-fqdn}}/api/vcenter/authentication/token \
--header 'authorization: Bearer {{access_token}}' \
--header 'content-type: application/x-www-form-urlencoded' \
--data grant_type=urn:ietf:params:oauth:grant-type:token-exchange \
--data requested_token_type=urn:ietf:params:oauth:token-type:saml2 \
--data subject_token_type=urn:ietf:params:oauth:token-type:access_token \
--data 'subject_token={{access_token}}'

Response:

{
"access_token": "PD94bWwgdmVycBJRD0iX2QzMzY3MzFxxxxxxxxxU3RhdGVtZW50Pjwvc2FtbDI6QXNzZXJ0aW9uPg==",
"issued_token_type": "urn:ietf:params:oauth:token-type:saml2",
"token_type": "Bearer",
"expires_in": 299
}

The access token in the response is a Base64 encoded SAML token in XML format. To use it for a vCenter authentication, we decode it, compress it, and then Base-64 encode it again. If you are using the Bruno API client, you can leverage the following Post Response script to get the right format SAML token (we call it vCenterCompToken here).

bru.setGlobalEnvVar("vCenterSamlToken", res.body.access_token);
const zlib = require('zlib');
const samlToken = res.body.access_token ;
if (samlToken) {
try {
// Step 1: Base64 Decode
const decodedBuffer = Buffer.from(samlToken, 'base64');
// Step 2: Gzip compression
// We use gzipSync for simplicity in scripts
const gzippedBuffer = zlib.gzipSync(decodedBuffer);
// Step 3: Base64 Encode (Standard output is already "w0" equivalent - no newlines)
const compToken = gzippedBuffer.toString('base64');
// Step 4: Save the new token back to a variable
bru.setGlobalEnvVar("vCenterCompToken", compToken);
console.log("Token compressed and saved to COMP_TOKEN");
} catch (err) {
console.error("Error compressing token:", err);
}
} else {
console.warn("SAML_TOKEN environment variable is missing.");
}

Step 3: vCenter Auth

To get an authenticated vCenter Session, use the following API with the compressed encoded SAML token.

curl --request POST \
--url https://{{vc-fqdn}}/api/session \
--header 'authorization: SIGN token="{{vCenterCompToken}}"'

Now that you have authenticated your session, you are ready to start automating!