skip to Main Content

As per my comment under the Accepted Answer on this post:

Error message when calling Api Gateway with signature

I cannot figure out how to get the Session Token to use in C#.

I’ve gone through this tutorial – https://docs.aws.amazon.com/pinpoint/latest/developerguide/tutorials-using-postman-configuration.html
And it works perfectly in Postman. But the accepted answer on the SO Question fails due to authentication.

Somehow Postman is generating the session token for me to use. And I can’t see how it’s done in both the documentation and in the SO Accepted Answer.

Is someone could show me how to get the token via C# and do a successful call to the API, that would be greatly appreciated.

Thanks in advance

3

Answers


  1. The process explained through the Postman collections does not use a session token. It signs the request with the Access and Secret keys when consuming the endpoints.

    This library should assist you in consuming the AWS services through HTTP APIs.

    NuGet: Aws4RequestSigner

    In case you do not want to use a 3rd party library, you can define your own implementation with this reference documentation.

    Login or Signup to reply.
  2. In order to authenticate your request with Pinpoint APIs you need to pass Authorization header which contains accesssKey, region, and serviceName variables you have set in Postman. If you inspect the Postman collections you have imported and navigate to "Headers" you can click to reveal hidden headers – there you can find which HTTP headers you actually pass with your request, so you can mimic that with your C# implementation.
    How to do this in C#? Various ways. You simply need to create HTTP Client with Authorization header and pass the same value you see in Postman

    Login or Signup to reply.
  3. When you consider Signing AWS API requests, the process becomes much more elaborate: AWS uses temporary security credentials, consisting of an access key ID, a secret access key, and a security token, to enable applications to send authenticated requests on your behalf without sharing your long-term AWS security credentials. A session token is part of these temporary security credentials.

    Session tokens are issued by the AWS Security Token Service (STS).
    AWS STS is a web service that enables you to request temporary, limited-privilege credentials for AWS Identity and Access Management (IAM) users or for users that you authenticate (federated users).

    When you make a call to AWS STS to assume a role or a federated user, AWS STS returns the temporary security credentials, which consist of the access key, secret key, and session token. These temporary security credentials are then used to sign the AWS API requests.

    For instance, if you want to use temporary security credentials to sign an AWS API request:

    1. You or your application calls the AssumeRole (for IAM users) or GetFederationToken (for federated users) operation of AWS STS. You include an IAM role ARN or federated user credentials in the API call.

    2. AWS STS verifies the role or user credentials and returns temporary security credentials, which include an access key ID, a secret access key, and a session token.

    3. You use these temporary security credentials (access key ID, secret access key, and session token) to sign subsequent AWS API requests.

    4. AWS verifies the signature on the incoming request. If the signature is valid, and the session token is valid and has not expired, AWS allows the API call.

    5. The API call is processed, and the result is returned to you or your application.

    The AWS SDKs handle these steps automatically. When you use an AWS SDK to make requests, you specify the temporary security credentials (access key ID, secret access key, and session token), and the SDK uses them to sign the requests for you.

    In the context of AWS Signature Version 4, the session token is included in the X-Amz-Security-Token header of the HTTP request. Note that the session token itself is not included in the signature calculation.

    • When you are making a call to AWS STS to get a session token, you do not need to include the X-Amz-Security-Token parameter because you do not have a session token yet.
    • When you have a session token (obtained from AWS STS) and are using it to sign requests to other AWS services, you include the session token value in the X-Amz-Security-Token header as part of the HTTP request. This allows AWS to verify the temporary security credentials that you are using to make the request.

    While using temporary security credentials is optional, in your case AWS session tokens are generated by making a request to the AWS Security Token Service (STS). This is typically done when you want to use temporary security credentials instead of the long-term credentials associated with an IAM user.

    The steps to create a canonical request, create a string to sign, and calculate the signature are part of the process of signing the request to AWS services. These steps are generally performed after obtaining the temporary security credentials (including the session token) from AWS STS. Here is a general sequence of the steps:

    1. Call AWS STS to get temporary security credentials. This will give you an access key ID, a secret access key, and a session token.
    2. Use the access key ID and secret access key from the previous step to create a canonical request for the AWS service API operation you want to call.
    3. Create a string to sign based on the canonical request.
    4. Calculate the signature using the string to sign and your secret access key.
    5. Include the calculated signature and session token in the headers of your HTTP request when you call the AWS service API operation.

    This sequence ensures that your request to the AWS service is both authenticated (proves who you are) and authorized (proves you have permission to perform the requested operation).

    1. Create a Canonical Request: This includes all the information that you want to send in your request, including the HTTP method, the URL path, the query string parameters, the headers, and the payload (or body) of the request. This information is represented in a specific format defined by AWS.

    2. Create a String to Sign: This includes the hashed canonical request along with additional metadata, such as the algorithm you are using for signing (AWS4-HMAC-SHA256), the date and time of the request, and the service and region you are making the request to.

    3. Calculate the Signature: This involves using your AWS secret access key to create a signing key, and then using that signing key to create a signature from the string to sign. The signature proves that the request is authentic and has not been tampered with.

    After generating the signature, you include it in the Authorization header of your HTTP request, along with other necessary information such as your AWS access key ID and the headers that you included in the canonical request. If you are using temporary security credentials, you also include the session token in the X-Amz-Security-Token header.

    As a rough example:

    using Amazon;
    using Amazon.Runtime;
    using Amazon.SecurityToken;
    using Amazon.SecurityToken.Model;
    using System;
    using System.Net.Http;
    using System.Threading.Tasks;
    
    public class AWSSignatureExample
    {
        private const string AccessKey = "Your AWS Access Key";
        private const string SecretKey = "Your AWS Secret Key";
        private const string Region = "us-west-2"; // Or your preferred region
    
        public async Task CallAWSService()
        {
            // Step 1: Get temporary security credentials from STS
            var creds = new BasicAWSCredentials(AccessKey, SecretKey);
            var stsClient = new AmazonSecurityTokenServiceClient(creds, RegionEndpoint.GetBySystemName(Region));
            var getSessionTokenRequest = new GetSessionTokenRequest
            {
                DurationSeconds = 3600 // 1 hour
            };
            var sessionTokenResponse = await stsClient.GetSessionTokenAsync(getSessionTokenRequest);
            var temporaryCredentials = sessionTokenResponse.Credentials;
    
            // Steps 2-4: Create a canonical request, create a string to sign, and calculate the signature
            var signer = new AWS4Signer();
            signer.Initialize(temporaryCredentials.AccessKeyId, temporaryCredentials.SecretAccessKey, Region);
            var request = new HttpRequestMessage(HttpMethod.Get, "https://my-service-url"); // Replace with your service URL
            signer.Sign(request, temporaryCredentials.SessionToken);
    
            // Step 5: Send the request
            var httpClient = new HttpClient();
            var response = await httpClient.SendAsync(request);
        }
    }
    
    public class AWS4Signer
    {
        public void Initialize(string accessKey, string secretKey, string region)
        {
            // Initialize your signer with the access key, secret key, and region
        }
    
        public void Sign(HttpRequestMessage request, string sessionToken)
        {
            // Sign the request here using the AWS Signature Version 4 process
            // You would generate the canonical request, create the string to sign, calculate the signature,
            // and then add the 'Authorization' and 'X-Amz-Security-Token' headers to the request
        }
    }
    

    This example uses the AWS SDK for .NET (C#) and assumes that you have implemented the AWS Signature Version 4 process in the AWS4Signer class.

    Also, this example uses your AWS root user access key and secret key to call AWS STS. In a production scenario, you should avoid using root user credentials. Instead, use the credentials of an IAM user that has the necessary permissions, or use AWS Identity and Access Management (IAM) roles if you are running your application on Amazon EC2, AWS Lambda, or other AWS service.

    Do replace "Your AWS Access Key" and "Your AWS Secret Key" with your actual AWS access key and secret key, and replace "https://my-service-url" with the URL of the AWS service you want to call.

    An example of AWS Signature Version 4 process implementation in the AWS4Signer class would be:

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Net.Http;
    using System.Security.Cryptography;
    using System.Text;
    
    public class AWS4Signer
    {
        private const string Algorithm = "AWS4-HMAC-SHA256";
        private string accessKey;
        private string secretKey;
        private string region;
    
        public void Initialize(string accessKey, string secretKey, string region)
        {
            this.accessKey = accessKey;
            this.secretKey = secretKey;
            this.region = region;
        }
    
        public void Sign(HttpRequestMessage request, string sessionToken)
        {
            var date = DateTime.UtcNow;
            var dateStamp = date.ToString("yyyyMMdd");
            var dateTimeStamp = date.ToString("yyyyMMddTHHmmssZ");
    
            request.Headers.Add("x-amz-date", dateTimeStamp);
            request.Headers.Add("x-amz-security-token", sessionToken);
    
            string canonicalUri = request.RequestUri.AbsolutePath;
            string canonicalQueryString = request.RequestUri.Query.TrimStart('?');
            string canonicalHeaders = string.Join("n", request.Headers.OrderBy(x => x.Key).Select(x => $"{x.Key}:{x.Value.First()}")) + "n";
            string signedHeaders = string.Join(";", request.Headers.Select(x => x.Key));
            string payloadHash = HashAndHexEncode("");
    
            string canonicalRequest = $"{request.Method}n{canonicalUri}n{canonicalQueryString}n{canonicalHeaders}n{signedHeaders}n{payloadHash}";
    
            string credentialScope = $"{dateStamp}/{region}/execute-api/aws4_request";
            string stringToSign = $"{Algorithm}n{dateTimeStamp}n{credentialScope}n{HashAndHexEncode(canonicalRequest)}";
    
            string signature = CreateSignature(stringToSign, dateStamp);
    
            string authorizationHeader = $"{Algorithm} Credential={accessKey}/{credentialScope}, SignedHeaders={signedHeaders}, Signature={signature}";
            request.Headers.Add("Authorization", authorizationHeader);
        }
    
        private string HashAndHexEncode(string data)
        {
            using var sha256 = SHA256.Create();
            byte[] hash = sha256.ComputeHash(Encoding.UTF8.GetBytes(data));
            return BitConverter.ToString(hash).Replace("-", "").ToLower();
        }
    
        private string CreateSignature(string stringToSign, string dateStamp)
        {
            byte[] kDate = HmacSha256(dateStamp, Encoding.UTF8.GetBytes("AWS4" + secretKey));
            byte[] kRegion = HmacSha256(region, kDate);
            byte[] kService = HmacSha256("execute-api", kRegion);
            byte[] kSigning = HmacSha256("aws4_request", kService);
    
            return BitConverter.ToString(new HMACSHA256(kSigning).ComputeHash(Encoding.UTF8.GetBytes(stringToSign))).Replace("-", "").ToLower();
        }
    
        private byte[] HmacSha256(string data, byte[] key)
        {
            return new HMACSHA256(key).ComputeHash(Encoding.UTF8.GetBytes(data));
        }
    }
    
    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search