-
Notifications
You must be signed in to change notification settings - Fork 25
/
Get-IMAPAccessToken.ps1
237 lines (193 loc) · 11 KB
/
Get-IMAPAccessToken.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
<#
.SYNOPSIS
Allows IMAP OAuth testing with Office 365.
Please install MSAL.PS Powershell module as prerequisite.
https://github.com/DanijelkMSFT/ThisandThat/blob/main/Get-IMAPAccessToken.ps1
Refercing article with more insides
https://www.linkedin.com/pulse/start-using-oauth-office-365-popimap-authentication-danijel-klaric
https://techcommunity.microsoft.com/t5/exchange-team-blog/announcing-oauth-2-0-client-credentials-flow-support-for-pop-and/ba-p/3562963
https://techcommunity.microsoft.com/t5/exchange-team-blog/notes-from-the-field-using-oauth-for-activesync-and-pop-imap-in/ba-p/3606118
.DESCRIPTION
The function helps admins to test their IMAP OAuth Azure Application,
with Interactive user login und providing or the lately released client credential flow
using the right formatting for the XOAuth2 login string.
After successful logon, a simple IMAP folder listing is done, in addition it also allows to
test shared mailbox acccess for users if fullaccess has been provided.
Using Windows Powershell allows MSAL to cache the access+refresh token on disk for further executions for interactive login scenario.
It´s a simple proof of concept with no further error managment.
.PARAMETER tenantID
Specifies the target tenant.
.PARAMETER clientId
Specifies the ClientID/ApplicationID of the registered Azure AD Application with needed IMAP Graph permissions
.PARAMETER clientsecret
Specifies the ClientSecret configured in the Azure AD Application for client credential flow
.PARAMETER clientcertificate
Specifies the ClientCertificate Thumbprint configured in the Azure AD Application for client credential flow
.PARAMETER targeMailbox
Specifies the primary emailaddress of the targetmailbox which should be accessed by service principal which has fullaccess to for client credential flow
.PARAMETER redirectUri
Specifies the redirectUri of the registered Azure AD Application for authorization code flow (interactive flow)
.PARAMETER LoginHint
Specifies the Userprincipalname of the logging in user for authorization code flow (interactive flow)
.PARAMETER SharedMailbox (optinal)
Specifies the primary emailaddress of the Sharedmailbox logged in user has fullaccess to for authorization code flow (interactive flow)
.EXAMPLE
PS> .\Get-IMAPAccessToken.ps1 -tenantID "" -clientId "" -redirectUri "https://localhost" -LoginHint "user@contoso.com"
.EXAMPLE
PS> .\Get-IMAPAccessToken.ps1 -tenantID "" -clientId "" -redirectUri "https://localhost" -LoginHint "user@contoso.com" -SharedMailbox "SharedMailbox@contoso.com"
.EXAMPLE
PS> .\Get-IMAPAccessToken.ps1 -tenantID "" -clientId "" -redirectUri "https://localhost" -LoginHint "user@contoso.com" -Verbose
.EXAMPLE
PS> .\Get-IMAPAccessToken.ps1 -tenantID "" -clientId "" -clientsecret '' -targetMailbox "TargetMailbox@contoso.com"
.EXAMPLE
PS> .\Get-IMAPAccessToken.ps1 -tenantID "" -clientId "" -clientcertificate '' -targetMailbox "TargetMailbox@contoso.com"
.EXAMPLE
PS> .\Get-IMAPAccessToken.ps1 -tenantID "" -clientId "" -clientsecret '' -targetMailbox "TargetMailbox@contoso.com" -Verbose
#>
[CmdletBinding()]
param (
[Parameter(Mandatory = $true)][string]$tenantID,
[Parameter(Mandatory = $true)][String]$clientId,
[Parameter(Mandatory = $true,ParameterSetName="authorizationcode")][String]$redirectUri,
[Parameter(Mandatory = $true,ParameterSetName="authorizationcode")][String]$LoginHint,
[Parameter(Mandatory = $false,ParameterSetName="authorizationcode")][String]$SharedMailbox,
[Parameter(Mandatory = $true,ParameterSetName="clientcredentialsSecret")][String]$clientsecret,
[Parameter(Mandatory = $true,ParameterSetName="clientcredentialsCertificate")][String]$clientcertificate,
[Parameter(Mandatory = $true,ParameterSetName="clientcredentialsSecret")]
[Parameter(Mandatory = $true,ParameterSetName="clientcredentialsCertificate")]
[String]$targetMailbox
)
function Test-IMAPXOAuth2Connectivity {
# get Accesstoken via user authentication and store Access+Refreshtoken for next attempts
if ( $redirectUri ){
$MsftPowerShellClient = New-MsalClientApplication -ClientId $clientID -TenantId $tenantID -RedirectUri $redirectURI | Enable-MsalTokenCacheOnDisk -PassThru
try {
$authResult = $MsftPowerShellClient | Get-MsalToken -LoginHint $LoginHint -Scopes 'https://outlook.office365.com/.default'
}
catch {
Write-Host "[$((Get-Date).ToString("yyyy/MM/dd HH:mm:ss.fff"))] Ran into an exception while getting accesstoken user grant flow" -ForegroundColor Red
$_.Exception.Message
$_.FullyQualifiedErrorId
break
}
}
if ( $clientsecret ){
$SecuredclientSecret = ConvertTo-SecureString $clientsecret -AsPlainText -Force
$MsftPowerShellClient = New-MsalClientApplication -ClientId $clientID -TenantId $tenantID -ClientSecret $SecuredclientSecret
try {
$authResult = $MsftPowerShellClient | Get-MsalToken -Scopes 'https://outlook.office365.com/.default'
}
catch {
Write-Host "[$((Get-Date).ToString("yyyy/MM/dd HH:mm:ss.fff"))] Ran into an exception while getting accesstoken using clientsecret" -ForegroundColor Red
$_.Exception.Message
$_.FullyQualifiedErrorId
break
}
}
if ( $clientcertificate ){
$ClientCert = Get-ChildItem "cert:\currentuser\my\$clientcertificate"
$MsftPowerShellClient = New-MsalClientApplication -ClientId $clientID -TenantId $tenantID -ClientCertificate $ClientCert
try {
$authResult = $MsftPowerShellClient | Get-MsalToken -Scopes 'https://outlook.office365.com/.default'
}
catch {
Write-Host "[$((Get-Date).ToString("yyyy/MM/dd HH:mm:ss.fff"))] Ran into an exception while getting accesstoken using certificate" -ForegroundColor Red
$_.Exception.Message
$_.FullyQualifiedErrorId
break
}
}
$accessToken = $authResult.AccessToken
Write-Verbose "[$((Get-Date).ToString("yyyy/MM/dd HH:mm:ss.fff"))] Access Token -- $accessToken"
$userName = $authResult.Account.Username
# build authentication string with accesstoken and username like documented here
# https://docs.microsoft.com/en-us/exchange/client-developer/legacy-protocols/how-to-authenticate-an-imap-pop-smtp-application-by-using-oauth#authenticate-connection-requests
# in the case of client credential usage we need to add the target mailbox like shared mailbox access to the SASL string
if ( $targetMailbox) { $SharedMailbox = $targetMailbox }
if ( $SharedMailbox ) {
$b="user=" + $SharedMailbox + "$([char]0x01)auth=Bearer " + $accessToken + "$([char]0x01)$([char]0x01)"
Write-Host "[$((Get-Date).ToString("yyyy/MM/dd HH:mm:ss.fff"))] Accessing Sharedmailbox - $SharedMailbox - with Accesstoken of User $userName." -ForegroundColor DarkGreen
} else {
$b="user=" + $userName + "$([char]0x01)auth=Bearer " + $accessToken + "$([char]0x01)$([char]0x01)"
}
$Bytes = [System.Text.Encoding]::ASCII.GetBytes($b)
$POPIMAPLogin =[Convert]::ToBase64String($Bytes)
Write-Verbose "[$((Get-Date).ToString("yyyy/MM/dd HH:mm:ss.fff"))] SASL XOAUTH2 login string $POPIMAPLogin"
# connecting to Office 365 IMAP Service
Write-Host "[$((Get-Date).ToString("yyyy/MM/dd HH:mm:ss.fff"))] Connect to Office 365 IMAP Service." -ForegroundColor DarkGreen
$ComputerName = 'Outlook.office365.com'
$Port = '993'
try {
$TCPConnection = New-Object System.Net.Sockets.Tcpclient($($ComputerName), $Port)
$TCPStream = $TCPConnection.GetStream()
try {
$SSLStream = New-Object System.Net.Security.SslStream($TCPStream)
$SSLStream.ReadTimeout = 5000
$SSLStream.WriteTimeout = 5000
$CheckCertRevocationStatus = $true
$SSLStream.AuthenticateAsClient($ComputerName,$null,[System.Security.Authentication.SslProtocols]::Tls12,$CheckCertRevocationStatus)
}
catch {
Write-Host "[$((Get-Date).ToString("yyyy/MM/dd HH:mm:ss.fff"))] Ran into an exception while negotating SSL connection. Exiting." -ForegroundColor Red
$_.Exception.Message
break
}
}
catch {
Write-Host "[$((Get-Date).ToString("yyyy/MM/dd HH:mm:ss.fff"))] Ran into an exception while opening TCP connection. Exiting." -ForegroundColor Red
$_.Exception.Message
break
}
# continue if connection was successfully established
$SSLstreamReader = new-object System.IO.StreamReader($sslStream)
$SSLstreamWriter = new-object System.IO.StreamWriter($sslStream)
$SSLstreamWriter.AutoFlush = $true
$SSLstreamWriter.Newline = "`r`n"
$SSLstreamReader.ReadLine()
Write-Host "[$((Get-Date).ToString("yyyy/MM/dd HH:mm:ss.fff"))] Authenticate using XOAuth2." -ForegroundColor DarkGreen
# authenticate and check for results
$command = "A01 AUTHENTICATE XOAUTH2 {0}" -f $POPIMAPLogin
Write-Verbose "[$((Get-Date).ToString("yyyy/MM/dd HH:mm:ss.fff"))] Executing command -- $command"
$SSLstreamWriter.WriteLine($command)
#respose might take longer sometimes
while (!$ResponseStr ) {
try { $ResponseStr = $SSLstreamReader.ReadLine() } catch { }
}
if ( $ResponseStr -like "*OK AUTHENTICATE completed.")
{
$ResponseStr
Write-Host "[$((Get-Date).ToString("yyyy/MM/dd HH:mm:ss.fff"))] Getting mailbox folder list as authentication was successfull." -ForegroundColor DarkGreen
$command = 'A01 LIST "" *'
Write-Verbose "[$((Get-Date).ToString("yyyy/MM/dd HH:mm:ss.fff"))] Executing command -- $command"
$SSLstreamWriter.WriteLine($command)
$done = $false
$str = $null
while (!$done ) {
$str = $SSLstreamReader.ReadLine()
if ($str -like "* OK LIST completed.") { $str ; $done = $true }
elseif ($str -like "* BAD User is authenticated but not connected.") { $str; "Causing Error: IMAP protocol access to mailbox is disabled or permission not granted for client credential flow. Please enable IMAP protcol access or grant fullaccess to service principal."; $done = $true}
else { $str }
}
Write-Host "[$((Get-Date).ToString("yyyy/MM/dd HH:mm:ss.fff"))] Logout and cleanup sessions." -ForegroundColor DarkGreen
$command = 'A01 Logout'
Write-Verbose "[$((Get-Date).ToString("yyyy/MM/dd HH:mm:ss.fff"))] Executing command -- $command"
$SSLstreamWriter.WriteLine($command)
$SSLstreamReader.ReadLine()
} else {
Write-host "[$((Get-Date).ToString("yyyy/MM/dd HH:mm:ss.fff"))] ERROR during authentication $ResponseStr" -Foregroundcolor Red
}
# Session cleanup
if ($SSLStream) {
$SSLStream.Dispose()
}
if ($TCPStream) {
$TCPStream.Dispose()
}
if ($TCPConnection) {
$TCPConnection.Dispose()
}
}
#check for needed msal.ps module
if ( !(Get-Module msal.ps -ListAvailable) ) { Write-Host "[$((Get-Date).ToString("yyyy/MM/dd HH:mm:ss.fff"))] MSAL.PS module not installed, please check it out here https://www.powershellgallery.com/packages/MSAL.PS/" -ForegroundColor Red; break}
# execute function
Test-IMAPXOAuth2Connectivity