| 91 | * The signing logic is described here: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_sigv-create-signed-request.html |
| 92 | */ |
| 93 | export async function aws4Sign( |
| 94 | options: AwsSigv4Options, |
| 95 | credentials: AWSCredentials |
| 96 | ): Promise<SignedHeaders> { |
| 97 | /** |
| 98 | * From the spec: https://docs.aws.amazon.com/IAM/latest/UserGuide/reference_sigv-create-signed-request.html |
| 99 | * |
| 100 | * Summary of signing steps |
| 101 | * 1. Create a canonical request |
| 102 | * Arrange the contents of your request (host, action, headers, etc.) into a standard canonical format. The canonical request is one of the inputs used to create the string to sign. |
| 103 | * 2. Create a hash of the canonical request |
| 104 | * Hash the canonical request using the same algorithm that you used to create the hash of the payload. The hash of the canonical request is a string of lowercase hexadecimal characters. |
| 105 | * 3. Create a string to sign |
| 106 | * Create a string to sign with the canonical request and extra information such as the algorithm, request date, credential scope, and the hash of the canonical request. |
| 107 | * 4. Derive a signing key |
| 108 | * Use the secret access key to derive the key used to sign the request. |
| 109 | * 5. Calculate the signature |
| 110 | * Perform a keyed hash operation on the string to sign using the derived signing key as the hash key. |
| 111 | * 6. Add the signature to the request |
| 112 | * Add the calculated signature to an HTTP header or to the query string of the request. |
| 113 | */ |
| 114 | |
| 115 | // 1: Create a canonical request |
| 116 | |
| 117 | // Date – The date and time used to sign the request. |
| 118 | const date = options.date; |
| 119 | // RequestDateTime – The date and time used in the credential scope. This value is the current UTC time in ISO 8601 format (for example, 20130524T000000Z). |
| 120 | const requestDateTime = date.toISOString().replace(/[:-]|\.\d{3}/g, ''); |
| 121 | // RequestDate – The date used in the credential scope. This value is the current UTC date in YYYYMMDD format (for example, 20130524). |
| 122 | const requestDate = requestDateTime.substring(0, 8); |
| 123 | // Method – The HTTP request method. For us, this is always 'POST'. |
| 124 | const method = options.method; |
| 125 | // CanonicalUri – The URI-encoded version of the absolute path component URI, starting with the / that follows the domain name and up to the end of the string |
| 126 | // For our requests, this is always '/' |
| 127 | const canonicalUri = options.path; |
| 128 | // CanonicalQueryString – The URI-encoded query string parameters. For our requests, there are no query string parameters, so this is always an empty string. |
| 129 | const canonicalQuerystring = ''; |
| 130 | |
| 131 | // CanonicalHeaders – A list of request headers with their values. Individual header name and value pairs are separated by the newline character ("\n"). |
| 132 | // All of our known/expected headers are included here, there are no extra headers. |
| 133 | const headers = new Headers({ |
| 134 | 'content-length': convertHeaderValue(options.headers['Content-Length']), |
| 135 | 'content-type': convertHeaderValue(options.headers['Content-Type']), |
| 136 | host: convertHeaderValue(options.host), |
| 137 | 'x-amz-date': convertHeaderValue(requestDateTime), |
| 138 | 'x-mongodb-gs2-cb-flag': convertHeaderValue(options.headers['X-MongoDB-GS2-CB-Flag']), |
| 139 | 'x-mongodb-server-nonce': convertHeaderValue(options.headers['X-MongoDB-Server-Nonce']) |
| 140 | }); |
| 141 | // If session token is provided, include it in the headers |
| 142 | if ('sessionToken' in credentials && credentials.sessionToken) { |
| 143 | headers.append('x-amz-security-token', convertHeaderValue(credentials.sessionToken)); |
| 144 | } |
| 145 | |
| 146 | // Canonical headers are lowercased and sorted. |
| 147 | const canonicalHeaders = Array.from(headers.entries()) |
| 148 | .map(([key, value]) => `${key.toLowerCase()}:${value}`) |
| 149 | .sort() |
| 150 | .join('\n'); |