Unattended Login to OneDrive with Username and Password – Feasibility test
In February I wrote a PowerShell module for the PowerShellGallery which makes some commands available to interact with OneDrive (https://www.sepago.com/blog/2016/02/21/Use-PowerShell-Module-OneDrive-from-PowerShellGallery-command-line). The command Get-ODAuthentication authenticates the user to OneDrive by opening a browser window for the live login, so that the user can enter the credentials. As far as I know it’s currently not possible to login automatically via a script.
In this blog I offer a “dirty” solution to login automatically. But careful: This method uses an Internet Explorer com object to perform the login process. If Microsoft changes something, the automatic login can fail. I also assume that this way is (for sure) not supported by Microsoft nor desired. I used this challenge to train PowerShell and Internet Explorer remoting – which is very spooky (IE 11 on Win10 and Server handles events differently, etc.).
I tested the script with the following OS:
- Windows 10: Englisch, Deutsch, IE11
- Windows Server 2012 R2: Englisch, Deutsch, IE11
The script performs the following steps:
- Open a single IE instance and log a possible user out
- Go to the login page
- Fill out the form (username and password)
- Send “change” events to the fields
- Press submit
- Confirm access
- Extract the access token
|
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
|
$DebugPreference = "Continue"#Variables$LoginName="Marcel.Meurer@sepago.de"$Password="------------"$ClientID="00000000XXXXXXXXXXXXXXX"#Add *.live.com and *.gfx.ms to trusted sitesNew-Item -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\EscDomains" -Name "live.com" –Force | Out-NullSet-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\EscDomains\live.com" -Name "https" -Value 2New-Item -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\EscDomains" -Name "gfx.ms" –Force | Out-NullSet-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\EscDomains\gfx.ms" -Name "https" -Value 2#Enable protected mode for trusted sitesSet-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\Zones\2" -Name "2500" -Value 0$URIGetAccessToken="https://login.live.com/oauth20_authorize.srf?client_id="+$ClientID+"&scope=onedrive.readwrite&response_type=token&redirect_uri=https://login.live.com/oauth20_desktop.srf"#Create IE Com-object$ie = New-Object -com "InternetExplorer.Application.1"$ieLogout = New-Object -com "InternetExplorer.Application.1"$ie.visible = $false$ie.silent = $truewrite-debug ("Logout first")while($ieLogout.busy){Start-Sleep 1} $ieLogout.Quit()write-debug ("Go to login page")$ie.Navigate($URIGetAccessToken)while($ie.busy){Start-Sleep 1} #Check, if function createEvent exist$legacy=$falsetry {$ie.document.createEvent()| Out-Null} catch {$legacy=$true}if ($legacy) {write-debug ("Working in legacy mode")}if (!$legacy) { #Prepare event $evt = $ie.document.createEvent("HTMLEvents") $evt.initEvent("change",$true,$false)}write-debug ("Filling the formular")$ie.Document.IHTMLDocument3_getElementsByName("login").item().Value=$LoginName$ie.Document.IHTMLDocument3_getElementsByName("loginfmt").item().Value=$LoginName$ie.Document.IHTMLDocument3_getElementsByName("passwd").item().Value=$Passwordwrite-debug ("Sending change events to the fields")if ($legacy){ $ie.Document.IHTMLDocument3_getElementsByName("loginfmt").item().FireEvent("onChange") | Out-Null $ie.Document.IHTMLDocument3_getElementsByName("passwd").item().FireEvent("onChange") | Out-Null} else{ $ie.Document.IHTMLDocument3_getElementsByName("loginfmt").item().DispatchEvent($evt) | Out-Null $ie.Document.IHTMLDocument3_getElementsByName("passwd").item().DispatchEvent($evt) | Out-Null}write-debug ("Press submit")$ie.Document.IHTMLDocument3_getElementsByName("idSIButton9").item().click()while($ie.busy){Start-Sleep 1} write-debug ("Confirm access")$ie.Document.IHTMLDocument3_getElementsByName("ucaccept").item().click()while($ie.busy){Start-Sleep 1} $ReturnURI=($ie.LocationURL).ToString().Replace("#","&")$ie.Quit()$Authentication = New-Object PSObjectForEach ($element in $ReturnURI.Split("?")[1].Split("&")) { $Authentication | add-member Noteproperty $element.split("=")[0] $element.split("=")[1]}if ($Authentication.PSobject.Properties.name -match "expires_in"){ $Authentication | add-member Noteproperty "expires" ([System.DateTime]::Now.AddSeconds($Authentication.expires_in))}if (!($Authentication.PSobject.Properties.name -match "expires_in")){ write-warning("There is maybe an errror, because there is no access_token!")}$Authentication.scope=$Authentication.scope.Replace("%20"," ")$Authentication |