With all the recent hubbub about AI and LLM inference (and the much more recent hubbub about managing ever increasing AI token consumption - funny money that costs real money), I have been looking at various AWS Bedrock things at work. While the context of how to leverage these various components is not mine to discuss here, I did learn some interesting stuff about Bedrock API keys that was fun to explore on the side.
Specifically, AWS allows for generating and using API keys for their Bedrock (and Bedrock mantle) services to allow using a single Bearer token (over AWS specific API schemas for AWS IAM) to invoke models. Largely useful in proprietary harnesses, coding harnesses like OpenCode e.t.c to move away from having to use a whole IAM User or some other token exchanging means of leveraging an IAM Role. While this comes in 2 flavors, long-term (backed by an IAM User) and short term (the subject of my post here) API Keys, I found the short-term API keys interesting as there didn’t seem to be any overt AWS action (Bedrock:CreateSuperCoolAPIKey perhaps) to actually mint these short-term keys.
Per AWS:
Short-term – Create an Amazon Bedrock API key that lasts as long as your session (and no longer than 12 hours). You should already have an IAM principal set up with the proper permissions to use Amazon Bedrock. This option is preferred over long-term keys for production environments that require regular changing of credentials for greater security.
Short term keys have the following properties:
Valid for the shorter of the following values:
12 hours
The duration of the session generated by the IAM principal used to generate the key.
Inherit the permissions attached to the principal used to generate the key.
Can be used only in the AWS Region from which you generated it.
While this gives some clues (and I later discovered, AWS does have a short blurb on the nature of this API Key, which I have linked below), I was originally stumped by what AWS API Action I could control via things like Service Control Policies to actually control creation of these API keys. Creating an AWS Bedrock API Key in the Console didn’t actually seem to call any AWS API action, which seemed odd for the cloud platform where generally everything is an AWS API action against some service on some resource…
AWS Bedrock API Keys
I started by looking at the documentation of how to generate an AWS Bedrock Short term API key and of course it does not have CLI documentation since the CLI uses the AWS API models such as this one for bedrock
The python, javascript and java versions of generating a short-term API key seem to be packages outside of the usual boto, seemingly a purpose built library. For python it is the creatively named “aws-bedrock-token-generator” library that generates aws bedrock tokens as expected with a provide_token function. So naturally, I dug into what this codebase is, over on github
AWS Bedrock Token Generator (Python)
aws_bedrock_token_generator/
├── __init__.py
└── token_generator.py
tests/
├── __init__.py
├── test_init.py
└── test_token_generator.py
.gitignore
CHANGELOG.md
CODE_OF_CONDUCT.md
CONTRIBUTING.md
LICENSE.txt
NOTICE
README.md
SECURITY.md
SUPPORT.md
pyproject.toml
Examining the more interesting file of the module (with the token generation logic that provide_token actually calls) with the code here:
def _generate_token(credentials: Credentials, region: str, expires: int) -> str:
"""
Internal method to build the presigned bearer token.
Args:
credentials: AWS credentials.
region: AWS region.
expires: Expiry time in seconds.
Returns:
A base64-encoded bearer token string.
"""
request = AWSRequest(
method="POST",
url=DEFAULT_URL,
headers={"host": DEFAULT_HOST},
params={"Action": "CallWithBearerToken"},
)
auth = SigV4QueryAuth(credentials, SERVICE_NAME, region, expires=expires)
auth.add_auth(request)
presigned_url = request.url.replace("https://", "") + TOKEN_VERSION
encoded_token = base64.b64encode(presigned_url.encode("utf-8")).decode("utf-8")
return f"{AUTH_PREFIX}{encoded_token}"
This is effectively the token generation path, and it is the same logic behind the short-lived Bedrock bearer token. The key is built from a presigned SigV4 request to bedrock.amazonaws.com/?Action=CallWithBearerToken and then base64-encoded with the bedrock-api-key- prefix.
Short term API Key structure
The API key is really just a pre-signed URL as described below, encoded as a base64 string with bedrock-api-key- as the prefix. That means it’s more than just some value to compare against some other value server side but a record of your session’s (when you minted the key) AWS Secret key ID and the session token. While these are 2 juicy pieces of information, sadly they do not include the private key itself which would have meant this key could be a key to the role in full. Nonetheless, what the key does have is as follows:
Endpoint
bedrock.amazonaws.com/?Action=CallWithBearerToken
This is the “CallWithBearerToken” action against the Amazon Bedrock service. CallWithBearerToken indicates this URL is intended to facilitate a request authenticated using the credentials embedded in the pre-signed URL.
Signing algorithm
X-Amz-Algorithm=AWS4-HMAC-SHA256
Tells AWS which signing algorithm was used which is their SigV4 using HMAC-SHA256 for the authentication code (integrity and authenticity of this signed message)
Credential / signing scope
X-Amz-Credential=ASIAWTXV3C53GJFPR4WZ%2F20260620%2Fus-east-1%2Fbedrock%2Faws4_request
which when URL-decoded is
X-Amz-Credential=
ASIAWTXV3C53GJFPR4WZ└── AWS access key ID20260620└── Signing dateus-east-1└── AWS regionbedrock└── AWS serviceaws4_request└── SigV4 termination string
Signing timestamp
X-Amz-Date=20260620T124044Z
This is when the request was signed.
Expiration
X-Amz-Expires=43200
This is intended validity period of 12 hours (in seconds) from the signing time. Although AWS will also impose its own constraint based on if the session this came from already expired, so X-Amz-Expires isn’t by itself a guarantee that a URL will remain usable for the entire requested period.
Temporary security token
X-Amz-Security-Token=IQoJb3JpZ2luX2Vj...
This is the AWS STS session token associated with the temporary credentials. The secret access key was used to calculate the signature but the session token is included in the URL because AWS needs it to identify/validate the temporary session which may or may not expire before the 12 hours of the Bedrock API Key.
Signature
X-Amz-Signature=dea85c418b31c277bdf5d5e24199b0cee97f4438e29b623b290caf3e2afe8fd2
This is the resulting AWS SigV4 signature proving that the request was generated using the corresponding AWS secret access key.
Signed headers
X-Amz-SignedHeaders=host
This says that the host HTTP header was included when calculating the signature effectively binding the signature to:
Host: bedrock.amazonaws.com
If a signed header is changed, the resulting request won’t match the signature.
API version
Version=1
This one is separate from the SigV4 authentication parameters and we can see where it gets appended in from the code above.
So what?
For my unique use-case at work, this just entails that blocking or controlling the ability to do the CallWithBearerToken action against the Bedrock service and resources is the only lever that I am likely to have here. For the rest of the world, this means don’t leave this key out there hardcoded on GitHub (but if you do, atleast it’ll limit the damage to your token consumption and not whatever else your role/user had access to while minting the key…). GitHub search is likely to turn up things if one does leave them in code:

