Imports System Imports System.IO Imports System.Net.Http Imports System.Text Imports System.Windows.Forms.VisualStyles.VisualStyleElement.StartPanel Imports Newtonsoft.Json Imports sun.security.util Imports VERAG_PROG_ALLGEMEIN.cRelayHub Public Class cRelayHubToken Private Shared ReadOnly TOKEN_ENDPOINT_PROD As String = "https://login.singlewindow.io/auth/realms/agsw/protocol/openid-connect/token" Private Shared ReadOnly TOKEN_ENDPOINT_TEST As String = "https://dev-kc.singlewindow.io/auth/realms/agsw/protocol/openid-connect/token" Private Shared ReadOnly CLIENT_ID As String = "agsw-admin" Private Shared ReadOnly USERNAME_PROD As String = "dhub@verag.ag" Private Shared ReadOnly PASSWORD_PROD As String = "Dhub1234*" Private Shared ReadOnly USERNAME_TEST As String = "andreas.test@test.com" Private Shared ReadOnly PASSWORD_TEST As String = "Atez123." Private Const ExpirySkewSeconds As Integer = 60 '====================================================================== ' KONFIGURATION '====================================================================== Private Shared Function GetTokenEndpoint(environment As cRelayHubEnvironment) As String Select Case environment Case cRelayHubEnvironment.TEST Return TOKEN_ENDPOINT_TEST Case cRelayHubEnvironment.PROD Return TOKEN_ENDPOINT_PROD Case Else Throw New ApplicationException("Unbekannte RelayHub-Umgebung.") End Select End Function Private Shared Function GetUsername(environment As cRelayHubEnvironment) As String Select Case environment Case cRelayHubEnvironment.TEST Return USERNAME_TEST Case cRelayHubEnvironment.PROD Return USERNAME_PROD Case Else Throw New ApplicationException("Unbekannte RelayHub-Umgebung.") End Select End Function Private Shared Function GetPassword(environment As cRelayHubEnvironment) As String Select Case environment Case cRelayHubEnvironment.TEST Return PASSWORD_TEST Case cRelayHubEnvironment.PROD Return PASSWORD_PROD Case Else Throw New ApplicationException("Unbekannte RelayHub-Umgebung.") End Select End Function Private Shared Function GetStorePath(environment As cRelayHubEnvironment) As String Dim fileName As String = If(environment = cRelayHubEnvironment.TEST, "token_test.json", "token_prod.json") Return Path.Combine(System.Environment.GetFolderPath(System.Environment.SpecialFolder.ApplicationData), "RelayHub", fileName) End Function '====================================================================== ' ACCESS TOKEN '====================================================================== Public Shared Function GetAccessToken(environment As cRelayHubEnvironment) As String Dim store = LoadStore(environment) If store IsNot Nothing AndAlso Not String.IsNullOrWhiteSpace(store.AccessToken) Then If store.ExpiresAtUtc > DateTimeOffset.UtcNow.AddSeconds(ExpirySkewSeconds) Then Return store.AccessToken End If If store IsNot Nothing AndAlso Not String.IsNullOrWhiteSpace(store.RefreshToken) Then store = TryRefresh(store.RefreshToken, environment) If store Is Nothing OrElse String.IsNullOrWhiteSpace(store.AccessToken) Then store = PasswordLogin(environment) If store Is Nothing OrElse String.IsNullOrWhiteSpace(store.AccessToken) Then Throw New ApplicationException("Konnte keinen gültigen Access Token erhalten (" & environment.ToString() & ").") SaveStore(store, environment) Return store.AccessToken End Function '====================================================================== ' LOGIN '====================================================================== Private Shared Function PasswordLogin(environment As cRelayHubEnvironment) As TokenStore Dim form = New Dictionary(Of String, String) From { {"grant_type", "password"}, {"username", GetUsername(environment)}, {"password", GetPassword(environment)}, {"client_id", CLIENT_ID} } Dim resp = PostForm(form, environment) Return ParseTokenResponse(resp, environment) End Function '====================================================================== ' REFRESH '====================================================================== Private Shared Function TryRefresh(refreshToken As String, environment As cRelayHubEnvironment) As TokenStore Try Dim form = New Dictionary(Of String, String) From { {"grant_type", "refresh_token"}, {"refresh_token", refreshToken}, {"client_id", CLIENT_ID} } Dim resp = PostForm(form, environment) Dim token = ParseTokenResponse(resp, environment) If Not String.IsNullOrWhiteSpace(token.AccessToken) Then SaveStore(token, environment) Return token End If Catch ex As Exception Console.WriteLine("Token Refresh fehlgeschlagen (" & environment.ToString() & "): " & ex.Message) End Try Return Nothing End Function '====================================================================== ' HTTP '====================================================================== Private Shared Function PostForm(formFields As Dictionary(Of String, String), environment As cRelayHubEnvironment) As String Using client As New HttpClient() Using content As New FormUrlEncodedContent(formFields) Dim tokenEndpoint As String = GetTokenEndpoint(environment) Dim username As String = GetUsername(environment) Dim password As String = GetPassword(environment) Console.WriteLine("") Console.WriteLine("==================================================") Console.WriteLine(" RELAYHUB TOKEN REQUEST") Console.WriteLine("==================================================") Console.WriteLine("Environment : " & environment.ToString()) Console.WriteLine("Token Endpoint : " & tokenEndpoint) Console.WriteLine("Client ID : " & CLIENT_ID) 'Console.WriteLine("Username : " & username) 'Console.WriteLine("Password : " & password) 'Console.WriteLine("Password Länge : " & If(password Is Nothing, 0, password.Length)) 'Console.WriteLine("Grant Type : " & If(formFields.ContainsKey("grant_type"), formFields("grant_type"), "")) 'Console.WriteLine("Token Store : " & GetStorePath(environment)) 'Console.WriteLine("==================================================") Dim response = client.PostAsync(tokenEndpoint, content).Result Dim body = response.Content.ReadAsStringAsync().Result If Not response.IsSuccessStatusCode Then Throw New ApplicationException("Token-Endpoint Fehler (" & environment.ToString() & " / " & CInt(response.StatusCode) & "): " & body) Return body End Using End Using End Function '====================================================================== ' TOKEN RESPONSE '====================================================================== Private Shared Function ParseTokenResponse(json As String, environment As cRelayHubEnvironment) As TokenStore Dim r = JsonConvert.DeserializeObject(Of TokenResponse)(json) If r Is Nothing OrElse String.IsNullOrWhiteSpace(r.access_token) Then Throw New ApplicationException("Token-Antwort ungültig oder ohne access_token.") Dim now = DateTimeOffset.UtcNow Dim expiresIn = If(r.expires_in <= 0, 3600, r.expires_in) Dim store = New TokenStore With { .AccessToken = r.access_token.Trim(), .RefreshToken = If(r.refresh_token, String.Empty), .ExpiresAtUtc = now.AddSeconds(expiresIn) } Console.WriteLine("== Neuer RelayHub Token ==") Console.WriteLine("Environment: " & environment.ToString()) Console.WriteLine("Gültig bis UTC: " & store.ExpiresAtUtc.ToString("yyyy-MM-dd HH:mm:ss")) Return store End Function '====================================================================== ' TOKEN LADEN '====================================================================== Private Shared Function LoadStore(environment As cRelayHubEnvironment) As TokenStore Try Dim storePath As String = GetStorePath(environment) If File.Exists(storePath) Then Dim json = File.ReadAllText(storePath, Encoding.UTF8) Dim s = JsonConvert.DeserializeObject(Of TokenStore)(json) If s IsNot Nothing Then Console.WriteLine("== Token aus Datei geladen ==") Console.WriteLine("Environment: " & environment.ToString()) Console.WriteLine("Gültig bis UTC: " & s.ExpiresAtUtc.ToString("yyyy-MM-dd HH:mm:ss")) End If Return s Else Console.WriteLine("Keine Token-Datei für " & environment.ToString() & " vorhanden.") End If Catch ex As Exception Console.WriteLine("Fehler beim Laden der Token-Datei: " & ex.Message) End Try Return Nothing End Function '====================================================================== ' TOKEN SPEICHERN '====================================================================== Private Shared Sub SaveStore(store As TokenStore, environment As cRelayHubEnvironment) Try Dim storePath As String = GetStorePath(environment) Dim dir = Path.GetDirectoryName(storePath) If Not Directory.Exists(dir) Then Directory.CreateDirectory(dir) Dim json = JsonConvert.SerializeObject(store, Formatting.Indented) File.WriteAllText(storePath, json, Encoding.UTF8) Console.WriteLine("Token-Datei gespeichert: " & storePath) Catch ex As Exception Console.WriteLine("Fehler beim Speichern der Token-Datei: " & ex.Message) End Try End Sub '====================================================================== ' TOKEN LÖSCHEN '====================================================================== Private Shared Sub Delete_Store(Optional environment As cRelayHubEnvironment = cRelayHubEnvironment.PROD) Try Dim storePath As String = GetStorePath(environment) If File.Exists(storePath) Then File.Delete(storePath) Console.WriteLine("Token-Datei gelöscht: " & storePath) Catch ex As Exception Console.WriteLine("Fehler beim Löschen der Token-Datei: " & ex.Message) End Try End Sub Public Shared Sub DeleteAllStores() Delete_Store(cRelayHubEnvironment.TEST) Delete_Store(cRelayHubEnvironment.PROD) End Sub '====================================================================== ' DTOs '====================================================================== Private Class TokenResponse Public Property access_token As String Public Property expires_in As Integer Public Property refresh_expires_in As Integer Public Property refresh_token As String Public Property token_type As String Public Property scope As String End Class Private Class TokenStore Public Property AccessToken As String Public Property RefreshToken As String Public Property ExpiresAtUtc As DateTimeOffset End Class End Class