Secure Token Service

Get a token

Role to assumearn:aws:iam::973674585612:role/ViewSecretAircraftRole
Role session nametheRole
Duration900

Token details

AssumedRoleUserArn
AssumedRoleId
CredentialsAccessKeyId
SecretAccessKey
SessionToken
Expiration

Use the token

URLhttp://secret.demo.wlid.nl/a17.jpg

What's happening here?

Secure Token Service is similar to Signed URLs: It allows a regular IAM user to delegate the authority to access something, to somebody else.

Signed URLs are relatively simple since they only apply to S3 buckets or objects, and the URL that is produced can be used immediately in a browser. STS tokens are a lot more complex, since they allow access to virtually any AWS API call. Therefore STS does not give you a URL, but it gives you credentials to assume a role. This role then has a regular policy attached that tells AWS whether a particular API call is allowed.

This demo will go through all the steps required to setup the user account and roles, generate the STS token, and will then use that token to generate a signed URL. The signed URL is then used to display the image of a secret aircraft.

This demo is contained in a single HTML page. It is important to note that this is not normally the case. Normally the first step (generating the STS token) is done in a secure environment, for instance a server that you trust. It is therefore completely acceptable to store the user account credentials on this server. The result of this first step, the token, is then forwarded to a client system that is not necessarily trusted. With this token, this untrusted system can only access the resources allowed by the role, and for a limited time only. In order to simulate the two-step process I have actually broken up the demo into those two steps, and the communication between the two steps is done by embedding the STS token in the HTML page. There is no other communication between the two steps other than what you see on the screen.

How to set this up.

First, setup an IAM user. I called this IAM user STSDemo. The IAM user does not need any policies or group memberships, but it does need Security Credentials. These need to be stored on the secure server where the STS token generation will take place. In case of this demo, they are stored inside the JavaScript function that generates the token.

Second, set up an IAM role that allows you to perform the API call(s) that you want your users to perform. In this case, I have reused the "ViewSecretAircraftRole" from my Signed URL demo. This role has the following policy attached:

{
    "Version": "2012-10-17",
    "Statement": [
        {
            "Effect": "Allow",
            "Action": [
                "s3:GetObject"
            ],
            "Resource": "arn:aws:s3:::secret.demo.wlid.nl/a17.jpg"
        }
    ]
}

The role also has a trust relationship so that the user "STSDemo" is able to assume this role. This trust relationship looks like this:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Principal": {
        "AWS": "arn:aws:iam::973674585612:user/STSDemo"
      },
      "Action": "sts:AssumeRole"
    }
  ]
}

That's the basic preparation done. Any user that's able to switch to a role, is also implicitly able to generate an STS token that lets others switch to that role. Here's the JavaScript code that accomplishes this:

function getToken()
{
  // See the Configuring section to configure credentials in the SDK
  // Note that encapsulating credentials is generally considered bad practice.
  // In this particular case, the IAM user that these credentials point to, is
  // scoped with very, very limited privileges: To assume the ViewSecretAircraftRole only.
  AWS.config.update( {accessKeyId: 'AKIAIKXSZKE3JYH2UU4Q',
                      secretAccessKey: 'QAZ3BUkzQNenDNz/5FKWaLUgRwC7DCvqx3Vdb51O'} );

  // Configure your region
  AWS.config.region = 'eu-central-1';

  // Get the STS Token
  var sts = new AWS.STS({apiVersion: '2011-06-15'});
  var params = { RoleArn: document.getElementById("role").innerHTML,
                 RoleSessionName: document.getElementById("roleSessionName").innerHTML,
                 DurationSeconds: document.getElementById("duration").innerHTML };
  sts.assumeRole( params, function( err, data )
  {
    if (err) console.log(err, err.stack); // an error occurred
    else     console.log(data);           // successful response

    document.getElementById("assumedRoleId").innerHTML = data.AssumedRoleUser.AssumedRoleId;
    document.getElementById("arn").innerHTML = data.AssumedRoleUser.Arn;
    document.getElementById("accessKeyId").innerHTML = data.Credentials.AccessKeyId;
    document.getElementById("expiration").innerHTML = data.Credentials.Expiration;
    document.getElementById("secretAccessKey").innerHTML = data.Credentials.SecretAccessKey;
    document.getElementById("sessionToken").innerHTML = data.Credentials.SessionToken;
  } );
}

As you can see, we are using the user credentials embedded in the JavaScript, plus the role to assume and a few other parameters that are embedded in the HTML, to run the sts.assumeRole API call. The result of this, if everything is correct, is a set of credentials (Access Key, Secret Key, Expiration and Session Token). These are again embedded in the HTML of this page so that you can view them.

The next step is to use these credentials:

function useToken()
{
  // Use the STS token
  AWS.config.update( {accessKeyId: document.getElementById("accessKeyId").innerHTML,
                      secretAccessKey: document.getElementById("secretAccessKey").innerHTML,
		      sessionToken: document.getElementById("sessionToken").innerHTML } );

  // Configure your region
  AWS.config.region = 'eu-central-1';

  // Get the signed URL
  var s3 = new AWS.S3();
  var params = { Bucket: 'secret.demo.wlid.nl', Key: 'a17.jpg', Expires: 10 };
  s3.getSignedUrl( 'getObject', params, function( err, url )
  {	
    if( err ) console.log( err, err.stack );
    else console.log( url );

    document.getElementById("url").innerHTML = url;
    document.getElementById("img").src = url;
  } );
}

We're pulling the access key, secret key and session token from the HTML page. Armed with these credentials we can perform an API call, s3.getSignedUrl. Since the role that we assume with these credentials has access to the S3 object, the getSignedUrl call succeeds and returns a Signed URL. We then use this Signed URL to view the picture of a secret aircraft.

Once again, note that this last part would normally be executed on an insecure client system. You do not want to trust that client system with the IAM user credentials, so you use an expiring STS token instead.

The s3.getSignedUrl API call is just an example which works fine for this demo. But in reality you can perform virtually any AWS API call. As long as you have an unexpired token that allows you to assume a role that allows the API call. This is what makes STS great for securely embedding AWS API calls in client/server applications.