Why the fake test/test credentials are accepted without question, how basic IAM policy evaluation works in LocalStack, the use of resource-based policies, and the IAM emulation limitations you must know about.

In the previous episode 13 we pointed SDKs and the CLI at http://localhost:4566 via endpoint injection and awslocal. But one question still nags: the AWS CLI always demands credentials, yet LocalStack never refuses. How does authentication actually work in the emulator? This episode answers that: the fake test/test credentials, the AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY variables, how LocalStack evaluates IAM policies, resource-based policies, and the emulation limitations you need to know before carrying production assumptions into your local environment.
LocalStack's default does not verify credentials. All access key and secret key combinations are accepted, as long as the format is valid. This is deliberate, so production SDKs and CLIs run without modifications beyond the endpoint URL. The most common pair is test/test with region us-east-1 — exactly what awslocal uses automatically.
We can prove it with the AWS CLI pointed at the emulator:
aws --endpoint-url=http://localhost:4566 sts get-caller-identityLocalStack responds with the default account identity 000000000000:
{
"UserId": "AKIAIOSFODNN7EXAMPLE",
"Account": "000000000000",
"Arn": "arn:aws:iam::000000000000:root"
}Again: LocalStack doesn't check whether AKIAIOSFODNN7EXAMPLE really belongs to you. Account 000000000000 is the default account used by all local resources.
For SDKs and the CLI, the cleanest way is environment variables:
export AWS_ACCESS_KEY_ID=test
export AWS_SECRET_ACCESS_KEY=test
export AWS_DEFAULT_REGION=us-east-1
export AWS_ENDPOINT_URL=http://localhost:4566After this, any production code runs as-is — no line changes beyond the endpoint.
boto3 (Python) uses the env vars above without needing explicit credentials:
import boto3
s3 = boto3.client(
"s3",
region_name="us-east-1",
endpoint_url="http://localhost:4566",
aws_access_key_id="test",
aws_secret_access_key="test",
)
print(s3.list_buckets())Note: aws_access_key_id="test" is just a formality value. The same value is accepted in other SDKs (aws-sdk-js, aws-sdk-go) too.
Tip
Keep these fake credentials in a .env that's versioned, not in a secret store. Since LocalStack doesn't verify them, they're safe to share among developers — this is precisely their main advantage over real credentials.
LocalStack mimics an allow-by-default model: as long as no policy denies, access is permitted. But LocalStack can still evaluate policies attached to users, groups, and roles. Let's try: create a user, attach a read-only S3 policy, then inspect.
awslocal iam create-user --user-name dev
awslocal iam attach-user-policy \
--user-name dev \
--policy-arn arn:aws:iam::aws:policy/AmazonS3ReadOnlyAccess
awslocal iam list-attached-user-policies --user-name devWe can also create a custom policy with a JSON document and attach it:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": ["arn:aws:s3:::assets/*"]
}
]
}The policy above is stored via awslocal iam create-policy then attached to the dev user. As a result, an s3:PutObject operation not listed can be denied in some services — this is the basis of authorization emulation in LocalStack.
Besides identity-based policies, AWS also has resource-based policies attached to resources — most commonly on S3 buckets. LocalStack evaluates them in a limited way. Here's an example bucket policy granting s3:GetObject access to the dev user:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::000000000000:user/dev"},
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::assets/*"
}
]
}Apply it with the following commands:
awslocal s3 mb s3://assets
awslocal s3api put-bucket-policy \
--bucket assets --policy file://bucket-policy.jsonIt's important to know what is not simulated. Don't put your security trust in the emulator:
| IAM Aspect | Status in LocalStack | Notes |
|---|---|---|
| Credential validation | Not checked | All credentials are accepted |
| Policy evaluation | Partial | Highly dependent on the service |
| Deny precedence | Limited | Allow-by-default logic dominates |
| STS assume-role | Mock | Sessions created without real session policies |
| SCP & Organizations | Not supported | No organization-level policies |
| KMS key policy | Partial | Only certain services |
The implication: use IAM emulation to test your code flow, not to test security. Real authorization must still be verified on a real AWS account in staging or a production-like environment.
Warning
Never treat LocalStack policy evaluation results as proof of safety. Because it's allow-by-default, access that should be denied can slip through the emulator and then be rejected on real AWS — or the other way around.
This episode opened LocalStack's authentication black box:
test/test with region us-east-1 is the default you can freely use.AWS_ACCESS_KEY_ID and AWS_SECRET_ACCESS_KEY just need to be pointed via env vars, and the endpoint via AWS_ENDPOINT_URL.Now that credentials and policies are understood, the next question is the security of the environment itself: where to store data, how to isolate state between developers, and how to treat PERSISTENCE. In the next episode 15 we discuss Security & Best Practice — principles for using LocalStack comfortably without creating production risks. See you there!