using Azure.Core; using Azure.Identity; using Azure.Identity.Broker; using Microsoft.Graph; using Microsoft.Graph.Drives.Item.Items.Item.Workbook.Functions.Cosh; using Microsoft.Graph.Me.Presence.SetUserPreferredPresence; using Microsoft.Identity.Client; using Microsoft.Identity.Client.Broker; using Microsoft.Identity.Client.Extensions.Msal; using System.Net.Http.Headers; using System.Net.Http; using System.Reflection; using System.Runtime.InteropServices; using System.Windows; using TeamsLocalLibary; using static System.Formats.Asn1.AsnWriter; using Microsoft.Identity.Client.NativeInterop; using TeamsNetphoneLink.WPF; using TeamsNetphoneLink.Communication; using System; using System.Text.RegularExpressions; namespace TeamsNetphoneLink.Teams { public delegate void PresenceStausEventHandler(Microsoft.Graph.Models.Presence presence); public class TeamsGraph : TeamsGraphEventHandlers { private GraphServiceClient graphClient; public bool Authenticated { get; private set; } private Timer presenceStatusTimer; private Microsoft.Graph.Models.Presence lastPresence; public void CheckPresenceStatusTimer() { presenceStatusTimer = new Timer(CheckPresenceStatus, null, Timeout.Infinite, 2000); presenceStatusTimer.Change(0, 1000); } public void CheckPresenceStatus(object state) { if (graphClient is not null && Authenticated) { try { var presence = graphClient.Me.Presence.GetAsync().GetAwaiter().GetResult(); // If lastPresence is null, initialize it with the current presence if (lastPresence == null) { lastPresence = presence; } // Check if Availability has changed if (lastPresence.Availability != presence.Availability) { OnAvailabilityChanged(presence); } // Check if Activity has changed if (lastPresence.Activity != presence.Activity) { OnActivityChanged(presence); } // Update lastPresence to the current presence lastPresence = presence; } catch (Exception ex) { Console.WriteLine($"Error checking presence state: {ex.Message}"); } } } public async Task IsCachedAccounts() { IPublicClientApplication app = PublicClientApplicationBuilder.Create(Settings.Default.AppID) .WithDefaultRedirectUri() .WithAuthority(String.Format("https://login.microsoftonline.com/{0}", Settings.Default.TenantID)) .Build(); // Register MSAL cache var storage = new StorageCreationPropertiesBuilder("teamsnetphonelink.msal.cache", MsalCacheHelper.UserRootDirectory).Build(); var cacheHelper = await MsalCacheHelper.CreateAsync(storage); cacheHelper.RegisterCache(app.UserTokenCache); IEnumerable accounts = await app.GetAccountsAsync(); return accounts.Any(); } // Authentifizierungsmethode mit Azure Identity public async Task AuthenticateAsync(bool clearCache = false, bool silent = true) { try { // Check if caching is enabled in settings bool useCache = Settings.Default.SaveEntraCredentials; IPublicClientApplication app = PublicClientApplicationBuilder.Create(Settings.Default.AppID) .WithDefaultRedirectUri() .WithAuthority(String.Format("https://login.microsoftonline.com/{0}", Settings.Default.TenantID)) .Build(); // Register MSAL cache only if caching is enabled if (useCache) { var storage = new StorageCreationPropertiesBuilder("teamsnetphonelink.msal.cache", MsalCacheHelper.UserRootDirectory).Build(); var cacheHelper = await MsalCacheHelper.CreateAsync(storage); cacheHelper.RegisterCache(app.UserTokenCache); } IEnumerable accounts = await app.GetAccountsAsync(); //Alle Accounts aus dem Cache entfernen wenn clearCache gesetzt ist if (clearCache) { foreach(var account in accounts) await app.RemoveAsync(account); } // Try to use the previously signed-in account from the cache var existingAccount = accounts.FirstOrDefault(); AuthenticationResult authentication; if (existingAccount is not null && useCache && !clearCache) { Console.WriteLine("Attempting to acquire token silently using cached account."); try { authentication = await app.AcquireTokenSilent(new[] { "Presence.ReadWrite", "offline_access" }, existingAccount) .ExecuteAsync(); await InitializeGraphClient(authentication.AccessToken); return Authenticated; } catch (MsalUiRequiredException) { Console.WriteLine("Silent token acquisition failed. Falling back to interactive authentication."); } } // If no cached account or silent authentication fails, prompt the user for authentication Console.WriteLine("Prompting user for authentication."); authentication = await app.AcquireTokenInteractive(new[] { "Presence.ReadWrite", "offline_access" }) .ExecuteAsync(); await InitializeGraphClient(authentication.AccessToken); return Authenticated; } catch (Exception ex) { ExceptionWindowHelper.Show(this.GetType().Name, MethodBase.GetCurrentMethod().Name, "Fehler bei der Authentifizierung", ex.Message, ex.StackTrace); Console.WriteLine($"Error during authentication: {ex.Message}"); return Authenticated; } } // Helper method to initialize GraphServiceClient private async Task InitializeGraphClient(string accessToken) { graphClient = new GraphServiceClient(new HttpClient(new AuthHandler(accessToken, new HttpClientHandler()))); Authenticated = await TestAuthentication(); } // Testung der Anmeldung und Zugriffsrechte public async Task TestAuthentication() { try { // Test-Anfrage, um Authentifizierung zu validieren await graphClient.Me.Presence.GetAsync(); return true; // Anmeldung und Zugriffsrechte korrekt } catch (Exception ex) { ExceptionWindowHelper.Show(this.GetType().Name, MethodBase.GetCurrentMethod().Name, "Authentifizierung fehlerhaft", ex.Message, ex.StackTrace); return false; // Anmeldung und Zugriffsrechte felerhaft } } // Methode zum Setzen des Präsenzstatus public async Task SetPresenceAsync(PresenceState presenceState) { if (graphClient is null || !Authenticated) { Console.WriteLine("Authentifizierung nicht abgeschlossen. Präsenz kann nicht gesetzt werden."); return false; } // Zuordnen des Präsenzstatus zu Verfügbarkeit und Aktivität var presenceMap = new Dictionary { { PresenceState.Available, ("Available", "Available") }, { PresenceState.Busy, ("Busy", "Busy") }, { PresenceState.DoNotDisturb, ("DoNotDisturb", "DoNotDisturb") }, { PresenceState.BeRightBack, ("BeRightBack", "BeRightBack") }, { PresenceState.Away, ("Away", "Away") }, { PresenceState.Offline, ("Offline", "OffWork") } }; if (!presenceMap.ContainsKey(presenceState)) { Console.WriteLine("Ungültiger Präsenzstatus."); ExceptionWindowHelper.Show(this.GetType().Name, MethodBase.GetCurrentMethod().Name, "Ungültiger Präsenzstatus", presenceState.ToString()); return false; // Ungültiger Status } var (availability, activity) = presenceMap[presenceState]; var requestBody = new SetUserPreferredPresencePostRequestBody { Availability = availability, Activity = activity, ExpirationDuration = TimeSpan.FromHours(2) // Ablaufzeit der Präsenz }; try { await graphClient.Me.Presence.SetUserPreferredPresence.PostAsync(requestBody); return true; // Präsenz erfolgreich gesetzt } catch (Exception ex) { if(ex.GetType() != typeof(Microsoft.Graph.Models.ODataErrors.ODataError)) { ExceptionWindowHelper.Show(this.GetType().Name, MethodBase.GetCurrentMethod().Name, "Fehler beim Setzen der Präsenz", ex.Message, ex.StackTrace); } return false; // Fehler beim Setzen der Präsenz } } // Enum für die verschiedenen Präsenzstatus public enum PresenceState { Available, Busy, DoNotDisturb, BeRightBack, Away, Offline } private static Regex isGuid = new Regex(@"^(\{){0,1}[0-9a-fA-F]{8}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{4}\-[0-9a-fA-F]{12}(\}){0,1}$", RegexOptions.Compiled); public static bool IsGuid(string candidate) { bool isValid = false; if (candidate != null) { if (isGuid.IsMatch(candidate)) { isValid = true; } } return isValid; } } // Custom HttpMessageHandler to inject the access token public class AuthHandler : DelegatingHandler { private readonly string _accessToken; public AuthHandler(string accessToken, HttpMessageHandler innerHandler) : base(innerHandler) { _accessToken = accessToken; } protected override async Task SendAsync(HttpRequestMessage request, CancellationToken cancellationToken) { // Add the access token to the request headers request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", _accessToken); return await base.SendAsync(request, cancellationToken); } } }