@@ -33,6 +33,8 @@ type Signer struct {
3333 Algorithm string
3434 CredentialScope string
3535 Finalizer Finalizer
36+
37+ SignatureType v4.SignatureType
3638}
3739
3840// Finalizer performs the final step in v4 signing, deriving a signature for
@@ -53,15 +55,26 @@ func (s *Signer) Do() error {
5355
5456 s .setRequiredHeaders ()
5557
56- canonicalRequest , signedHeaders := s .buildCanonicalRequest ()
58+ // Build canonical headers first to get signedHeaders
59+ canonHeaders , signedHeaders := s .buildCanonicalHeaders ()
60+
61+ if s .SignatureType == v4 .SignatureTypeQueryString {
62+ s .addSignatureParametersToQuery (signedHeaders )
63+ }
64+
65+ canonicalRequest := s .buildCanonicalRequestWithHeaders (canonHeaders , signedHeaders )
5766 stringToSign := s .buildStringToSign (canonicalRequest )
5867 signature , err := s .Finalizer .SignString (stringToSign )
5968 if err != nil {
6069 return err
6170 }
6271
63- s .Request .Header .Set ("Authorization" ,
64- s .buildAuthorizationHeader (signature , signedHeaders ))
72+ if s .SignatureType == v4 .SignatureTypeQueryString {
73+ s .addSignatureToQuery (signature , signedHeaders )
74+ } else {
75+ s .Request .Header .Set ("Authorization" ,
76+ s .buildAuthorizationHeader (signature , signedHeaders ))
77+ }
6578
6679 return nil
6780}
@@ -108,20 +121,32 @@ func (s *Signer) setRequiredHeaders() {
108121 headers := s .Request .Header
109122
110123 s .Request .Header .Set ("Host" , s .Request .Host )
111- s .Request .Header .Set ("X-Amz-Date" , s .Time .Format (TimeFormat ))
112124
113- if len (s .Credentials .SessionToken ) > 0 {
114- s .Request .Header .Set ("X-Amz-Security-Token" , s .Credentials .SessionToken )
125+ // X-Amz-Date and X-Amz-Security-Token are only set as headers when using a header signature type
126+ if s .SignatureType == v4 .SignatureTypeHeader {
127+ s .Request .Header .Set ("X-Amz-Date" , s .Time .Format (TimeFormat ))
128+ if len (s .Credentials .SessionToken ) > 0 {
129+ s .Request .Header .Set ("X-Amz-Security-Token" , s .Credentials .SessionToken )
130+ }
131+ } else {
132+ // For query string auth, ensure these headers are not present
133+ s .Request .Header .Del ("X-Amz-Date" )
134+ s .Request .Header .Del ("X-Amz-Security-Token" )
115135 }
136+
116137 if len (s .PayloadHash ) > 0 && s .Options .AddPayloadHashHeader {
117138 headers .Set ("X-Amz-Content-Sha256" , payloadHashString (s .PayloadHash ))
118139 }
119140}
120141
121142func (s * Signer ) buildCanonicalRequest () (string , string ) {
143+ canonHeaders , signedHeaders := s .buildCanonicalHeaders ()
144+ canonicalRequest := s .buildCanonicalRequestWithHeaders (canonHeaders , signedHeaders )
145+ return canonicalRequest , signedHeaders
146+ }
147+
148+ func (s * Signer ) buildCanonicalRequestWithHeaders (canonHeaders , signedHeaders string ) string {
122149 canonPath := s .Request .URL .EscapedPath ()
123- // https://docs.aws.amazon.com/IAM/latest/UserGuide/create-signed-request.html:
124- // if input has no path, "/" is used
125150 if len (canonPath ) == 0 {
126151 canonPath = "/"
127152 }
@@ -135,8 +160,6 @@ func (s *Signer) buildCanonicalRequest() (string, string) {
135160 }
136161 canonQuery := strings .Replace (query .Encode (), "+" , "%20" , - 1 )
137162
138- canonHeaders , signedHeaders := s .buildCanonicalHeaders ()
139-
140163 req := strings .Join ([]string {
141164 s .Request .Method ,
142165 canonPath ,
@@ -146,7 +169,7 @@ func (s *Signer) buildCanonicalRequest() (string, string) {
146169 payloadHashString (s .PayloadHash ),
147170 }, "\n " )
148171
149- return req , signedHeaders
172+ return req
150173}
151174
152175func (s * Signer ) buildCanonicalHeaders () (canon , signed string ) {
@@ -203,6 +226,28 @@ func (s *Signer) buildAuthorizationHeader(signature, headers string) string {
203226 signature )
204227}
205228
229+ func (s * Signer ) addSignatureParametersToQuery (signedHeaders string ) {
230+ query := s .Request .URL .Query ()
231+ query .Set ("X-Amz-Algorithm" , s .Algorithm )
232+ query .Set ("X-Amz-Credential" , s .Credentials .AccessKeyID + "/" + s .CredentialScope )
233+ query .Set ("X-Amz-Date" , s .Time .Format (TimeFormat ))
234+ query .Set ("X-Amz-SignedHeaders" , signedHeaders )
235+
236+ if len (s .Credentials .SessionToken ) > 0 {
237+ query .Set ("X-Amz-Security-Token" , s .Credentials .SessionToken )
238+ }
239+
240+ s .Request .URL .RawQuery = query .Encode ()
241+ }
242+
243+ func (s * Signer ) addSignatureToQuery (signature , signedHeaders string ) {
244+ query := s .Request .URL .Query ()
245+ query .Set ("X-Amz-SignedHeaders" , signedHeaders )
246+ query .Set ("X-Amz-Signature" , signature )
247+
248+ s .Request .URL .RawQuery = query .Encode ()
249+ }
250+
206251func payloadHashString (p []byte ) string {
207252 if string (p ) == "UNSIGNED-PAYLOAD" {
208253 return string (p ) // sentinel, do not hex-encode
0 commit comments