curl --request POST \
--url https://iam.basaltic.sh/v1/assume-role-with-web-identity \
--header 'Content-Type: application/json' \
--data '
{
"web_identity_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6...",
"role": "crn:iam::production:role/reports",
"account": "crn:workspace:::account/9f8b1c2d-3e4f-4a5b-8c9d-0e1f2a3b4c5d",
"session_name": "reports-exporter",
"duration_seconds": 3600
}
'import requests
url = "https://iam.basaltic.sh/v1/assume-role-with-web-identity"
payload = {
"web_identity_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6...",
"role": "crn:iam::production:role/reports",
"account": "crn:workspace:::account/9f8b1c2d-3e4f-4a5b-8c9d-0e1f2a3b4c5d",
"session_name": "reports-exporter",
"duration_seconds": 3600
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
web_identity_token: 'eyJhbGciOiJSUzI1NiIsImtpZCI6...',
role: 'crn:iam::production:role/reports',
account: 'crn:workspace:::account/9f8b1c2d-3e4f-4a5b-8c9d-0e1f2a3b4c5d',
session_name: 'reports-exporter',
duration_seconds: 3600
})
};
fetch('https://iam.basaltic.sh/v1/assume-role-with-web-identity', 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/assume-role-with-web-identity",
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([
'web_identity_token' => 'eyJhbGciOiJSUzI1NiIsImtpZCI6...',
'role' => 'crn:iam::production:role/reports',
'account' => 'crn:workspace:::account/9f8b1c2d-3e4f-4a5b-8c9d-0e1f2a3b4c5d',
'session_name' => 'reports-exporter',
'duration_seconds' => 3600
]),
CURLOPT_HTTPHEADER => [
"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/assume-role-with-web-identity"
payload := strings.NewReader("{\n \"web_identity_token\": \"eyJhbGciOiJSUzI1NiIsImtpZCI6...\",\n \"role\": \"crn:iam::production:role/reports\",\n \"account\": \"crn:workspace:::account/9f8b1c2d-3e4f-4a5b-8c9d-0e1f2a3b4c5d\",\n \"session_name\": \"reports-exporter\",\n \"duration_seconds\": 3600\n}")
req, _ := http.NewRequest("POST", url, payload)
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/assume-role-with-web-identity")
.header("Content-Type", "application/json")
.body("{\n \"web_identity_token\": \"eyJhbGciOiJSUzI1NiIsImtpZCI6...\",\n \"role\": \"crn:iam::production:role/reports\",\n \"account\": \"crn:workspace:::account/9f8b1c2d-3e4f-4a5b-8c9d-0e1f2a3b4c5d\",\n \"session_name\": \"reports-exporter\",\n \"duration_seconds\": 3600\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://iam.basaltic.sh/v1/assume-role-with-web-identity")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"web_identity_token\": \"eyJhbGciOiJSUzI1NiIsImtpZCI6...\",\n \"role\": \"crn:iam::production:role/reports\",\n \"account\": \"crn:workspace:::account/9f8b1c2d-3e4f-4a5b-8c9d-0e1f2a3b4c5d\",\n \"session_name\": \"reports-exporter\",\n \"duration_seconds\": 3600\n}"
response = http.request(request)
puts response.read_body{
"access_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6Ii4uLiJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"access_key_id": "AKIA...",
"secret_access_key": "wJalrXUtnFEMI...",
"session_token": "FwoGZXIvYXdzE...",
"expiration": "2026-01-15T09:30:00Z",
"account_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"account_handle": "<string>",
"role_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid request parameters",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "invalid web identity token",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "IAM_CANNOT_ASSUME_ROLE",
"message": "You are not authorized to assume this role",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"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": "NOT_IMPLEMENTED",
"message": "web identity federation is not configured",
"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"
}
}Assumir função com identidade web
Troque um token de identidade emitido por um provedor de federação em que esta plataforma confia por credenciais temporárias. O resultado é a mesma sessão de função assumida POST /v1/assume-role mints, e é usado da mesma maneira.
Essa solicitação carrega sem assinatura e é a única chamada de venda de credenciais que não o faz. Um chamador federado ainda não possui credenciais do Basaltic — é para isso que serve a troca — então o token no corpo é a credencial que está sendo apresentada. Uma assinatura enviada de qualquer maneira é ignorada, e nada é retirado do contexto da requisição: role e account são lidos do corpo como qualquer outro campo.
Isso não deixa o ponto final aberto. Dois portões independentes têm que passar, e eles falham de forma diferente.
O token precisa ser verificado. Isso acontece antes que qualquer função seja lida, portanto, um token forjado nunca alcança uma política de confiança. A assinatura deve ser encadeada a uma chave que o provedor publica, o público deve ser aquele que esta plataforma aceita, e exp deve ser no futuro. Um signatário errado, um token cunhado para outro consumidor e um token expirado respondem todos com 401 com a mesma mensagem — a resposta não diz qual verificação falhou.
A função tem que concordar. A verificação do token estabelece quem está chamando; não concede nada. A função nomeada em role é assumida somente se sua própria política de confiança admite essa identidade. Seus principals devem nomear o provedor de federação, escrito crn:iam:::oidc-provider/<provider> — o único caso em que um principal de política de confiança não é o próprio CRN do chamador, porque uma identidade federada não tem CRN e o que é confiável é a fonte que a garantiu. Cada entrada em conditions deve então ser mantida contra as reivindicações do token: basalt:webidentity:Subject carrega o token sub e basalt:webidentity:Audience seu aud, então uma função pode vincular uma identidade em vez de aceitar tudo o que o provedor irá emitir. Uma condição em uma reivindicação que o token não carrega falha fechada.
Uma função cuja política de confiança não nomeie nenhum provedor, portanto, não pode ser assumida dessa maneira, por mais bom que o token seja. Essa é a linha entre as duas falhas: 401 significa que o token não é confiável, 403 significa que é e a função ainda não o terá.
As credenciais retornam com o escopo para account, carregando as próprias permissões da função. Não há nenhum campo policy aqui — ao contrário de POST /v1/assume-role, uma sessão federada não pode ser escopo down no momento da troca, então as políticas anexadas da função são a concessão inteira. Dimensione o papel de acordo.
Como não requer credenciais, as solicitações são limitadas por taxa por IP do cliente.
Quais provedores são confiáveis faz parte da própria configuração da plataforma. Ainda não há uma API para registrar um provedor de identidade próprio, portanto, essa operação está ativa, mas não tem um provedor externo cujos tokens aceitaria; as funções que a usam hoje são gerenciadas pela plataforma.
curl --request POST \
--url https://iam.basaltic.sh/v1/assume-role-with-web-identity \
--header 'Content-Type: application/json' \
--data '
{
"web_identity_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6...",
"role": "crn:iam::production:role/reports",
"account": "crn:workspace:::account/9f8b1c2d-3e4f-4a5b-8c9d-0e1f2a3b4c5d",
"session_name": "reports-exporter",
"duration_seconds": 3600
}
'import requests
url = "https://iam.basaltic.sh/v1/assume-role-with-web-identity"
payload = {
"web_identity_token": "eyJhbGciOiJSUzI1NiIsImtpZCI6...",
"role": "crn:iam::production:role/reports",
"account": "crn:workspace:::account/9f8b1c2d-3e4f-4a5b-8c9d-0e1f2a3b4c5d",
"session_name": "reports-exporter",
"duration_seconds": 3600
}
headers = {"Content-Type": "application/json"}
response = requests.post(url, json=payload, headers=headers)
print(response.text)const options = {
method: 'POST',
headers: {'Content-Type': 'application/json'},
body: JSON.stringify({
web_identity_token: 'eyJhbGciOiJSUzI1NiIsImtpZCI6...',
role: 'crn:iam::production:role/reports',
account: 'crn:workspace:::account/9f8b1c2d-3e4f-4a5b-8c9d-0e1f2a3b4c5d',
session_name: 'reports-exporter',
duration_seconds: 3600
})
};
fetch('https://iam.basaltic.sh/v1/assume-role-with-web-identity', 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/assume-role-with-web-identity",
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([
'web_identity_token' => 'eyJhbGciOiJSUzI1NiIsImtpZCI6...',
'role' => 'crn:iam::production:role/reports',
'account' => 'crn:workspace:::account/9f8b1c2d-3e4f-4a5b-8c9d-0e1f2a3b4c5d',
'session_name' => 'reports-exporter',
'duration_seconds' => 3600
]),
CURLOPT_HTTPHEADER => [
"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/assume-role-with-web-identity"
payload := strings.NewReader("{\n \"web_identity_token\": \"eyJhbGciOiJSUzI1NiIsImtpZCI6...\",\n \"role\": \"crn:iam::production:role/reports\",\n \"account\": \"crn:workspace:::account/9f8b1c2d-3e4f-4a5b-8c9d-0e1f2a3b4c5d\",\n \"session_name\": \"reports-exporter\",\n \"duration_seconds\": 3600\n}")
req, _ := http.NewRequest("POST", url, payload)
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/assume-role-with-web-identity")
.header("Content-Type", "application/json")
.body("{\n \"web_identity_token\": \"eyJhbGciOiJSUzI1NiIsImtpZCI6...\",\n \"role\": \"crn:iam::production:role/reports\",\n \"account\": \"crn:workspace:::account/9f8b1c2d-3e4f-4a5b-8c9d-0e1f2a3b4c5d\",\n \"session_name\": \"reports-exporter\",\n \"duration_seconds\": 3600\n}")
.asString();require 'uri'
require 'net/http'
url = URI("https://iam.basaltic.sh/v1/assume-role-with-web-identity")
http = Net::HTTP.new(url.host, url.port)
http.use_ssl = true
request = Net::HTTP::Post.new(url)
request["Content-Type"] = 'application/json'
request.body = "{\n \"web_identity_token\": \"eyJhbGciOiJSUzI1NiIsImtpZCI6...\",\n \"role\": \"crn:iam::production:role/reports\",\n \"account\": \"crn:workspace:::account/9f8b1c2d-3e4f-4a5b-8c9d-0e1f2a3b4c5d\",\n \"session_name\": \"reports-exporter\",\n \"duration_seconds\": 3600\n}"
response = http.request(request)
puts response.read_body{
"access_token": "eyJhbGciOiJFUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6Ii4uLiJ9...",
"token_type": "Bearer",
"expires_in": 3600,
"access_key_id": "AKIA...",
"secret_access_key": "wJalrXUtnFEMI...",
"session_token": "FwoGZXIvYXdzE...",
"expiration": "2026-01-15T09:30:00Z",
"account_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a",
"account_handle": "<string>",
"role_id": "3c90c3cc-0d44-4b50-8888-8dd25736052a"
}{
"error": {
"code": "INVALID_INPUT",
"message": "Invalid request parameters",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "UNAUTHORIZED",
"message": "invalid web identity token",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "IAM_CANNOT_ASSUME_ROLE",
"message": "You are not authorized to assume this role",
"request_id": "550e8400-e29b-41d4-a716-446655440000"
}
}{
"error": {
"code": "NOT_FOUND",
"message": "Resource not found",
"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": "NOT_IMPLEMENTED",
"message": "web identity federation is not configured",
"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"
}
}Corpo
A troca que um chamador federado envia. Ele não carrega nenhuma assinatura — o token é a credencial — então cada campo é lido do corpo e nada é inferido do contexto da solicitação.
O token de identidade a ser trocado, como um JWT assinado. É verificado antes de qualquer função ser lida: a assinatura deve ser encadeada a uma chave que o provedor confiável publica, o público deve ser aquele que esta plataforma foi configurada para aceitar e exp deve ser no futuro.
"eyJhbGciOiJSUzI1NiIsImtpZCI6..."
UUID da função da conta, nome imutável na conta selecionada, ou crn:iam:::papel/. AssumeRole pode usar um CRN qualificado para direcionar outra conta na mesma organização.A sessão resultante é vinculada à conta de função de destino.
"crn:iam::production:role/reports"
UUID da conta, identificador imutável ou CRN de uma conta do Workspace (crn:workspace:::account/A conta estabelece a organização proprietária para a federação e deve corresponder à conta de função de destino.
"crn:workspace:::account/9f8b1c2d-3e4f-4a5b-8c9d-0e1f2a3b4c5d"
Um rótulo registrado na sessão e na trilha de auditoria. Por padrão, a reivindicação sub do token, de modo que uma sessão sem nome ainda registra de qual identidade ela veio.
"reports-exporter"
Duração da validade da credencial (15 min a 12 horas). Um valor acima do próprio max_session_duration da função é rejeitado em vez de ser fixado.
900 <= x <= 432003600
Resposta
Função assumida com sucesso
As credenciais de uma sessão de função, em ambas as formas que podem ser apresentadas.
access_token é um token de portador para esta API — envie-o como Authorization: Bearer <token>. Os outros quatro campos são credenciais do AWS SigV4 para o endpoint de armazenamento de objetos compatível com o S3, que não diz mais nada.
Ambos vêm da mesma sessão e compartilham sua expiração, portanto, revogar a sessão interrompe ambos de uma só vez. Use o que o endpoint que você está chamando precisa; não há necessidade de escolher um no momento da solicitação.
Token de portador para a API Basaltic. Esteja presente em cada sessão de função.
"eyJhbGciOiJFUzI1NiIsInR5cCI6ImF0K2p3dCIsImtpZCI6Ii4uLiJ9..."
Sempre Bearer quando access_token está presente.
"Bearer"
Segundos até que o access_token expire.
3600
ID da chave de acesso SigV4, para o endpoint S3.
"AKIA..."
SigV4 secreto, para o endpoint S3.
"wJalrXUtnFEMI..."
Token de sessão SigV4, para o endpoint S3. Envie como X-Amz-Security-Token e inclua-o em SignedHeaders.
"FwoGZXIvYXdzE..."
Quando a sessão — e, portanto, ambos os formulários de credenciais — expira.
"2026-01-15T09:30:00Z"
UUID da conta proprietária da função de destino.
O identificador da conta proprietária da função de destino.
UUID imutável da função assumida.

