@@ -68,6 +68,8 @@ Public ConsumerSecret As String
6868Public Token As String
6969Public TokenSecret As String
7070Public Realm As String
71+ Public Nonce As String
72+ Public Timestamp As String
7173
7274' ============================================= '
7375' Public Methods
@@ -106,7 +108,7 @@ End Sub
106108
107109Private Sub IAuthenticator_BeforeExecute (ByVal Client As RestClient , ByRef Request As RestRequest )
108110 ' Add authorization header to request
109- Request.AddHeader "Authorization" , CreateHeader(Request)
111+ Request.AddHeader "Authorization" , CreateHeader(Client, Request)
110112End Sub
111113
112114''
@@ -136,18 +138,28 @@ Private Sub IAuthenticator_HttpOpen(ByRef Http As Object, ByVal Client As RestCl
136138 Call Http .Open (Request.MethodName(), Request.FullUrl(BaseUrl), UseAsync)
137139End Sub
138140
139- Private Function CreateHeader (Request As RestRequest ) As String
141+ Public Function CreateHeader (Client As RestClient , Request As RestRequest ) As String
140142 Dim Header As String
141- Dim nonce As String
142- Dim timestamp As String
143+ Dim Nonce As String
144+ Dim Timestamp As String
143145 Dim base As String
144146 Dim signingKey As String
145147 Dim signature As String
146148
149+ ' Load or create nonce and timestamp
150+ If Me.Nonce <> "" Then
151+ Nonce = Me.Nonce
152+ Else
153+ Nonce = RestHelpers.CreateNonce()
154+ End If
155+ If Me.Timestamp <> "" Then
156+ Timestamp = Me.Timestamp
157+ Else
158+ Timestamp = CreateTimestamp
159+ End If
160+
147161 ' Create needed parts of authorization header
148- nonce = CreateNonce()
149- timestamp = CreateTimestamp()
150- base = CreateBaseString(nonce, timestamp, Request)
162+ base = CreateBaseString(Nonce, Timestamp, Client, Request)
151163 signingKey = CreateSigningKey()
152164 signature = CreateSignature(base, signingKey)
153165
@@ -160,46 +172,85 @@ Private Function CreateHeader(Request As RestRequest) As String
160172
161173 ' Construct header parts
162174 ' [OAuth Core 1.0 Revision A](http://oauth.net/core/1.0a/)
163- Header = Header & "oauth_consumer_key=" & Chr(34 ) & Me.ConsumerKey & Chr(34 )
164- Header = Header & ", oauth_nonce=" & Chr(34 ) & nonce & Chr(34 )
165- Header = Header & ", oauth_signature=" & Chr(34 ) & URLEncode (signature) & Chr(34 )
166- Header = Header & ", oauth_signature_method=" & Chr(34 ) & SignatureMethod & Chr(34 )
167- Header = Header & ", oauth_timestamp=" & Chr(34 ) & timestamp & Chr(34 )
168- Header = Header & ", oauth_token=" & Chr(34 ) & Me.Token & Chr(34 )
169- Header = Header & ", oauth_version=" & Chr(34 ) & "1.0" & Chr(34 )
175+ Header = Header & "oauth_consumer_key=" & Chr(34 ) & Me.ConsumerKey & Chr(34 ) & ", "
176+ Header = Header & "oauth_nonce=" & Chr(34 ) & Nonce & Chr(34 ) & ", "
177+ Header = Header & "oauth_signature=" & Chr(34 ) & UrlEncode (signature) & Chr(34 ) & ", "
178+ Header = Header & "oauth_signature_method=" & Chr(34 ) & SignatureMethod & Chr(34 ) & ", "
179+ Header = Header & "oauth_timestamp=" & Chr(34 ) & Timestamp & Chr(34 ) & ", "
180+ Header = Header & "oauth_token=" & Chr(34 ) & Me.Token & Chr(34 ) & ", "
181+ Header = Header & "oauth_version=" & Chr(34 ) & "1.0" & Chr(34 )
170182 CreateHeader = Header
171183End Function
172184
173- Private Function CreateBaseString (nonce As String , timestamp As String , Request As RestRequest ) As String
185+ Public Function CreateBaseString (Nonce As String , Timestamp As String , Client As RestClient , Request As RestRequest ) As String
174186 Dim base As String
175- Dim paramKey As Variant
176187
177- base = "oauth_consumer_key" & "=" & Me.ConsumerKey
178- base = base & "&" & "oauth_nonce" & "=" & nonce
188+ ' Check for parameters and add to base if present
189+ Dim Parameters As String
190+ Parameters = RequestParameters(Client, Request)
191+ If Parameters <> "" Then
192+ base = Parameters & "&"
193+ End If
194+
195+ base = base & "oauth_consumer_key" & "=" & Me.ConsumerKey
196+ base = base & "&" & "oauth_nonce" & "=" & Nonce
179197 base = base & "&" & "oauth_signature_method" & "=" & SignatureMethod
180- base = base & "&" & "oauth_timestamp" & "=" & timestamp
198+ base = base & "&" & "oauth_timestamp" & "=" & Timestamp
181199 base = base & "&" & "oauth_token" & "=" & Me.Token
182200 base = base & "&" & "oauth_version=1.0"
183- If Not IsMissing(Request.Parameters) And Not Request.Parameters Is Nothing Then
184- If Request.Parameters.count > 0 Then
185- For Each paramKey In Request.Parameters.keys
186- base = base & "&" & paramKey & "=" & URLEncode(Request.Parameters(paramKey))
187- Next paramKey
188- End If
201+
202+ CreateBaseString = Request.MethodName() & "&" & RestHelpers.UrlEncode(RequestUrl(Client, Request)) & "&" & RestHelpers.UrlEncode(base)
203+ End Function
204+
205+ Public Function RequestUrl (Client As RestClient , Request As RestRequest ) As String
206+ ' From OAuth 1.0 Docs
207+ ' http://oauth.net/core/1.0/#anchor14
208+ '
209+ ' The Signature Base String includes the request absolute URL, tying the signature to a specific endpoint.
210+ ' The URL used in the Signature Base String MUST include the scheme, authority, and path, and MUST exclude the query and fragment as defined by [RFC3986] section 3.
211+ '
212+ ' If the absolute request URL is not available to the Service Provider (it is always available to the Consumer),
213+ ' it can be constructed by combining the scheme being used, the HTTP Host header, and the relative HTTP request URL.
214+ ' If the Host header is not available, the Service Provider SHOULD use the host name communicated to the Consumer in the documentation or other means.
215+ '
216+ ' The Service Provider SHOULD document the form of URL used in the Signature Base String to avoid ambiguity due to URL normalization.
217+ ' Unless specified, URL scheme and authority MUST be lowercase and include the port number; http default port 80 and https default port 443 MUST be excluded.
218+
219+ Dim Parts As Dictionary
220+ Set Parts = RestHelpers.UrlParts(Request.FullUrl(Client.BaseUrl))
221+
222+ ' Url scheme and authority MUST be lowercase
223+ RequestUrl = LCase(Parts("Protocol" ) & "//" & Parts("Hostname" ))
224+
225+ ' Include port (80 and 443 MUST be excluded)
226+ If Parts("Port" ) <> 80 And Parts("Port" ) <> 443 Then
227+ RequestUrl = RequestUrl & ":" & Parts("Port" )
189228 End If
190229
191- CreateBaseString = Request.MethodName() & "&" & URLEncode(Request.FormattedResource()) & "&" & URLEncode(base)
230+ ' Include path
231+ RequestUrl = RequestUrl + Parts("Uri" )
232+
233+ ' MUST exclude query and fragment
234+ End Function
235+
236+ Public Function RequestParameters (Client As RestClient , Request As RestRequest ) As String
237+ ' TODO Sort parameters by key then value
238+
239+ Dim Parts As Dictionary
240+ Set Parts = RestHelpers.UrlParts(Request.FullUrl(Client.BaseUrl))
241+
242+ RequestParameters = RestHelpers.UrlDecode(Replace(Parts("Querystring" ), "?" , "" ))
192243End Function
193244
194- Private Function CreateSigningKey () As String
245+ Public Function CreateSigningKey () As String
195246 CreateSigningKey = Me.ConsumerSecret & "&" & Me.TokenSecret
196247End Function
197248
198- Private Function CreateSignature (base As String , signingKey As String ) As String
199- CreateSignature = Base64_HMACSHA1(base, signingKey)
249+ Public Function CreateSignature (base As String , signingKey As String ) As String
250+ CreateSignature = RestHelpers. Base64_HMACSHA1(base, signingKey)
200251End Function
201252
202- Private Function CreateTimestamp () As String
253+ Public Function CreateTimestamp () As String
203254 CreateTimestamp = CStr(DateDiff("s" , #1/1/1970# , GetGMT()))
204255End Function
205256
0 commit comments