-
Notifications
You must be signed in to change notification settings - Fork 3
Expand file tree
/
Copy pathWinSPAuthenticator.cls
More file actions
278 lines (265 loc) · 11.7 KB
/
Copy pathWinSPAuthenticator.cls
File metadata and controls
278 lines (265 loc) · 11.7 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
VERSION 1.0 CLASS
BEGIN
MultiUse = -1 'True
END
Attribute VB_Name = "WinSPAuthenticator"
Attribute VB_GlobalNameSpace = False
Attribute VB_Creatable = False
Attribute VB_PredeclaredId = False
Attribute VB_Exposed = False
''
' Sharepoint with windows auth
' (c) Adam Queler
'
' Description
' With a big assist from https://blogs.msdn.microsoft.com/omarv/2012/11/15/developing-windows-8-store-apps-for-sharepoint-online-with-sso-single-sign-on/
' Requires WindowsAuthenticator
' Windows only
' @class
' @implements IWebAuthenticator v4.*
' @author Adam Queler
' @license MIT (http://www.opensource.org/licenses/mit-license.php)
'' ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ '
Implements IWebAuthenticator
Option Explicit
' ============================================= '
' Public Methods
' ============================================= '
''
' Setup authenticator
''
'IOMode Constants
Private originalRq As WebRequest
' sharepoint likes this UA
Const SPO_UA As String = "Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; WOW64; Trident/5.0)"
'IOMode Constants
Const ForAppending As Integer = 8
Const ForReading As Integer = 1
Const ForWriting As Integer = 2
Dim spSite As String
Const rtfa As String = "rtFa"
Const fedAuth As String = "FedAuth"
Private cookieRtfa As String
Private cookieFedAuth As String
Public GotCookies As Boolean
Public Sub Setup(Optional altSiteURL As String = "")
'only need to run this is you are sending something other than a pathless host of the base url for authentication
' i.e. if base url is host.sharepoint.com/somesite/path, default will use host.sharepoint.com for authentication purposes
spSite = altSiteURL
End Sub
Private Sub Class_Initialize()
GotCookies = False
End Sub
''
' Hook for taking action before a request is executed
'
' @param {WebClient} Client The client that is about to execute the request
' @param in|out {WebRequest} Request The request about to be executed
''
Private Sub IWebAuthenticator_BeforeExecute(ByVal client As WebClient, ByRef request As WebRequest)
' e.g Add headers, cookies, etc.
loadCookies client
If cookieFedAuth = "" Or cookieRtfa = "" Then 'Or headerDigest = "" Then
WebHelpers.LogDebug "cookie blank getting new ones"
getCookiesFromSPO client
Else
LogDebug "probably loaded cookies", "WinSPAuthenticator"
End If
Set originalRq = request
Set request = originalRq.Clone
If IsEmpty(FindInKeyValues(request.Cookies, rtfa)) Then
request.AddCookie rtfa, cookieRtfa
End If
If IsEmpty(FindInKeyValues(request.Cookies, fedAuth)) Then
request.AddCookie fedAuth, cookieFedAuth
End If
End Sub
''
' Hook for taking action after request has been executed
'
' @param {WebClient} Client The client that executed request
' @param {WebRequest} Request The request that was just executed
' @param in|out {WebResponse} Response to request
''
Private Sub IWebAuthenticator_AfterExecute(ByVal client As WebClient, ByVal request As WebRequest, ByRef Response As WebResponse)
' e.g. Handle 401 Unauthorized or other issues
If Response.StatusCode = FORBIDDEN Then 'refresh
LogDebug "forbidden"
If Not GotCookies Then
LogDebug "but we haven't gotten new cookies yet in this rq"
getCookiesFromSPO client
Set request = originalRq
request.AddCookie rtfa, cookieRtfa
request.AddCookie fedAuth, cookieFedAuth
Set Response = client.Execute(request)
Set request = originalRq
End If
End If
End Sub
''
' Hook for updating http before send
'
' @param {WebClient} Client
' @param {WebRequest} Request
' @param in|out {WinHttpRequest} Http
''
Private Sub IWebAuthenticator_PrepareHttp(ByVal client As WebClient, ByVal request As WebRequest, ByRef Http As Object)
' e.g. Update option, headers, etc.
End Sub
''
' Hook for updating cURL before send
'
' @param {WebClient} Client
' @param {WebRequest} Request
' @param in|out {String} Curl
''
Private Sub IWebAuthenticator_PrepareCurl(ByVal client As WebClient, ByVal request As WebRequest, ByRef Curl As String)
' e.g. Add flags to cURL
End Sub
Private Function hash(s As String)
hash = Left(MD5(s, "Base64"), 22)
End Function
Public Sub loadCookies(client As WebClient)
Dim fso As Object ' As FileSystemObject
Set fso = CreateObject("Scripting.FileSystemObject") 'New Scripting.FileSystemObject
Dim fp As String
fp = fso.BuildPath(Environ$("APPDATA"), hash("WinSPAuthenticator" & client.BaseUrl))
If fso.FolderExists(fp) Then
Dim pRtfa As String: pRtfa = fso.BuildPath(fp, hash(rtfa))
Dim pFedAuth As String: pFedAuth = fso.BuildPath(fp, hash(fedAuth))
'Dim pDigest As String: pDigest = fso.BuildPath(fp, hash("digest"))
If fso.fileexists(pRtfa) And fso.fileexists(pFedAuth) Then 'And fso.fileexists(pDigest) Then
Dim ts ' As Scripting.TextStream
Set ts = fso.OpenTextFile(pRtfa)
cookieRtfa = (ts.ReadAll)
ts.Close
Set ts = fso.OpenTextFile(pFedAuth)
cookieFedAuth = (ts.ReadAll)
ts.Close
End If
End If
End Sub
Public Sub saveCookies(client As WebClient)
Dim fso ' As FileSystemObject
Set fso = CreateObject("Scripting.FileSystemObject") 'New Scripting.FileSystemObject
Dim fp As String
fp = fso.BuildPath(Environ$("APPDATA"), hash("WinSPAuthenticator" & client.BaseUrl))
If Not fso.FolderExists(fp) Then
fso.CreateFolder fp
End If
Dim ts As Object 'Scripting.TextStream
Dim pRtfa As String: pRtfa = fso.BuildPath(fp, hash(rtfa))
Dim pFedAuth As String: pFedAuth = fso.BuildPath(fp, hash(fedAuth))
Set ts = fso.OpenTextFile(pRtfa, ForWriting, True)
ts.write cookieRtfa
ts.Close
Set ts = Nothing
Set ts = fso.OpenTextFile(pFedAuth, ForWriting, True)
ts.write cookieFedAuth
ts.Close
End Sub
Public Sub getCookiesFromSPO(client As WebClient)
Call WebHelpers.LogDebug("getting cookies", "WinSPAuthenticator")
Const spowssigninUri As String = "_forms/default.aspx?wa=wsignin1.0"
Const msoStsUrl As String = "https://login.microsoftonline.com/extSTS.srf"
Dim realmClient As New WebClient
Dim wa As New WindowsAuthenticator
Set realmClient.Authenticator = wa
realmClient.BaseUrl = "https://login.microsoftonline.com/"
If spSite = "" Then
Dim parts As Dictionary
Set parts = GetUrlParts(client.BaseUrl)
spSite = Replace(client.BaseUrl, parts("Path"), "")
spSite = Replace(spSite, parts("Querystring"), "")
spSite = Replace(spSite, parts("Hash"), "")
End If
Dim rx As New WebRequest
rx.UserAgent = SPO_UA
rx.Resource = "GetUserRealm.srf"
rx.ResponseFormat = json
rx.RequestFormat = FormUrlEncoded
rx.AddBodyParameter "login", Environ("username") & "@" & Environ("userdnsdomain") ' might be different for other people
rx.Method = Httppost
Dim resp As WebResponse
Set resp = realmClient.Execute(rx)
Dim authURL As String
authURL = resp.Data("AuthURL"): realmClient.BaseUrl = authURL
Dim authRx As New WebRequest: Set authRx = New WebRequest
With authRx
.Format = PlainText
.UserAgent = SPO_UA
.Accept = "text/html; charset=utf-8"
.Method = HttpGet
.ContentType = "text/html; charset=utf-8"
.Resource = ""
.Body = ""
End With
Set resp = realmClient.Execute(authRx)
Dim dom As Object: Set dom = CreateObject("MSXML2.DOMDocument.6.0") 'New MSXML2.DOMDocument60
dom.Async = False
dom.LoadXML resp.Content
dom.LoadXML dom.SelectSingleNode("//form/input[@name='wresult']/@value").NodeValue
Dim assertion As String
dom.setProperty "SelectionNamespaces", "xmlns:saml=""urn:oasis:names:tc:SAML:1.0:assertion"""
assertion = dom.SelectSingleNode("//saml:Assertion").Xml
Dim env As String
env = ParameterizeSoapRequestTokenMsgWithAssertion(spSite, assertion, msoStsUrl)
realmClient.BaseUrl = msoStsUrl
Set authRx = New WebRequest
With authRx
.Format = PlainText
.UserAgent = SPO_UA
.Accept = "application/soap+xml; charset=utf-8"
.Method = HttpGet
.ContentType = .Accept
.Resource = ""
.Body = env
End With
Set resp = realmClient.Execute(authRx)
Dim secToken As String, secTokenExp
dom.LoadXML resp.Content
dom.setProperty "SelectionNamespaces", "xmlns:wsa='http://www.w3.org/2005/08/addressing' xmlns:wsse='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd' xmlns:wsu='http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd' xmlns:wsp='http://schemas.xmlsoap.org/ws/2004/09/policy' xmlns:wst='http://schemas.xmlsoap.org/ws/2005/02/trust' xmlns:S='http://www.w3.org/2003/05/soap-envelope'"
secToken = dom.SelectSingleNode("//wsse:BinarySecurityToken").Text
secTokenExp = ParseIso(dom.SelectSingleNode("//wsu:Expires").Text)
realmClient.BaseUrl = spSite
Set rx = New WebRequest
rx.Resource = spowssigninUri
rx.Method = Httppost
rx.Body = secToken
rx.Format = FormUrlEncoded
Set resp = realmClient.Execute(rx)
cookieRtfa = FindInKeyValues(resp.Cookies, rtfa)
cookieFedAuth = FindInKeyValues(resp.Cookies, fedAuth)
saveCookies client
GotCookies = True
End Sub
Function ParameterizeSoapRequestTokenMsgWithAssertion(spSite As String, samlAssertion As String, stsUrl As String) As String
Dim samlRTString As String
samlRTString = "<s:Envelope xmlns:s=""http://www.w3.org/2003/05/soap-envelope"" xmlns:a=""http://www.w3.org/2005/08/addressing"" xmlns:u=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd"">" & _
" <s:Header>" & _
" <a:Action s:mustUnderstand=""1"">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action>" & _
" <a:ReplyTo>" & _
" <a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address>" & _
" </a:ReplyTo>" & _
" <a:To s:mustUnderstand=""1"">[toUrl]</a:To>" & _
" <o:Security s:mustUnderstand=""1"" xmlns:o=""http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd"">[assertion]" & _
" </o:Security>" & _
" </s:Header>" & _
" <s:Body>" & _
"<t:RequestSecurityToken xmlns:t=""http://schemas.xmlsoap.org/ws/2005/02/trust"">" & _
" <wsp:AppliesTo xmlns:wsp=""http://schemas.xmlsoap.org/ws/2004/09/policy"">" & _
" <a:EndpointReference>" & _
" <a:Address>[url]</a:Address>" & _
" </a:EndpointReference>" & _
" </wsp:AppliesTo>" & _
" <t:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</t:KeyType>" & _
" <t:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</t:RequestType>" & _
" <t:TokenType>urn:oasis:names:tc:SAML:1.0:assertion</t:TokenType>" & _
" </t:RequestSecurityToken>" & _
" </s:Body>" & _
"</s:Envelope>"
samlRTString = Replace(samlRTString, "[assertion]", samlAssertion)
samlRTString = Replace(samlRTString, "[url]", spSite)
samlRTString = Replace(samlRTString, "[toUrl]", stsUrl)
ParameterizeSoapRequestTokenMsgWithAssertion = samlRTString
End Function