| | 0

Automatischer Login für OneDrive mit Benutzernamen und Passwort – Machbarkeitstest

Im Februar schrieb ich ein PowerShell-Modul für die PowerShellGallery. Das Modul stellt Befehle für die Interaktion mit OneDrive (https://www.sepago.com/blog/2016/02/21/Use-PowerShell-Module-OneDrive-from-PowerShellGallery-command-line) zur Verfügung. Der Befehl Get-ODAuthentication authentifiziert Benutzer auf OneDrive mit Hilfe der Microsoft Live-Website. Auf dieser gibt der Benutzer seine Anmeldeinformationen ein und wird authentifiziert. Soweit ich weiß, ist es derzeit nicht möglich, Anmeldungen über ein Skript unbeaufsichtigt durchzuführen.

In diesem Blog biete ich eine Möglichkeit, den Login automatisch durchzuführen. Aber Vorsicht: Diese Methode verwendet ein Internet Explorer-com-Objekt, um die Anmeldung durchzuführen. Der unbeaufsichtigte Login kann fehlschlagen, wenn Microsoft etwas an den Seiten ändert. Ich gehe auch davon aus, dass dieser Weg weder von Microsoft supported wird (das ist sicher) noch gewünscht ist. Ich habe diese Herausforderung zum Trainieren von PowerShell und IE Remoting genutzt. Die ist teilweise sehr gruselig (IE 11 auf Win10 und Server handeln z.B. das Eventhandling verschieden etc..).

Getestet habe ich das Skript mit den folgenden Betriebssystemen:

  • Windows 10: Englisch, Deutsch, IE11
  • Windows Server 2012 R2: Englisch, Deutsch, IE11

Das Skript funktioniert folgendermaßen:

  1. Öffnen einer einzelnen IE Instanz zum Abmelden eines evtl. angemeldeten Benutzers
  2. Navigation zur Login-Seite
  3. Ausfüllen des Formulars (Benutzername und Passwort)
  4. Senden des Change Events an die Felder
  5. Klicken zum Senden
  6. Zugriff bestätigen
  7. Das Zugriffstoken extrahieren
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")
$ieLogout.Navigate("https://login.live.com/logout.srf")
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