Skip to content

Provide way to get current credentials (AWS SDKs do not support SSO) #5261

Closed

Description

@chrsmith

My team has set up AWS SSO and is starting to use aws sso login for most of their needs. Everything is working smoothly. However, it appears that AWS SDKs (e.g. Golang apps that call AWS APIs) do not support reading the temporary SSO credentials stored in ~/.aws/cli/cache/~/.aws/sso/cache. (See aws/aws-sdk-go#3186)

And as a result, my team is blocked from adopting AWS SSO because it only works with the AWS CLI but none one our existing tools.

Ideally, the various language-specific AWS SDKs would be able to pick up on the current AWS SSO credentials seamlessly. Honoring AWS_PROFILE or AWS_DEFAULT_PROFILE environment variables, and using the same credential lookup algorithm as the CLI. (I assume that's the on the roadmap eventually.)

However, getting that change fixed across all of the AWS SDKs, as well as them upstreamed into tools that rely on them, will take a long time. And it would be nice to unblock my team until then so they can just rely on aws sso login.

I can see the credential files on my local disk, e.g. ~/.aws/sso/cache/61368d38a2497e42a24a243072108001849d0b07.json. But it isn't clear how to map the current set of environment variables to which JSON file to load.

Could the CLI support some way of returning whatever the credentials it is using? e.g.

# Return the SSO credentials file for the provided profile, or just use AWS_PROFILE, etc.
aws --profile staging-environment \
    sts get-caller-credentials

# Or even better: write the SSO credentials to ~/.aws/credentials. So other tools
# could just read from it.
aws sso write-credentials-to-disk --profile staging-environment

I don't know if there is a better approach here, as I'm do not know the specific differences between credential resolution in the AWS CLI vs. AWS SDKs. But hopefully there is some sort of workaround to make this scenario work?

Activity

  1. added
    feature-requestA feature should be added or improved.
    needs-triageThis issue or PR still needs to be triaged.
    on Jun 4, 2020
  2. maestretti commented on Jun 10, 2020

    @maestretti

    This is an important feature, please prioritize it. Many of the AWS SDKs do not work with SSO forcing a workaround. Most SDKs do support external credential_process handlers via configuration profile. It would be great if aws sso could output credentials in the supported format as a one liner. This would eliminate the need for a number of third-party tools that work around this, and the many AWS customers that are rewriting those tools so as not to expose their credentials.

    aws sso get-profile-credentials --profile profileName could get the token from cache and return the STS creds for the given profile in the JSON format for credential_process.

    Format for credential helpers to export: https://docs.aws.amazon.com/cli/latest/userguide/cli-configure-sourcing-external.html

    Example of a 3rd party tool, also has a list of other tools on the README: https://github.com/victorskl/yawsso

  3. aleksandrzhiliaev commented on Aug 13, 2020

    @aleksandrzhiliaev

    Are there any updates on this feature request?

  4. ldormoy commented on Sep 4, 2020

    @ldormoy

    In the meantime, aws-vault v6+ is a nice solution to circumvent this issue in your local environment.

    See 99designs/aws-vault#549

  5. ryansonshine commented on Oct 10, 2020

    @ryansonshine
    Contributor

    I've created an npm package for updating the credentials from the command line for any users out there running node https://github.com/ryansonshine/aws-sso-creds-helper

  6. added and removed
    needs-triageThis issue or PR still needs to be triaged.
    on Oct 19, 2020
  7. kdaily commented on Oct 19, 2020

    @kdaily
    Member

    Hi @chrsmith,

    Thanks for the request and sorry for the delay. This is also related to #4982. Marking as an SSO feature request.

  8. benkehoe commented on Nov 16, 2020

    @benkehoe

    I've also written a utility in Python that supports AWS SSO credentials. https://github.com/benkehoe/aws-export-credentials

  9. m17kea commented on May 18, 2021

    @m17kea

    On the CDK repo they recommend the following wrapper: https://pypi.org/project/aws2-wrap/

  10. m4dc4p commented on Oct 5, 2021

    @m4dc4p

    Please, please fix this. aws sso needs to write ~/.aws/credentials.

  11. added
    needs-reviewThis issue or pull request needs review from a core team member.
    on Oct 5, 2021
  12. 3 remaining items

  13. TechIsCool commented on May 5, 2022

    @TechIsCool

    Since my team and I needed this sooner than the merged PR, which I am glad to see in motion.

    Here is my shell function

    get_me_creds() {
      aws sso get-role-credentials \
        --account-id $(aws configure get sso_account_id --profile ${AWS_PROFILE}) \
        --role-name $(aws configure get sso_role_name --profile ${AWS_PROFILE}) \
        --access-token $(find ~/.aws/sso/cache -type f ! -name "botocore*.json" | xargs jq -r .accessToken) \
        --region $(aws configure get region --profile ${AWS_PROFILE}) |\
      jq -r '.roleCredentials |
          {
            "AWS_ACCESS_KEY_ID": .accessKeyId,
            "AWS_SECRET_ACCESS_KEY": .secretAccessKey,
            "AWS_SESSION_TOKEN": .sessionToken,
            "AWS_CREDENTIALS_EXPIRATION": (.expiration / 1000 | todate)
          } | keys[] as $k | "export \($k)=\(.[$k])"'
    }
    
    # AWS_PROFILE=example get_me_creds
    
  14. datfinesoul commented on Jun 24, 2022

    @datfinesoul

    Thanks @TechIsCool, that function was extremely helpful. I had to make just a few changes to make it work for me, since I swap between SSO orgs, and some profiles didn't have regions, so I defaulted us-east-1 in my case.

    aws-sso-creds() {
      local account_id role_name access_token region
      account_id="$(aws configure get sso_account_id --profile ${AWS_PROFILE})"
      role_name="$(aws configure get sso_role_name --profile ${AWS_PROFILE})"
      region="$(aws configure get region --profile ${AWS_PROFILE})"
      access_token="$( \
        \ls -c "${HOME}/.aws/sso/cache/" | grep -v botocore \
        | sort -nr | cut -d' ' -f2 | head -n1 \
        | xargs -I{} jq -r .accessToken ${HOME}/.aws/sso/cache/{}
      )"
      aws sso get-role-credentials \
        --account-id "${account_id}" \
        --role-name "${role_name}" \
        --region "${region:-us-east-1}" \
        --access-token "${access_token}" \
        --no-sign-request \
        --output json \
        | jq -r '.roleCredentials |
          {
            "AWS_ACCESS_KEY_ID": .accessKeyId,
            "AWS_SECRET_ACCESS_KEY": .secretAccessKey,
            "AWS_SESSION_TOKEN": .sessionToken,
            "AWS_CREDENTIALS_EXPIRATION": (.expiration / 1000 | todate)
          } | keys[] as $k | "export \($k)=\(.[$k])"'
    }

    Edited: Made my first version OSX compatible

  15. ebolingerjc commented on Aug 2, 2022

    @ebolingerjc

    Based on the examples above, my version initializes AWS env vars using .aws/sso & .aws/cli caches. It requires good profiles defined in .aws/config file. Compatible with both Zsh and Bash.

    Source this file from your login profile (.zprofile or .bash_profile) or directly from the shell:
    source ~/setup-aws

    Switch between profiles by exporting AWS_PROFILE with a new value & source this script again.
    export AWS_PROFILE=staging-env
    source ~/setup-aws

    export AWS_PROFILE=${AWS_PROFILE:-development}
    eval $(aws configure list --profile $AWS_PROFILE > /tmp/aws$$ || \
    { aws sso login > /dev/null; aws configure list --profile $AWS_PROFILE > /tmp/aws$$; } && \
    jq -r '.Credentials | {
    "AWS_ACCESS_KEY_ID": .AccessKeyId,
    "AWS_SECRET_ACCESS_KEY": .SecretAccessKey,
    "AWS_SESSION_TOKEN": .SessionToken,
    "AWS_EXPIRATION":.Expiration } |
    keys[] as $k | "export \($k)=\(.[$k])"' $(grep -l $(grep access_key /tmp/aws$$ | cut -c 32-35) ~/.aws/cli/cache/*))
    

    Save that as ~/setup-aws or wherever you like.

  16. jsifuentes commented on Aug 2, 2022

    @jsifuentes

    Love all the script sharing here. I used @datfinesoul's as a base, but I wanted to have more confidence finding the SSO cache file instead of using the first found token. The SSO cache file name is a sha1 based on the sso_start_url, so we can just do the same thing in our script. https://github.com/boto/botocore/blob/b006ff741d12608a9187b873e276abd1fd8eb707/botocore/utils.py#L2364-L2365

    This does not automatically set the environment variables. It just outputs the export statements.

    aws_creds() {
      local profile="${1:-${AWS_PROFILE}}"
      local account_id="$(aws configure get sso_account_id --profile "${profile}")" \
      role_name="$(aws configure get sso_role_name --profile "${profile}")" \
      region="$(aws configure get region --profile "${profile}")" \
      start_url="$(aws configure get sso_start_url --profile "${profile}")"
    
      if [ -z "$start_url" ] ; then
        echo "did not find sso_start_url in profile ${profile}"
        exit 1
      fi
    
      local cache_file="${HOME}/.aws/sso/cache/$(echo -n "$start_url" | sha1sum | awk '{print $1}').json"
    
      if [ ! -f "$cache_file" ] ; then
        echo "sso creds not found. are you logged into AWS SSO?"
        echo ;
        echo "aws sso login --profile \"${profile}\""
        exit 1
      fi
    
      local access_token=$(jq -r .accessToken "${cache_file}")
    
      aws sso get-role-credentials \
        --account-id "${account_id}" \
        --role-name "${role_name}" \
        --region "${region:-us-east-1}" \
        --access-token "${access_token}" \
        --no-sign-request \
        --output json \
        | jq -r '.roleCredentials |
          {
          "AWS_ACCESS_KEY_ID": .accessKeyId,
          "AWS_SECRET_ACCESS_KEY": .secretAccessKey,
          "AWS_SESSION_TOKEN": .sessionToken,
          "AWS_CREDENTIALS_EXPIRATION": (.expiration / 1000 | todate)
          } | keys[] as $k | "export \($k)=\(.[$k])"'
    }
  17. vnagendra commented on Aug 22, 2022

    @vnagendra

    @jsifuentes - I am not sure if this is right..

      local cache_file="${HOME}/.aws/sso/cache/$(echo -n "$start_url" | sha1sum | awk '{print $1}').json"
    

    The code you link to is a great find, I don't want to take away from that. But shouldn't you be doing utf-8 encoding before looking up the file based on SHA1? I have absolutely no idea how to do this in bash... 😂

    Anyway, I tried the following...

    1. I tried to echo whatever I had in the profile (ex: echo https://mydomain.awsapps.com/start)
    2. Then piped it to sha1sum Ex: echo https://mydomain.awsapps.com/start | sha1sum
    3. That is NOT the same as what I had in the ~/.aws/sso/cache directory

    Once I find a solution for this, I'll post here. But you can verify the results fairly easily using this recipe

    https://gchq.github.io/CyberChef/#recipe=Encode_text('UTF-8%20(65001)')SHA1(80)&input=aHR0cHM6Ly9teWRvbWFpbi5hd3NhcHBzLmNvbS9zdGFydA

  18. jsifuentes commented on Aug 22, 2022

    @jsifuentes

    Hi @vnagendra , I'm not sure I understand your question about the UTF-8 encoding. I don't think it's applicable in this case. Make sure you don't forget to include -n in your echo command. The -n is important because it removes the \n that echo automatically includes at the end, so if you're excluding -n, it will lead to a different sha1 sum.

    Hope that helps.

  19. vnagendra commented on Aug 24, 2022

    @vnagendra

    @jsifuentes, Thanks for your help! Using your script as a base, this is what I did.

    My specific use case was there were a couple of Terraform modules. I wanted to upgrade them one by one to the latest version of Terraform. The latest version supports AWS SSO credentials, the older versions don't. I've always used direnv for managing a bunch of environment variables. So I used the following script inside the .envrc of the module that was not upgraded. Meaning if you have the newer Terraform (and provider) version, you do not need all this circus.

    source_up
    
    function aws_sso() {
      local start_url=$(aws configure get sso_start_url)
      if [ -z "$start_url" ]; then
        echo "No start URL. Will not continue."
        return 1
      fi
      local fname=$(echo -n $start_url | sha1sum | awk '{print $1}')
      local cache_file=${HOME}/.aws/sso/cache/${fname}.json
      if [ ! -f "$cache_file" ]; then
        echo "No SSO cache found. Logged in?"
        return 1
      fi
      local access_token=$(jq -r .accessToken "${cache_file}")
      aws sso get-role-credentials \
        --account-id "$(aws configure get sso_account_id)" \
        --role-name "$(aws configure get sso_role_name)" \
        --region "$(aws configure get region)" \
        --access-token "${access_token}" \
        --no-sign-request \
        --output json | jq -r '.roleCredentials'
    }
    
    local f=$(aws_sso)
    if [ "$f" ]; then
    	AWS_ACCESS_KEY_ID=$(echo -n $f | jq -r '.accessKeyId')
    	AWS_SECRET_ACCESS_KEY=$(echo -n $f | jq -r '.secretAccessKey')
    	AWS_SESSION_TOKEN=$(echo -n $f | jq -r '.sessionToken')
    	echo "Token Expires $(echo -n $f | jq -r '(.expiration / 1000 | todate)')"
    fi
    

    Please note, if you use this trick -- you must have the "function aws_sso()" declared inside that specific .envrc (inside the module that is not upgraded). In my case the source_up was pointing to another .envrc (in the parent directory of course) which was just exporting the following

    set -a
    AWS_PROFILE=sso
    AWS_DEFAULT_REGION=us-west-2
    

    With this setup, it was fairly simple..

    > cd my-terraform/
    direnv: loading ...../.envrc
    direnv: export +AWS_DEFAULT_REGION +AWS_PROFILE +ret
    
    > aws sso login
    Attempting to automatically open the SSO authorization page in your default browser.
    If the browser does not open or you wish to use a different device to authorize this request, open the following URL:
    
    https://device.sso.us-west-2.amazonaws.com/
    
    Then enter the code:
    
    DHGN-WKFW
    Successfully logged into Start URL: https://something.awsapps.com/start
    
    > cd my-not-upgraded-module/
    direnv: loading ..../.envrc
    direnv: loading ......./.envrc
    Token Expires 2022-08-23T22:25:29Z
    direnv: export +AWS_ACCESS_KEY_ID +AWS_DEFAULT_REGION +AWS_PROFILE +AWS_SECRET_ACCESS_KEY +AWS_SESSION_TOKEN +ret
    
  20. added 2 commits that reference this issue on Nov 2, 2022
    ef9fab7
    ace597d
  21. drewwells commented on Nov 8, 2022

    @drewwells

    The code examples above work by mutating the AWS environment variables. This can be problematic if you're using multiple environments. I altered a previous script to instead dump credentials into the credentials file. Now I can sso into multiple profiles

    I use this to run it
    aws_creds | xargs -d\\n -n1 sh -c

    aws_creds() {
      local profile="${1:-${AWS_PROFILE}}"
      local account_id="$(aws configure get sso_account_id --profile "${profile}")" \
      role_name="$(aws configure get sso_role_name --profile "${profile}")" \
      region="$(aws configure get region --profile "${profile}")" \
      start_url="$(aws configure get sso_start_url --profile "${profile}")"
    
      if [ -z "$start_url" ] ; then
        echo "did not find sso_start_url in profile ${profile}"
        exit 1
      fi
    
      local cache_file="${HOME}/.aws/sso/cache/$(echo -n "$start_url" | sha1sum | awk '{print $1}').json"
    
      if [ ! -f "$cache_file" ] ; then
        echo "sso creds not found. are you logged into AWS SSO?"
        echo ;
        echo "aws sso login --profile \"${profile}\""
        exit 1
      fi
    
      local access_token=$(jq -r .accessToken "${cache_file}")
    
      aws sso get-role-credentials \
        --account-id "${account_id}" \
        --role-name "${role_name}" \
        --region "${region:-us-east-1}" \
        --access-token "${access_token}" \
        --no-sign-request \
        --output json \
        | jq --arg p "${AWS_PROFILE}" -r '.roleCredentials |
          {
          "aws_access_key_id": .accessKeyId,
          "aws_secret_access_key": .secretAccessKey,
          "aws_session_token": .sessionToken,
          "aws_credentials_expiration": (.expiration / 1000 | todate)
          } | keys[] as $k | "aws configure set --profile \($p) \($k) \"\(.[$k])\";\n"'}
    }
    
  22. tim-finnigan commented on Feb 24, 2023

    @tim-finnigan
    Contributor

    I think this can now be closed as #7398 was merged. Please refer to this documentation for aws configure export-credentials: https://awscli.amazonaws.com/v2/documentation/api/latest/reference/configure/export-credentials.html

  23. github-actions commented on Feb 24, 2023

    @github-actions
    Contributor

    ⚠️COMMENT VISIBILITY WARNING⚠️

    Comments on closed issues are hard for our team to see.
    If you need more assistance, please open a new issue that references this one. If you wish to keep having a conversation with other community members under this issue feel free to do so.

  24. apogrebnyak commented on Sep 28, 2023

    @apogrebnyak

    For AWS cli > 2.13.5 which uses sso-session sections in configuration file, you need to change the SHA1 source to sso_session value in your profile

    ...
    local sso_session="$(aws configure get sso_session --profile "${profile}")"
    ...
    local cache_file="${HOME}/.aws/sso/cache/$(echo -n "$sso_session" | sha1sum | awk '{print $1}').json"
    
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    configurationfeature-requestA feature should be added or improved.needs-reviewThis issue or pull request needs review from a core team member.p2This is a standard priority issuessov2

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions