Skip to content

Commit 1b6e118

Browse files
committed
Merge pull request #37 from timhall/oauth1-updates
Oauth1 updates
2 parents 869c430 + 50fcb2b commit 1b6e118

17 files changed

+472
-219
lines changed

.gitignore

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,10 @@
11
# node_modules should be installed with npm install
22
specs/node_modules
33

4+
# Ignore generated site files
5+
_site
6+
_cache
7+
48
# Ignore temporary Excel files
59
*/~$*
610

authenticators/GoogleAuthenticator.cls

Lines changed: 13 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -60,7 +60,7 @@ Private pToken As String
6060

6161
Public ClientId As String
6262
Public ClientSecret As String
63-
Public ApiKey As String
63+
Public APIKey As String
6464
Public Scopes As Dictionary
6565
Public LoginByDefault As Boolean
6666

@@ -91,7 +91,7 @@ Public Property Get LoginUrl() As String
9191
If Not Me.Scopes Is Nothing Then
9292
Dim Scope As Variant
9393
Dim UrlParts As Dictionary
94-
For Each Scope In Me.Scopes.keys()
94+
For Each Scope In Me.Scopes.Keys()
9595
If Me.Scopes(Scope) Then
9696
Set UrlParts = RestHelpers.UrlParts(CStr(Scope))
9797
If UrlParts("Protocol") = "" Then
@@ -116,7 +116,7 @@ End Property
116116
Public Property Get Token() As String
117117
On Error GoTo CleanUp
118118

119-
If pToken = "" And Me.ApiKey = "" Then
119+
If pToken = "" And Me.APIKey = "" Then
120120
Dim Client As New RestClient
121121
Dim Response As RestResponse
122122

@@ -168,7 +168,7 @@ End Sub
168168
' Login to Google
169169
' --------------------------------------------- '
170170

171-
Public Sub Login(Optional ApiKey As String = "")
171+
Public Sub Login(Optional APIKey As String = "")
172172
On Error GoTo CleanUp
173173

174174
Dim IE As Object
@@ -177,9 +177,9 @@ Public Sub Login(Optional ApiKey As String = "")
177177
Dim Code As String
178178

179179
Completed = True
180-
If ApiKey <> "" Then
181-
Me.ApiKey = ApiKey
182-
ElseIf Me.ApiKey = "" Then
180+
If APIKey <> "" Then
181+
Me.APIKey = APIKey
182+
ElseIf Me.APIKey = "" Then
183183
Completed = False
184184
Set IE = CreateObject("InternetExplorer.Application")
185185
With IE
@@ -232,7 +232,7 @@ End Sub
232232
' --------------------------------------------- '
233233

234234
Public Sub Logout()
235-
Me.ApiKey = ""
235+
Me.APIKey = ""
236236
Me.AuthorizationCode = ""
237237
Me.Token = ""
238238
End Sub
@@ -303,8 +303,8 @@ End Sub
303303
' --------------------------------------------- '
304304

305305
Private Sub IAuthenticator_BeforeExecute(ByVal Client As RestClient, ByRef Request As RestRequest)
306-
If Me.ApiKey <> "" Then
307-
Request.AddQuerystringParam "key", Me.ApiKey
306+
If Me.APIKey <> "" Then
307+
Request.AddQuerystringParam "key", Me.APIKey
308308
Else
309309
Request.AddHeader "Authorization", "Bearer " & Me.Token
310310
End If
@@ -376,7 +376,7 @@ Private Function OAuthIsDenied(IE As Object) As Boolean
376376

377377
Dim Element As Object
378378
For Each Element In Document.Body.all
379-
If Element.nodeName = "P" And Element.Id = "access_denied" Then
379+
If Element.nodeName = "P" And Element.ID = "access_denied" Then
380380
OAuthIsDenied = True
381381
Exit Function
382382
End If
@@ -420,9 +420,9 @@ Private Function OAuthExtractError(IE As Object) As String
420420

421421
Set Document = IE.Document
422422
For Each Element In Document.Body.all
423-
If Element.Id = "errorCode" Then
423+
If Element.ID = "errorCode" Then
424424
OAuthExtractError = Element.innerHTML
425-
ElseIf Element.Id = "errorDescription" Then
425+
ElseIf Element.ID = "errorDescription" Then
426426
OAuthExtractError = OAuthExtractError & ", " & Element.innerHTML
427427
Exit Function
428428
End If

authenticators/OAuth1Authenticator.cls

Lines changed: 81 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,8 @@ Public ConsumerSecret As String
6868
Public Token As String
6969
Public TokenSecret As String
7070
Public 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

107109
Private 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)
110112
End 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)
137139
End 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
171183
End 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"), "?", ""))
192243
End Function
193244

194-
Private Function CreateSigningKey() As String
245+
Public Function CreateSigningKey() As String
195246
CreateSigningKey = Me.ConsumerSecret & "&" & Me.TokenSecret
196247
End 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)
200251
End Function
201252

