August Feng

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!