-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathSPOnline-Get-Cookie.ps1
311 lines (256 loc) · 15.7 KB
/
SPOnline-Get-Cookie.ps1
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
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
<#
.Synopsis
Retrieve SPOIDCR cookie for SharePoint Online.
.Description
Authenticates to the sts and retrieves the SPOIDCR cookie for SharePoint Online.
Will use the custom IDP if one has been setup.
Optionally, can use integrated credentials (when integrated is set to true) with ADFS using the windowsmixed endpoint.
Results are formattable as XML, JSON, KEYVALUE, and by line.
Makes global variables avaiable at the end of the run.
$spoidcrl contains the SPOIDCRL cookie
.Example
The following returns the SPOIDCRL cookie value provided a username and password.
PS> .\spoidcrl.ps1 -url https://contoso.sharepoint.com -username [email protected] -password ABCDEFG
.Example
The following returns the SPOIDCRL cookie value using integrated windows credentials. Applies only to ADFS.
PS> .\spoidcrl.ps1 -url https://contoso.sharepoint.com/sites/site1 -integrated
.Example
The following saves the SPOIDCRL cookie value using integrated windows credentials. Applies only to ADFS.
PS> .\spoidcrl.ps1 -url https://contoso.sharepoint.com/sites/site1 -integrated -format "XML" | Out-File "c:\temp\spoidcr.txt"
.PARAMETER url
Tenant url (e.g. contoso.sharepoint.com)
.PARAMETER username
The username to login with. (e.g. [email protected] or [email protected])
.PARAMETER password
The password to login with.
.PARAMETER integrated
Whether to use integrated credentials (user running PowerShell) instead of explicit credentials.
Needs to be supported by ADFS.
.PARAMETER format
How to format the output. Options include: XML, JSON, KEYVALUE
#>
[CmdletBinding()]
Param(
[Parameter(Mandatory=$true)]
[string]$url,
[Parameter(Mandatory=$false)]
[string]$username,
[Parameter(Mandatory=$false)]
[string]$password,
[Parameter(Mandatory=$false)]
[switch]$integrated = $false,
[Parameter(Mandatory=$false)]
[string]$format
)
$statusText = New-Object System.Text.StringBuilder
function log($info)
{
if([string]::IsNullOrEmpty($info))
{
$info = ""
}
[void]$statusText.AppendLine($info)
}
try
{
if (![uri]::IsWellFormedUriString($url, [UriKind]::Absolute))
{
throw "Parameter 'url' is not a valid URI."
}
else
{
$uri = [uri]::new($url)
$tenant = $uri.Authority
}
if ($tenant.EndsWith("sharepoint.com", [System.StringComparison]::OrdinalIgnoreCase))
{
$msoDomain = "sharepoint.com"
}
else
{
$msoDomain = $tenant
}
if ($integrated.ToBool())
{
[System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices") | out-null
[System.Reflection.Assembly]::LoadWithPartialName("System.DirectoryServices.AccountManagement") | out-null
$username = [System.DirectoryServices.AccountManagement.UserPrincipal]::Current.UserPrincipalName
}
elseif ([string]::IsNullOrWhiteSpace($username) -or [string]::IsNullOrWhiteSpace($password))
{
$credential = Get-Credential -UserName $username -Message "Enter credentials"
$username = $credential.UserName
$password = $credential.GetNetworkCredential().Password
}
$contextInfoUrl = $url.TrimEnd('/') + "/_api/contextinfo"
$getRealmUrl = "https://login.microsoftonline.com/GetUserRealm.srf"
$realm = "urn:federation:MicrosoftOnline"
$msoStsAuthUrl = "https://login.microsoftonline.com/rst2.srf"
$idcrlEndpoint = "https://$tenant/_vti_bin/idcrl.svc/"
$username = [System.Security.SecurityElement]::Escape($username)
$password = [System.Security.SecurityElement]::Escape($password)
# Custom STS integrated authentication envelope format index info
# 0: message id - unique guid
# 1: custom STS auth url
# 2: realm
$customStsSamlIntegratedRequestFormat = "<?xml version=`"1.0`" encoding=`"UTF-8`"?><s:Envelope xmlns:s=`"http://www.w3.org/2003/05/soap-envelope`" xmlns:a=`"http://www.w3.org/2005/08/addressing`"><s:Header><a:Action s:mustUnderstand=`"1`">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</a:Action><a:MessageID>urn:uuid:{0}</a:MessageID><a:ReplyTo><a:Address>http://www.w3.org/2005/08/addressing/anonymous</a:Address></a:ReplyTo><a:To s:mustUnderstand=`"1`">{1}</a:To></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`"><wsa:EndpointReference xmlns:wsa=`"http://www.w3.org/2005/08/addressing`"><wsa:Address>{2}</wsa:Address></wsa: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:RequestSecurityToken></s:Body></s:Envelope>";
# custom STS envelope format index info
# {0}: ADFS url, such as https://corp.sts.contoso.com/adfs/services/trust/2005/usernamemixed, its value comes from the response in GetUserRealm request.
# {1}: MessageId, it could be an arbitrary guid
# {2}: UserLogin, such as [email protected]
# {3}: Password
# {4}: Created datetime in UTC, such as 2012-11-16T23:24:52Z
# {5}: Expires datetime in UTC, such as 2012-11-16T23:34:52Z
# {6}: tokenIssuerUri, such as urn:federation:MicrosoftOnline, or urn:federation:MicrosoftOnline-int
$customStsSamlRequestFormat = "<?xml version=`"1.0`" encoding=`"UTF-8`"?><s:Envelope xmlns:s=`"http://www.w3.org/2003/05/soap-envelope`" xmlns:wsse=`"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd`" xmlns:saml=`"urn:oasis:names:tc:SAML:1.0:assertion`" xmlns:wsp=`"http://schemas.xmlsoap.org/ws/2004/09/policy`" xmlns:wsu=`"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd`" xmlns:wsa=`"http://www.w3.org/2005/08/addressing`" xmlns:wssc=`"http://schemas.xmlsoap.org/ws/2005/02/sc`" xmlns:wst=`"http://schemas.xmlsoap.org/ws/2005/02/trust`"><s:Header><wsa:Action s:mustUnderstand=`"1`">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</wsa:Action><wsa:To s:mustUnderstand=`"1`">{0}</wsa:To><wsa:MessageID>{1}</wsa:MessageID><ps:AuthInfo xmlns:ps=`"http://schemas.microsoft.com/Passport/SoapServices/PPCRL`" Id=`"PPAuthInfo`"><ps:HostingApp>Managed IDCRL</ps:HostingApp><ps:BinaryVersion>6</ps:BinaryVersion><ps:UIVersion>1</ps:UIVersion><ps:Cookies></ps:Cookies><ps:RequestParams>AQAAAAIAAABsYwQAAAAxMDMz</ps:RequestParams></ps:AuthInfo><wsse:Security><wsse:UsernameToken wsu:Id=`"user`"><wsse:Username>{2}</wsse:Username><wsse:Password>{3}</wsse:Password></wsse:UsernameToken><wsu:Timestamp Id=`"Timestamp`"><wsu:Created>{4}</wsu:Created><wsu:Expires>{5}</wsu:Expires></wsu:Timestamp></wsse:Security></s:Header><s:Body><wst:RequestSecurityToken Id=`"RST0`"><wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType><wsp:AppliesTo><wsa:EndpointReference> <wsa:Address>{6}</wsa:Address></wsa:EndpointReference></wsp:AppliesTo><wst:KeyType>http://schemas.xmlsoap.org/ws/2005/05/identity/NoProofKey</wst:KeyType></wst:RequestSecurityToken></s:Body></s:Envelope>"
# mso envelope format index info (Used for custom STS + MSO authentication)
# 0: custom STS assertion
# 1: mso endpoint
$msoSamlRequestFormat = "<?xml version=`"1.0`" encoding=`"UTF-8`"?><S:Envelope xmlns:S=`"http://www.w3.org/2003/05/soap-envelope`" xmlns:wsse=`"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd`" xmlns:wsp=`"http://schemas.xmlsoap.org/ws/2004/09/policy`" xmlns:wsu=`"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd`" xmlns:wsa=`"http://www.w3.org/2005/08/addressing`" xmlns:wst=`"http://schemas.xmlsoap.org/ws/2005/02/trust`"><S:Header><wsa:Action S:mustUnderstand=`"1`">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</wsa:Action><wsa:To S:mustUnderstand=`"1`">https://login.microsoftonline.com/rst2.srf</wsa:To><ps:AuthInfo xmlns:ps=`"http://schemas.microsoft.com/LiveID/SoapServices/v1`" Id=`"PPAuthInfo`"><ps:BinaryVersion>5</ps:BinaryVersion><ps:HostingApp>Managed IDCRL</ps:HostingApp></ps:AuthInfo><wsse:Security>{0}</wsse:Security></S:Header><S:Body><wst:RequestSecurityToken xmlns:wst=`"http://schemas.xmlsoap.org/ws/2005/02/trust`" Id=`"RST0`"><wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType><wsp:AppliesTo><wsa:EndpointReference><wsa:Address>{1}</wsa:Address></wsa:EndpointReference></wsp:AppliesTo><wsp:PolicyReference URI=`"MBI`"></wsp:PolicyReference></wst:RequestSecurityToken></S:Body></S:Envelope>"
# mso envelope format index info (Used for MSO-only authentication)
# 0: mso endpoint
# 1: username
# 2: password
$msoSamlRequestFormat2 = "<?xml version=`"1.0`" encoding=`"UTF-8`"?><S:Envelope xmlns:S=`"http://www.w3.org/2003/05/soap-envelope`" xmlns:wsse=`"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-secext-1.0.xsd`" xmlns:wsp=`"http://schemas.xmlsoap.org/ws/2004/09/policy`" xmlns:wsu=`"http://docs.oasis-open.org/wss/2004/01/oasis-200401-wss-wssecurity-utility-1.0.xsd`" xmlns:wsa=`"http://www.w3.org/2005/08/addressing`" xmlns:wst=`"http://schemas.xmlsoap.org/ws/2005/02/trust`"><S:Header><wsa:Action S:mustUnderstand=`"1`">http://schemas.xmlsoap.org/ws/2005/02/trust/RST/Issue</wsa:Action><wsa:To S:mustUnderstand=`"1`">{0}</wsa:To><ps:AuthInfo xmlns:ps=`"http://schemas.microsoft.com/LiveID/SoapServices/v1`" Id=`"PPAuthInfo`"><ps:BinaryVersion>5</ps:BinaryVersion><ps:HostingApp>Managed IDCRL</ps:HostingApp></ps:AuthInfo><wsse:Security><wsse:UsernameToken wsu:Id=`"user`"><wsse:Username>{1}</wsse:Username><wsse:Password>{2}</wsse:Password></wsse:UsernameToken></wsse:Security></S:Header><S:Body><wst:RequestSecurityToken xmlns:wst=`"http://schemas.xmlsoap.org/ws/2005/02/trust`" Id=`"RST0`"><wst:RequestType>http://schemas.xmlsoap.org/ws/2005/02/trust/Issue</wst:RequestType><wsp:AppliesTo><wsa:EndpointReference><wsa:Address>sharepoint.com</wsa:Address></wsa:EndpointReference></wsp:AppliesTo><wsp:PolicyReference URI=`"MBI`"></wsp:PolicyReference></wst:RequestSecurityToken></S:Body></S:Envelope>"
function Invoke-HttpPost($endpoint, $body, $headers, $session)
{
log
log "Invoke-HttpPost"
log "url: $endpoint"
log "post body: $body"
$params = @{}
$params.Headers = $headers
$params.uri = $endpoint
$params.Body = $body
$params.Method = "POST"
$params.WebSession = $session
$response = Invoke-WebRequest @params -ContentType "application/soap+xml; charset=utf-8" -UseDefaultCredentials -UserAgent ([string]::Empty)
$content = $response.Content
return $content
}
# Get saml Assertion value from the custom STS
function Get-AssertionCustomSts($customStsAuthUrl)
{
log
log "Get-AssertionCustomSts"
$messageId = [guid]::NewGuid()
$created = [datetime]::UtcNow.ToString("o", [System.Globalization.CultureInfo]::InvariantCulture)
$expires = [datetime]::UtcNow.AddMinutes(10).ToString("o", [System.Globalization.CultureInfo]::InvariantCulture)
if ($integrated.ToBool())
{
log "integrated"
$customStsAuthUrl = $customStsAuthUrl.ToLowerInvariant().Replace("/usernamemixed","/windowstransport")
log $customStsAuthUrl
$requestSecurityToken = [string]::Format($customStsSamlIntegratedRequestFormat, $messageId, $customStsAuthUrl, $realm)
log $requestSecurityToken
}
else
{
log "not integrated"
$requestSecurityToken = [string]::Format($customStsSamlRequestFormat, $customStsAuthUrl, $messageId, $username, $password, $created, $expires, $realm)
log $requestSecurityToken
}
[xml]$customStsXml = Invoke-HttpPost $customStsAuthUrl $requestSecurityToken
return $customStsXml.Envelope.Body.RequestSecurityTokenResponse.RequestedSecurityToken.Assertion.OuterXml
}
function Get-BinarySecurityToken($customStsAssertion, $msoSamlRequestFormatTemp)
{
log
log "Get-BinarySecurityToken"
if ([string]::IsNullOrWhiteSpace($customStsAssertion))
{
log "using username and password"
$msoPostEnvelope = [string]::Format($msoSamlRequestFormatTemp, $msoDomain, $username, $password)
}
else
{
log "using custom sts assertion"
$msoPostEnvelope = [string]::Format($msoSamlRequestFormatTemp, $customStsAssertion, $msoDomain)
}
$msoContent = Invoke-HttpPost $msoStsAuthUrl $msoPostEnvelope
# Get binary security token using regex instead of [xml]
# Using regex to workaround PowerShell [xml] bug where hidden characters cause failure
[regex]$regex = "BinarySecurityToken Id=.*>([^<]+)<"
$match = $regex.Match($msoContent).Groups[1]
return $match.Value
}
function Get-SPOIDCRLCookie($msoBinarySecurityToken)
{
log
log "Get-SPOIDCRLCookie"
log
log "BinarySecurityToken: $msoBinarySecurityToken"
$binarySecurityTokenHeader = [string]::Format("BPOSIDCRL {0}", $msoBinarySecurityToken)
$params = @{uri=$idcrlEndpoint
Method="GET"
Headers = @{}
}
$params.Headers["Authorization"] = $binarySecurityTokenHeader
$params.Headers["X-IDCRL_ACCEPTED"] = "t"
$resonse = Invoke-WebRequest @params -UserAgent ([string]::Empty)
$cookie = $resonse.BaseResponse.Cookies["SPOIDCRL"]
return $cookie
}
# Retrieve the configured STS Auth Url (ADFS, PING, etc.)
function Get-UserRealmUrl($getRealmUrl, $username)
{
log
log "Get-UserRealmUrl"
log "url: $getRealmUrl"
log "username: $username"
[Net.ServicePointManager]::SecurityProtocol = [Net.SecurityProtocolType]::Tls12
$body = "login=$username&xml=1"
$response = Invoke-WebRequest -Uri $getRealmUrl -Method POST -Body $body -UserAgent ([string]::Empty)
return ([xml]$response.Content).RealmInfo.STSAuthURL
}
[System.Net.ServicePointManager]::Expect100Continue = $true
#1 Get custom STS auth url
$customStsAuthUrl = Get-UserRealmUrl $getRealmUrl $username
if ($customStsAuthUrl -eq $null)
{
#2 Get binary security token from the MSO STS by passing the SAML <Assertion> xml
$customStsAssertion = $null
$msoBinarySecurityToken = Get-BinarySecurityToken $customStsAssertion $msoSamlRequestFormat2
}
else
{
#2 Get SAML <Assertion> xml from custom STS
$customStsAssertion = Get-AssertionCustomSts $customStsAuthUrl
#3 Get binary security token from the MSO STS by passing the SAML <Assertion> xml
$msoBinarySecurityToken = Get-BinarySecurityToken $customStsAssertion $msoSamlRequestFormat
}
#3/4 Get SPOIDRCL cookie from SharePoint site by passing the binary security token
# Save cookie and reuse with multiple requests
$idcrl = $null
$idcrl = Get-SPOIDCRLCookie $msoBinarySecurityToken
if ([string]::IsNullOrEmpty($format))
{
$format = [string]::Empty
}
else
{
$format = $format.Trim().ToUpperInvariant()
}
$Global:spoidcrl = $idcrl
if ($format -eq "XML")
{
Write-Output ([string]::Format("<SPOIDCRL>{0}</SPOIDCRL>", $idcrl.Value))
}
elseif ($format -eq "JSON")
{
Write-Output ([string]::Format("{{`"SPOIDCRL`":`"{0}`"}}", $idcrl.Value))
}
elseif ($format.StartsWith("KEYVALUE") -or $format.StartsWith("NAMEVALUE"))
{
Write-Output ("SPOIDCRL:" + $idcrl.Value)
}
else
{
Write-Output $idcrl.Value
}
}
catch
{
log $error[0]
"ERROR:" + $statusText.ToString()
}