202-
Private Function CreateTimestamp() As String
253+
Public Function CreateTimestamp() As String
203254
CreateTimestamp = CStr(DateDiff("s", #1/1/1970#, GetGMT()))
204255
End Function
205256

build/export-specs.vbs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -30,7 +30,8 @@ Modules = Array(_
3030
"RestRequestSpecs.bas", _
3131
"RestHelpersSpecs.bas", _
3232
"DigestAuthenticatorSpecs.bas", _
33-
"GoogleAuthenticatorSpecs.bas" _
33+
"GoogleAuthenticatorSpecs.bas", _
34+
"OAuth1AuthenticatorSpecs.bas" _
3435
)
3536

3637
If WBPath <> "" And OutputPath <> "" Then

build/export.vbs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -29,7 +29,7 @@ Modules = Array(_
2929
"IAuthenticator.cls", _
3030
"RestClient.cls", _
3131
"RestRequest.cls", _
32-
"RestResponse.cls", _
32+
"RestResponse.cls" _
3333
)
3434

3535
If WBPath <> "" And OutputPath <> "" Then

credentials - example.txt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,4 +25,10 @@ Salesforce
2525
Facebook
2626
# Url: developers.facebook.com
2727
- id: Your App Id
28-
- secret: Your App Secret
28+
- secret: Your App Secret
29+
30+
LinkedIn
31+
- api_key: Your API key
32+
- api_secret: Your API secret
33+
- user_token: Your user token
34+
- user_secret: Your user secret

examples/Excel-REST - Example.xlsm

-25.9 KB
Binary file not shown.

examples/linkedin/LinkedIn.bas

Lines changed: 82 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,82 @@
1+
Attribute VB_Name = "LinkedIn"
2+
Private pLinkedInClient As RestClient
3+
Private pLinkedInAPIKey As String
4+
Private pLinkedInAPISecret As String
5+
Private pLinkedInUserToken As String
6+
Private pLinkedInUserSecret As String
7+
8+
Private Property Get LinkedInAPIKey() As String
9+
If pLinkedInAPIKey = "" Then
10+
If Credentials.Loaded Then
11+
pLinkedInAPIKey = Credentials.Values("LinkedIn")("api_key")
12+
Else
13+
pLinkedInAPIKey = InputBox("Please Enter LinkedIn API Key")
14+
End If
15+
End If
16+
17+
LinkedInAPIKey = pLinkedInAPIKey
18+
End Property
19+
20+
Private Property Get LinkedInAPISecret() As String
21+
If pLinkedInAPISecret = "" Then
22+
If Credentials.Loaded Then
23+
pLinkedInAPISecret = Credentials.Values("LinkedIn")("api_secret")
24+
Else
25+
pLinkedInAPISecret = InputBox("Please Enter LinkedIn API Secret")
26+
End If
27+
End If
28+
29+
LinkedInAPISecret = pLinkedInAPISecret
30+
End Property
31+
32+
Private Property Get LinkedInUserToken() As String
33+
If pLinkedInUserToken = "" Then
34+
If Credentials.Loaded Then
35+
pLinkedInUserToken = Credentials.Values("LinkedIn")("user_token")
36+
Else
37+
pLinkedInUserToken = InputBox("Please Enter LinkedIn User Token")
38+
End If
39+
End If
40+
41+
LinkedInUserToken = pLinkedInUserToken
42+
End Property
43+
44+
Private Property Get LinkedInUserSecret() As String
45+
If pLinkedInUserSecret = "" Then
46+
If Credentials.Loaded Then
47+
pLinkedInUserSecret = Credentials.Values("LinkedIn")("user_secret")
48+
Else
49+
pLinkedInUserSecret = InputBox("Please Enter LinkedIn User Secret")
50+
End If
51+
End If
52+
53+
LinkedInUserSecret = pLinkedInUserSecret
54+
End Property
55+
56+
Private Property Get LinkedInClient() As RestClient
57+
If pLinkedInClient Is Nothing Then
58+
Set pLinkedInClient = New RestClient
59+
pLinkedInClient.BaseUrl = "http://api.linkedin.com/v1/"
60+
61+
Dim Auth As New OAuth1Authenticator
62+
Auth.Setup _
63+
ConsumerKey:=LinkedInAPIKey, _
64+
ConsumerSecret:=LinkedInAPISecret, _
65+
Token:=LinkedInUserToken, _
66+
TokenSecret:=LinkedInUserSecret
67+
Set pLinkedInClient.Authenticator = Auth
68+
End If
69+
70+
Set LinkedInClient = pLinkedInClient
71+
End Property
72+
73+
Public Function GetProfile(Optional Callback As String = "") As RestResponse
74+
Dim Request As New RestRequest
75+
Request.Resource = "people/~?format={format}"
76+
77+
If Callback <> "" Then
78+
LinkedInClient.ExecuteAsync Request, Callback
79+
Else
80+
Set GetProfile = LinkedInClient.Execute(Request)
81+
End If
82+
End Function

0 commit comments

Comments
 (0)