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 sites New-Item -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\EscDomains" -Name "live.com" –Force | Out-Null Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\EscDomains\live.com" -Name "https" -Value 2 New-Item -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\EscDomains" -Name "gfx.ms" –Force | Out-Null Set-ItemProperty -Path "HKCU:\Software\Microsoft\Windows\CurrentVersion\Internet Settings\ZoneMap\EscDomains\gfx.ms" -Name "https" -Value 2 #Enable protected mode for trusted sites Set-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 = $true write-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 = $false try { $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= $Password write-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 PSObject ForEach ( $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 |