Before we jump in, I’d like to recommend checking if you can use AWS IAM Identity Center for managing user identities instead of AWS IAM users. It’s generally a more secure alternative with fewer pitfalls, especially when managing multiple users. Additionally, it supports FIDO-certified security keys for both AWS Console and API access.

Alright, with that said, let’s get started.

So you’ve finally bitten the security bullet and gotten yourself a hardware security key to use as a multi-factor authentication (MFA) device instead of alternatives such as Authy and Google Authenticator. You’re really looking forward to having one physical key (… or likely at least two - one as a backup!) to rule them all, and the final service you need to configure it for is AWS IAM. And more specifically, AWS IAM users. “With AWS’s strong security focus, they’ve surely made this an easy task. This will be a walk in the park!” you think to yourself.

AWS IAM user + hardware security keys = 💔?

… But wait, what’s this? Deep inside their documentation AWS notes:

AWS supports using FIDO certified security keys only in the AWS Management Console. Using FIDO Certified security keys for MFA is not supported in the AWS CLI and AWS API, or for access to MFA-protected API operations.

Well, that’s a bummer. FIDO-certified keys can only be used for AWS Console access. You need programmatic access to your AWS account, and living your life in the AWS Console doing ClickOps is a no-go. Does this mean that you can’t use your brand new security key for API access to AWS? Do you have to throw in the towel here and accept that you can’t live in a security key-only world?

OATH TOTP to the rescue

The YubiKey 5 series, among many others, supports the Open Authentication (OATH) Time-based One-Time Password (TOTP) standard used by your typical authenticator application. This means that you can use your security key to generate TOTP codes! You create a virtual MFA device, just as you would with Authy or Google Authenticator, and receive a secret key in return, typically represented as a QR code. Then, add that secret key to your YubiKey, which can then be prompted programmatically to generate codes. This is pretty nifty, and allows you to use your security key even for services and use-cases that don’t support FIDO-certified security keys natively.

Below are some steps for getting started with using a YubiKey as a virtual MFA device for your AWS IAM user:

  1. Install Yubico’s CLI tool for managing YubiKeys. On macOS, you can do this through the Homebrew package manager (e.g., brew install ykman).
  2. Plug in your YubiKey and run ykman info to verify that your key is detected.
  3. Create a new virtual MFA device and attach it to your IAM user. It’s easier to do this through the AWS Console, especially if you haven’t done it before with the AWS CLI.
  4. Give your virtual MFA device a descriptive name.
  5. Copy the secret key (e.g., by clicking Show secret key in the AWS Console), and add it to your YubiKey. (Note that you can use whatever account name you want here, but since you ideally want something unique and descriptive an ARN is well-suited).
    ykman oath accounts add "arn:aws:iam::<your-account-id>:mfa/<your-mfa-device-name>" --touch
  6. Generate two consecutive TOTP codes (and paste them in the AWS Console when prompted):
    ykman oath accounts code "arn:aws:iam::<your-account-id>:mfa/<your-mfa-device-name>" \
      && sleep 30 \
      && ykman oath accounts code "arn:aws:iam::<your-account-id>:mfa/<your-mfa-device-name>"
  7. Update your AWS CLI configuration file, typically located at $HOME/.aws/config, to use your new virtual MFA device when authenticating to AWS. Example:
    [profile my-profile]

I strongly recommend using aws-vault or similar tools to securely store your AWS IAM user credentials locally. An additional bonus is that aws-vault allows you to use a special parameter value mfa_process in your AWS CLI configuration that will prompt you to touch your security key and automatically fill in the code afterwards so you don’t have to manually run a CLI command and paste the result yourself ✨ Example:

[profile my-profile]
; ...
mfa_process=ykman oath accounts code --single arn:aws:iam::<your-account-id>:mfa/<your-mfa-device-name>