curl --request POST \
--url https://iam.basaltic.sh/v1/get-web-identity-token \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"audience": "sts.amazonaws.com",
"duration_seconds": 300
}
'import requests
url = "https://iam.basaltic.sh/v1/get-web-identity-token"
payload = {
"audience": "sts.amazonaws.com",
"duration_seconds": 300
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({audience: 'sts.amazonaws.com', duration_seconds: 300})
};
fetch('https://iam.basaltic.sh/v1/get-web-identity-token', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://iam.basaltic.sh/v1/get-web-identity-token",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'audience' => 'sts.amazonaws.com',
'duration_seconds' => 300
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://iam.basaltic.sh/v1/get-web-identity-token"
payload := strings.NewReader("{\n \"audience\": \"sts.amazonaws.com\",\n \"duration_seconds\": 300\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://iam.basaltic.sh/v1/get-web-identity-token")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"audience\": \"sts.amazonaws.com\",\n \"duration_seconds\": 300\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://iam.basaltic.sh/v1/get-web-identity-token")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"audience\": \"sts.amazonaws.com\",\n \"duration_seconds\": 300\n}"
response = http.request(request)
puts response.read_body{
"token": "<string>",
"expires_at": "2023-11-07T05:31:56Z"
}{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid request parameters",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "Authentication required",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "ACCESS_DENIED",
"message": "You don't have permission to perform this action",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "RATE_LIMITED",
"message": "Too many requests, please try again later",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "INTERNAL_ERROR",
"message": "An internal error occurred",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "SERVICE_UNAVAILABLE",
"message": "The service is temporarily unavailable. Please try again later.",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}Get web identity token
Mint an audience-bound identity proof using an authenticated service-account or assumed-role session. The selected account must match the session owner. Human users must first assume an account role. The caller needs iam:GetWebIdentityToken on its own service-account or role CRN.
The subject is derived from persisted identity, never request fields: org:ORGANIZATION_UUID:account:ACCOUNT_UUID:role:ROLE_UUID or org:ORGANIZATION_UUID:account:ACCOUNT_UUID:service-account:SERVICE_ACCOUNT_UUID. A direct VM metadata session retains its instance:INSTANCE_UUID subject. Chained sessions represent the target role. Issuer is https://iam.basaltic.sh; public discovery and workload JWKS are available on that host.
Any permitted external audience is supported, not only AWS. Policy conditions basalt:IdentityTokenAudience and basalt:IdentityTokenDurationSeconds restrict issuance. Tokens last at most five minutes and use separate keys from API tokens. Responses are not cached or stored for idempotency replay. Revoking the source session prevents further issuance; already issued proofs remain valid until expiry.
curl --request POST \
--url https://iam.basaltic.sh/v1/get-web-identity-token \
--header 'Authorization: Bearer <token>' \
--header 'Content-Type: application/json' \
--data '
{
"audience": "sts.amazonaws.com",
"duration_seconds": 300
}
'import requests
url = "https://iam.basaltic.sh/v1/get-web-identity-token"
payload = {
"audience": "sts.amazonaws.com",
"duration_seconds": 300
}
headers = {
"Authorization": "Bearer <token>",
"Content-Type": "application/json"
}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {Authorization: 'Bearer <token>', 'Content-Type': 'application/json'},
body: JSON.stringify({audience: 'sts.amazonaws.com', duration_seconds: 300})
};
fetch('https://iam.basaltic.sh/v1/get-web-identity-token', options)
.then(res => res.json())
.then(res => console.log(res))
.catch(err => console.error(err));<?php
$curl = curl_init();
curl_setopt_array($curl, [
CURLOPT_URL => "https://iam.basaltic.sh/v1/get-web-identity-token",
CURLOPT_RETURNTRANSFER => true,
CURLOPT_ENCODING => "",
CURLOPT_MAXREDIRS => 10,
CURLOPT_TIMEOUT => 30,
CURLOPT_HTTP_VERSION => CURL_HTTP_VERSION_1_1,
CURLOPT_CUSTOMREQUEST => "POST",
CURLOPT_POSTFIELDS => json_encode([
'audience' => 'sts.amazonaws.com',
'duration_seconds' => 300
]),
CURLOPT_HTTPHEADER => [
"Authorization: Bearer <token>",
"Content-Type: application/json"
],
]);
$response = curl_exec($curl);
$err = curl_error($curl);
curl_close($curl);
if ($err) {
echo "cURL Error #:" . $err;
} else {
echo $response;
}package main
import (
"fmt"
"strings"
"net/http"
"io"
)
func main() {
url := "https://iam.basaltic.sh/v1/get-web-identity-token"
payload := strings.NewReader("{\n \"audience\": \"sts.amazonaws.com\",\n \"duration_seconds\": 300\n}")
req, _ := http.NewRequest("POST", url, payload)
req.Header.Add("Authorization", "Bearer <token>")
req.Header.Add("Content-Type", "application/json")
res, _ := http.DefaultClient.Do(req)
defer res.Body.Close()
body, _ := io.ReadAll(res.Body)
fmt.Println(string(body))
}HttpResponse<String> response = Unirest.post("https://iam.basaltic.sh/v1/get-web-identity-token")
.header("Authorization", "Bearer <token>")
.header("Content-Type", "application/json")
.body("{\n \"audience\": \"sts.amazonaws.com\",\n \"duration_seconds\": 300\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://iam.basaltic.sh/v1/get-web-identity-token")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Authorization"] = 'Bearer <token>'
request["Content-Type"] = 'application/json'
request.body = "{\n \"audience\": \"sts.amazonaws.com\",\n \"duration_seconds\": 300\n}"
response = http.request(request)
puts response.read_body{
"token": "<string>",
"expires_at": "2023-11-07T05:31:56Z"
}{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid request parameters",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "Authentication required",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "ACCESS_DENIED",
"message": "You don't have permission to perform this action",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "RATE_LIMITED",
"message": "Too many requests, please try again later",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "INTERNAL_ERROR",
"message": "An internal error occurred",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "SERVICE_UNAVAILABLE",
"message": "The service is temporarily unavailable. Please try again later.",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}iam:GetWebIdentityToken. See IAM permissions for the full list, what each one covers, and an example policy.Authorizations
An OAuth 2.0 bearer token, sent as Authorization: Bearer <token>.
This is the recommended way to authenticate.
Get one by exchanging a service account's access key pair at
POST /v1/oauth/token with grant_type=client_credentials. It is the
standard client-credentials grant, so any OAuth-aware library will
obtain and refresh it for you.
curl -s -u "$KEY_ID:$SECRET" -d grant_type=client_credentials \
https://iam.basaltic.sh/v1/oauth/token
Tokens last an hour by default. The same access key pair is separately your AWS SigV4 credential for the S3-compatible object endpoint, which speaks nothing else.
Headers
Owning account UUID, CRN, or handle. Defaults to the authenticated workload account.
Body
Audience expected by the external relying party. Whitespace, control characters and the reserved Basaltic API audience basaltic are rejected. Permission conditions can restrict the allowed audience.
1 - 256"sts.amazonaws.com"
Lifetime of the identity proof. Omit for 300 seconds. The authenticated session must remain valid for the requested lifetime.
60 <= x <= 300
