97 lines
4.3 KiB
VB.net
97 lines
4.3 KiB
VB.net
Imports DigitalData.Modules.Base
|
|
Imports DigitalData.Modules.Logging
|
|
Imports Microsoft.Identity.Client
|
|
|
|
Namespace Mail
|
|
Public Class OAuth2
|
|
Inherits BaseClass
|
|
|
|
Private ReadOnly _tenantId As String
|
|
Private ReadOnly _clientId As String
|
|
Private ReadOnly _clientSecret As String
|
|
|
|
Public Const O365_SERVER As String = "outlook.office365.com"
|
|
Public Const O365_SCOPE As String = "https://outlook.office365.com/.default"
|
|
Public Const O365_AUTHORITY_PREFIX As String = "https://login.microsoftonline.com/"
|
|
|
|
Public Sub New(pLogConfig As LogConfig, tenantId As String, clientId As String, clientSecret As String)
|
|
MyBase.New(pLogConfig)
|
|
_tenantId = tenantId
|
|
_clientId = clientId
|
|
_clientSecret = clientSecret
|
|
End Sub
|
|
|
|
Public Function GetAccessToken() As String
|
|
' input hardening and better diagnostics
|
|
Dim tenantId = If(_tenantId, String.Empty).Trim()
|
|
Dim clientId = If(_clientId, String.Empty).Trim()
|
|
Dim clientSecret = If(_clientSecret, String.Empty).Trim()
|
|
|
|
If String.IsNullOrWhiteSpace(tenantId) Then
|
|
Logger.Error("OAuth2: tenantId is empty.")
|
|
Throw New ArgumentException("tenantId is empty")
|
|
End If
|
|
If String.IsNullOrWhiteSpace(clientId) Then
|
|
Logger.Error("OAuth2: clientId is empty.")
|
|
Throw New ArgumentException("clientId is empty")
|
|
End If
|
|
If String.IsNullOrWhiteSpace(clientSecret) Then
|
|
Logger.Error("OAuth2: clientSecret is empty.")
|
|
Throw New ArgumentException("clientSecret is empty")
|
|
End If
|
|
|
|
' common misconfiguration: using the Secret ID (GUID) instead of the Secret VALUE
|
|
Dim tmpGuid As Guid
|
|
If Guid.TryParse(clientSecret, tmpGuid) Then
|
|
Logger.Error("OAuth2: clientSecret looks like a GUID (likely the secret ID). Use the secret VALUE instead.")
|
|
Throw New ApplicationException("Invalid client secret: looks like Secret ID (GUID), not the secret value.")
|
|
End If
|
|
|
|
Try
|
|
Dim authority = O365_AUTHORITY_PREFIX & tenantId
|
|
|
|
Dim app = ConfidentialClientApplicationBuilder.Create(clientId).
|
|
WithClientSecret(clientSecret).
|
|
WithAuthority(authority).
|
|
Build()
|
|
|
|
Dim scopes = New String() {O365_SCOPE}
|
|
Dim result = app.AcquireTokenForClient(scopes).ExecuteAsync().GetAwaiter().GetResult()
|
|
|
|
Logger.Info("OAuth2 token acquired for tenant {0}. ExpiresOn={1:u}", tenantId, result.ExpiresOn.UtcDateTime)
|
|
Return result.AccessToken
|
|
Catch ex As MsalServiceException
|
|
' richer diagnostics without depending on specific MSAL version members
|
|
Logger.Error("MSAL service error. Code={0}. Message={1}", ex.ErrorCode, ex.Message)
|
|
|
|
' Try to log StatusCode, ResponseBody if available (via reflection to avoid version dependency)
|
|
Try
|
|
Dim t = GetType(MsalServiceException)
|
|
Dim scProp = t.GetProperty("StatusCode")
|
|
If scProp IsNot Nothing Then
|
|
Dim sc = scProp.GetValue(ex, Nothing)
|
|
If sc IsNot Nothing Then Logger.Error("MSAL StatusCode={0}", sc)
|
|
End If
|
|
Dim rbProp = t.GetProperty("ResponseBody")
|
|
If rbProp IsNot Nothing Then
|
|
Dim rb = rbProp.GetValue(ex, Nothing)
|
|
If rb IsNot Nothing Then Logger.Error("MSAL ResponseBody={0}", rb)
|
|
End If
|
|
Dim ciProp = t.GetProperty("Claims")
|
|
If ciProp IsNot Nothing Then
|
|
Dim claims = ciProp.GetValue(ex, Nothing)
|
|
If claims IsNot Nothing AndAlso claims.ToString().Length > 0 Then Logger.Error("MSAL Claims={0}", claims)
|
|
End If
|
|
Catch
|
|
' ignore reflection diagnostics failures
|
|
End Try
|
|
|
|
Throw
|
|
Catch ex As Exception
|
|
Logger.Error(ex)
|
|
Throw
|
|
End Try
|
|
End Function
|
|
End Class
|
|
End Namespace
|