AWS Swift SDK and email addresses as SSO session names
About
I'm a big fan of how AWS's SDK authentication mechanism is standardized across languages and also portable across environments.
Hint: The credential provider chain allows a program to authenticate when running locally and fall back to the IAM infrastructure when it's running in an AWS environment.
All good until Swift
When developing locally, I like to use the IAM Identity Center credential provider and avoid configuring access keys.
This means I run aws sso login; AWS_PROFILE=augustfeng and let the SDK resolve
the credentials automatically.
The AWS_PROFILE approach has worked for me with Rust, Go and TypeScript.
When I tried it with Swift, however, it did not work. I fell back to configuring access keys but the next day I spent some time investigating (and now documenting it in this blog post).
Here's a minimal program to reproduce the error:
import AWSSTS
import Foundation
func main() async throws {
do {
let sts = try await STSClient()
let _ = try await sts.getCallerIdentity(
input: GetCallerIdentityInput()
)
print("success!")
} catch {
print(error)
exit(1)
}
}
try await main()failedToResolveAWSCredentials("DefaultAWSCredentialIdentityResolverChain: Failed to resolve credentials.")Troubleshooting
Sandbox
My initial thought was that the runtime environment was running in an App
Sandbox and could not read my ~/.aws/config.
We can turn off the App Sandbox setting by deleting the App Sandbox capability.
I did that, and the error remained!
AWS_PROFILE
I had configured the AWS_PROFILE in the Scheme but I wanted to be sure that the application was indeed inheriting it.
I ran this code during startup:
let aws_profile = ProcessInfo.processInfo.environment["AWS_PROFILE"]
print(aws_profile)I found that AWS_PROFILE was correctly configured, so this wasn't it either!
Hacking around
Finally, I added the aws-sdk-swift as a local dependency and started hacking around.
After a bit of tracing, I found a statement that was throwing an unexpected exception. I wrapped it in a do/catch to print the error:
do {
// The following statement throws!
region = try getProperty(ssoSessionName, .ssoSession, "sso_region", fileBasedConfig)
} catch {
print(error)
throw error
}dataNotFound("SSOAWSCredentialIdentityResolver: Failed to retrieve sso_region from <redacted>@<redacted>.com ssoSession section.")
Strange! So it can't find the sso_region in my sso-session configuration.
Claude Help
I'm not going to lie, I've had Claude Code open this entire time. It hadn't been helpful until now.
I showed Claude Code where I was and asked if it knew why my sso-session section wasn't being acknowledged.
Claude let me know that the parser only accepts certain characters, and the @
in my email address most likely causes it to abort.
This is what my config looked like (with redaction):
[profile foo]
sso_session = [email protected]
sso_account_id = 123456789012
sso_role_name = AdministratorAccess
region = us-east-1
[sso-session [email protected]]
sso_start_url = https://xxx.awsapps.com/start
sso_region = us-east-1
sso_registration_scopes = sso:account:access
I followed its advice and edited the sso-session name to foo-sso, and
it worked! 🤯
Conclusion
The aws-sdk-swift could not resolve my SSO credentials because my SSO session was named after my email address.
I raised awslabs/aws-crt-swift#394 and renamed my session to foo-sso to
fix it.
While at it, I also filed a small typo patch that I found while debugging!