diff --git a/Calendula.Console/LocalFileService.cs b/Calendula.Console/LocalFileService.cs deleted file mode 100644 index 2733c94..0000000 --- a/Calendula.Console/LocalFileService.cs +++ /dev/null @@ -1,27 +0,0 @@ -using System.Text.Json; - -namespace Calendula.Console -{ - internal class LocalFileService - { - const string FilePath = @""; - internal TokenFile Tokens { get; set; } = new TokenFile(); - - internal async Task ReadFileAsync() - { - var content = await File.ReadAllTextAsync(FilePath); - if (string.IsNullOrEmpty(content)) - { - Tokens = new TokenFile(); - return; - } - Tokens = JsonSerializer.Deserialize(content); - } - - internal async Task WriteFileAsync() - { - var content = JsonSerializer.Serialize(Tokens); - await File.WriteAllTextAsync(FilePath, content); - } - } -} diff --git a/Calendula.Console/Program.cs b/Calendula.Console/Program.cs index d650afd..7cc27d1 100644 --- a/Calendula.Console/Program.cs +++ b/Calendula.Console/Program.cs @@ -18,9 +18,9 @@             DO NOT close this window until you see the final logging message. "); -var primaryAccountRefreshToken = configuration["PrimaryAccountRefreshToken"]; +var primaryUsername = configuration["PrimaryAccountUsername"]; var primaryAccountSubjectPrefix = configuration["PrimaryAccountSubjectPrefix"]; -var secondaryAccountRefreshToken = configuration["SecondaryAccountRefreshToken"]; +var secondaryUsername = configuration["SecondaryAccountUsername"]; var secondaryAccountSubjectPrefix = configuration["SecondaryAccountSubjectPrefix"]; var hour24Time = int.Parse(configuration["Hour24Time"]); var minute24Time = int.Parse(configuration["Minute24Time"]); @@ -52,8 +52,8 @@ td.Actions.Add(new ExecAction($"{dir}/Calendula.Console.exe", dir, null)); ts.RootFolder.RegisterTaskDefinition(DailyTaskName, td); - var source = new SecondaryAccToPrimaryAccProfile(secondaryAccountRefreshToken, secondaryAccountSubjectPrefix); - var dest = new PrimaryAccToSecondaryAccProfile(primaryAccountRefreshToken, primaryAccountSubjectPrefix); + var source = new SecondaryAccToPrimaryAccProfile(secondaryUsername, secondaryAccountSubjectPrefix); + var dest = new PrimaryAccToSecondaryAccProfile(primaryUsername, primaryAccountSubjectPrefix); var service = new CalendulaService(dest, source, logger, clientId, orgConnectionString); var startTime = DateTime.UtcNow; var endTime = startTime.AddDays(daysToSync); diff --git a/Calendula.Console/TokenFile.cs b/Calendula.Console/TokenFile.cs deleted file mode 100644 index 672e56d..0000000 --- a/Calendula.Console/TokenFile.cs +++ /dev/null @@ -1,14 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Text; -using System.Threading.Tasks; - -namespace Calendula.Console -{ - internal class TokenFile - { - public string ClientId { get; set; } - public string RefreshToken { get; set; } - } -} diff --git a/Calendula/AuthService.cs b/Calendula/AuthService.cs index f88d4a7..69de592 100644 --- a/Calendula/AuthService.cs +++ b/Calendula/AuthService.cs @@ -1,34 +1,64 @@ -using System.Text.Json; +using Microsoft.Identity.Client; +using Microsoft.Identity.Client.Extensions.Msal; +using System.Text.Json; namespace Calendula { public class AuthService { - private readonly string ClientId = ""; - private HttpClient Client { get; set; } = new HttpClient(); - public AuthService(string clientId) + private const string TokenCacheFileName = "CalendulaTokenCache"; + + private IPublicClientApplication ClientApp; + private MsalCacheHelper Cache; + + protected AuthService(IPublicClientApplication clientApp, MsalCacheHelper cache) { - ClientId = clientId; + ClientApp = clientApp; + Cache = cache; } - public async Task GetToken(string refreshToken) + public static async Task BuildAsync(string clientId) { - var values = new Dictionary() + var storageProperties = new StorageCreationPropertiesBuilder(TokenCacheFileName, MsalCacheHelper.UserRootDirectory) + .Build(); + + var cache = await MsalCacheHelper.CreateAsync(storageProperties); + + var app = PublicClientApplicationBuilder.Create(clientId) + .WithRedirectUri("http://localhost") + .Build(); + + cache.RegisterCache(app.UserTokenCache); + + return new AuthService(app, cache); + } + + public async Task GetToken(string username) + { + var accounts = await ClientApp.GetAccountsAsync(); + var account = accounts.FirstOrDefault(a => a.Username == username); + + var scopes = new[] { "offline_access", "https://graph.microsoft.com/Calendars.ReadWrite" }; + AuthenticationResult authResult; + + try + { + authResult = await ClientApp.AcquireTokenSilent(scopes, account) + .ExecuteAsync(); + } + catch (MsalUiRequiredException) { - ["client_id"] = ClientId, - ["grant_type"] = "refresh_token", - ["scope"] = "offline_access Calendars.ReadWrite", - ["refresh_token"] = refreshToken, - }; - var body = new FormUrlEncodedContent(values); - - using var response = await Client.PostAsync("https://login.microsoftonline.com/common/oauth2/v2.0/token", body); - - var resp = await response.Content.ReadAsStringAsync(); - using var stream = await response.Content.ReadAsStreamAsync(); - response.EnsureSuccessStatusCode(); - var responseBody = await JsonSerializer.DeserializeAsync(stream); - return responseBody; + var tokenBuilder = ClientApp.AcquireTokenInteractive(scopes) + .WithPrompt(Prompt.NoPrompt); + + tokenBuilder = account == null + ? tokenBuilder.WithLoginHint(username) + : tokenBuilder.WithAccount(account); + + authResult = await tokenBuilder.ExecuteAsync(); + } + + return authResult.AccessToken; } } } \ No newline at end of file diff --git a/Calendula/CalendarSyncService.cs b/Calendula/CalendarSyncService.cs index 366b537..e170a69 100644 --- a/Calendula/CalendarSyncService.cs +++ b/Calendula/CalendarSyncService.cs @@ -26,16 +26,16 @@ public CalendulaService(SyncProfile sourceProfile, SyncProfile destProfile, ILog private async Task InitGraphClientsAsync() { - var auth = new AuthService(ClientId); + var auth = await AuthService.BuildAsync(ClientId); if (SourceGraph == null) { - var response = await auth.GetToken(SourceProfile.RefreshToken); - SourceGraph = new GraphService(response.AccessToken); + var accessToken = await auth.GetToken(SourceProfile.Username); + SourceGraph = new GraphService(accessToken); } if (DestinationGraph == null) { - var response = await auth.GetToken(DestinationProfile.RefreshToken); - DestinationGraph = new GraphService(response.AccessToken); + var accessToken = await auth.GetToken(DestinationProfile.Username); + DestinationGraph = new GraphService(accessToken); } } diff --git a/Calendula/Calendula.csproj b/Calendula/Calendula.csproj index 808e053..81c06f4 100644 --- a/Calendula/Calendula.csproj +++ b/Calendula/Calendula.csproj @@ -8,6 +8,8 @@ + + diff --git a/Calendula/PrimaryAccToSecondaryAccProfile.cs b/Calendula/PrimaryAccToSecondaryAccProfile.cs index 3a8d592..6088a7a 100644 --- a/Calendula/PrimaryAccToSecondaryAccProfile.cs +++ b/Calendula/PrimaryAccToSecondaryAccProfile.cs @@ -9,9 +9,9 @@ namespace Calendula { public class PrimaryAccToSecondaryAccProfile : SyncProfile { - public PrimaryAccToSecondaryAccProfile(string refreshToken, string subjectPrefix) + public PrimaryAccToSecondaryAccProfile(string username, string subjectPrefix) { - RefreshToken = refreshToken; + Username = username; SubjectPrefix = subjectPrefix; } diff --git a/Calendula/RefreshTokenResponse.cs b/Calendula/RefreshTokenResponse.cs deleted file mode 100644 index 63fb026..0000000 --- a/Calendula/RefreshTokenResponse.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Text.Json.Serialization; - -namespace Calendula -{ - public class RefreshTokenResponse - { - [JsonPropertyName("access_token")] - public string AccessToken { get; set; } - [JsonPropertyName("token_type")] - public string TokenType { get; set; } - [JsonPropertyName("expires_in")] - public int ExpiresIn { get; set; } - [JsonPropertyName("refresh_token")] - public string RefreshToken { get; set; } - } -} diff --git a/Calendula/SecondaryAccToPrimaryAccProfile.cs b/Calendula/SecondaryAccToPrimaryAccProfile.cs index 3f9dc7b..a05fd50 100644 --- a/Calendula/SecondaryAccToPrimaryAccProfile.cs +++ b/Calendula/SecondaryAccToPrimaryAccProfile.cs @@ -4,9 +4,9 @@ namespace Calendula { public class SecondaryAccToPrimaryAccProfile : SyncProfile { - public SecondaryAccToPrimaryAccProfile(string refreshToken, string subjectPrefix) + public SecondaryAccToPrimaryAccProfile(string username, string subjectPrefix) { - RefreshToken = refreshToken; + Username = username; SubjectPrefix = subjectPrefix; } diff --git a/Calendula/SyncProfile.cs b/Calendula/SyncProfile.cs index b7b1be5..6f71e96 100644 --- a/Calendula/SyncProfile.cs +++ b/Calendula/SyncProfile.cs @@ -5,7 +5,7 @@ namespace Calendula public abstract class SyncProfile { public string SubjectPrefix { get; set; } - public string RefreshToken { get; set; } + public string Username { get; set; } public abstract Event MapEvent(Event calendarEvent); } }