6 changed files
WPE.App.Authentication.Maui | ||
Business | ||
Flow | ||
AuthClient.cs | ||
IAuthClient.cs | ||
WebBrowserAuthenticator.cs | ||
NativeAuthenticationStateProvider.cs | ||
Models | ||
AuthenticationConfiguration.cs | ||
WPE.App.Authentication.Maui.csproj | ||
AuthClient.cs
/WPE.App.Authentication.Maui/Business/Flow/AuthClient.cs-10+13/WPE.App.Authentication.Maui/Business/Flow/AuthClient.cs
Add comment 1 using IdentityModel.Client;
Add comment 2 using IdentityModel.OidcClient;
Add comment 3 using IdentityModel.OidcClient.Results;
Add comment 4 Plus using Infsoft.WPE.App.Authentication.Base.Models;
Add comment 4 5 using Infsoft.WPE.App.Authentication.Maui.Models;
Add comment 6 Plus using Infsoft.WPE.App.Services.Models;
Add comment 7 Plus using Infsoft.WPE.App.Services;
Add comment 5 8 using Microsoft.Extensions.Options;
Add comment 6 9 using IBrowser = IdentityModel.OidcClient.Browser.IBrowser;
Add comment 7 10
Add comment 12 15 /// </summary>
Add comment 13 16 /// <param name="Browser">Browser interface to actually execute login</param>
Add comment 14 17 /// <param name="options">Configured options for authentication</param>
Add comment 15 Minus internal class AuthClient(IBrowser Browser, IOptions<AuthenticationConfiguration> options) : IAuthClient
Add comment 18 Plus internal class AuthClient(IBrowser Browser, IOptions<AuthenticationConfiguration> options, ISettingsProvider SettingsProvider) : IAuthClient
Add comment 16 19 {
Add comment 17 20 private AuthenticationConfiguration Options => options.Value;
Add comment 18 21 private OidcClient? OidcClient { get; set; }
Add comment 20 23 private Parameters? AdditionalParams { get; set; }
Add comment 21 24
Add comment 22 25 /// <inheritdoc/>
Add comment 23 Minus public async Task<LoginResult> SignIn(string scope, bool customerLogin, string? voucher)
Add comment 26 Plus public async Task<LoginResult> SignIn(LoginType loginType, string scope, string emailOrVoucherCode)
Add comment 24 27 {
Add comment 25 Minus OidcClient = CreateOidcClient(scope, customerLogin, voucher);
Add comment 28 Plus OidcClient = CreateOidcClient(loginType, scope, emailOrVoucherCode);
Add comment 26 29 return await OidcClient.LoginAsync(new()
Add comment 27 30 {
Add comment 28 31 FrontChannelExtraParameters = AdditionalParams
Add comment 43 46 return await OidcClient.RefreshTokenAsync(refreshToken, scope: OidcClient.Options.Scope);
Add comment 44 47 }
Add comment 45 48
Add comment 46 Minus private OidcClient CreateOidcClient(string scope, bool customerLogin, string? voucher)
Add comment 49 Plus private OidcClient CreateOidcClient(LoginType loginType, string scope, string emailOrVoucherCode)
Add comment 47 50 {
Add comment 48 51 AdditionalParams = [];
Add comment 49 52
Add comment 50 Minus if (voucher is not null)
Add comment 51 Minus AdditionalParams.Add("X-Voucher-Code", voucher);
Add comment 53 Plus if (loginType is LoginType.Visitor)
Add comment 54 Plus AdditionalParams.Add("X-Voucher-Code", emailOrVoucherCode);
Add comment 55 Plus else if (loginType is LoginType.Employee)
Add comment 56 Plus AdditionalParams.Add("X-Login-Hint", emailOrVoucherCode);
Add comment 52 57
Add comment 53 Minus if (!customerLogin)
Add comment 54 Minus AdditionalParams.Add("X-External-Provider", "infsoft");
Add comment 55 Minus else
Add comment 56 Minus AdditionalParams.Add("X-External-Provider", Options.Tenant);
Add comment 58 Plus if (SettingsProvider.Tenant != "LocAwareOne")
Add comment 59 Plus AdditionalParams.Add("X-Tenant", SettingsProvider.Tenant);
Add comment 57 60
Add comment 58 61 if (OidcClient is not null && OidcClient.Options.Scope.Contains(scope))
Add comment 59 62 return OidcClient;
IAuthClient.cs
/WPE.App.Authentication.Maui/Business/Flow/IAuthClient.cs-1+7/WPE.App.Authentication.Maui/Business/Flow/IAuthClient.cs
Add comment 1 using IdentityModel.OidcClient;
Add comment 2 using IdentityModel.OidcClient.Results;
Add comment 3 Plus using Infsoft.WPE.App.Authentication.Base.Business;
Add comment 4 Plus using Infsoft.WPE.App.Authentication.Base.Models;
Add comment 3 5
Add comment 4 6 namespace Infsoft.WPE.App.Authentication.Maui.Business.Flow
Add comment 5 7 {
Add comment 11 13 /// <summary>
Add comment 12 14 /// Start a login and resolve tokens
Add comment 13 15 /// </summary>
Add comment 16 Plus /// <remarks><paramref name="loginType"/> will never be <see cref="LoginType.Anonymous"/>, that value is already handled in <see cref="AppAuthenticationStateProvider" /></remarks>
Add comment 17 Plus /// <param name="loginType">Login type requested</param>
Add comment 18 Plus /// <param name="scope">Scope to request</param>
Add comment 19 Plus /// <param name="emailOrVoucherCode">Entered email address or voucher code</param>
Add comment 14 20 /// <returns>Result of login flow, contains all tokens if successfull</returns>
Add comment 15 Minus Task<LoginResult> SignIn(string scope, bool customerLogin, string? voucher);
Add comment 21 Plus Task<LoginResult> SignIn(LoginType loginType, string scope, string emailOrVoucherCode);
Add comment 16 22
Add comment 17 23 /// <summary>
Add comment 18 24 /// Refresh all tokens with given refresh token and resolve them
WebBrowserAuthenticator.cs
/WPE.App.Authentication.Maui/Business/Flow/WebBrowserAuthenticator.cs-4+4/WPE.App.Authentication.Maui/Business/Flow/WebBrowserAuthenticator.cs
Add comment 52 /// <param name="redirectUrl">Initial redirect url</param>
Add comment 53 /// <param name="result">Result of Web authentication</param>
Add comment 54 /// <returns>cleaned redirect url</returns>
Add comment 55 Minus private string WinStateWorkaroundURL(string redirectUrl, WebAuthenticatorResult result)
Add comment 55 Plus private static string WinStateWorkaroundURL(string redirectUrl, WebAuthenticatorResult result)
Add comment 56 {
Add comment 57 #if WINDOWS
Add comment 58 try
Add comment 66 if (!string.IsNullOrWhiteSpace(stateParameter))
Add comment 67 {
Add comment 68 // Remove the state key added by WebAuthenticator that includes appInstanceId
Add comment 69 Minus modifiedParameters = modifiedParameters.Where(p => !p.StartsWith("state", StringComparison.OrdinalIgnoreCase)).ToList();
Add comment 69 Plus modifiedParameters = [.. modifiedParameters.Where(p => !p.StartsWith("state", StringComparison.OrdinalIgnoreCase))];
Add comment 70
Add comment 71 Minus stateParameter = $"state={System.Web.HttpUtility.UrlDecode(stateParameter).Split(':').Last()[1..^2]}";
Add comment 71 Plus stateParameter = $"state={System.Web.HttpUtility.UrlDecode(stateParameter).Split(':')[^1][1..^2]}";
Add comment 72 modifiedParameters.Add(stateParameter);
Add comment 73 }
Add comment 74 var values = string.Join("&", modifiedParameters);
Add comment 80 throw;
Add comment 81 }
Add comment 82 #else
Add comment 83 Minus return new RequestUrl(redirectUrl).Create(new Parameters(result.Properties));
Add comment 83 Plus return new RequestUrl(redirectUrl).Create([.. result.Properties]);
Add comment 84 #endif
Add comment 85 }
Add comment 86 }
NativeAuthenticationStateProvider.cs
/WPE.App.Authentication.Maui/Business/NativeAuthenticationStateProvider.cs-12+11/WPE.App.Authentication.Maui/Business/NativeAuthenticationStateProvider.cs
Add comment 4 using Infsoft.WPE.App.Authentication.Maui.Business.Token;
Add comment 5 using Infsoft.WPE.App.DTO;
Add comment 6 using Infsoft.WPE.App.Services;
Add comment 7 Minus using Infsoft.WPE.App.Services.Models;
Add comment 8 7 using Microsoft.AspNetCore.Components.Authorization;
Add comment 8 Plus using Microsoft.Extensions.Logging;
Add comment 9 using System.IdentityModel.Tokens.Jwt;
Add comment 10 using System.Security.Claims;
Add comment 11
Add comment 12 namespace Infsoft.WPE.App.Authentication.Maui.Business
Add comment 13 {
Add comment 14 internal class NativeAuthenticationStateProvider(ITokenManager TokenManager, IAuthClient AuthClient, ISettingsProvider SettingsProvider,
Add comment 15 Minus IWPELogger<NativeAuthenticationStateProvider> Logger, IWPELogger<AppAuthenticationStateProvider> BaseLogger) : AppAuthenticationStateProvider(BaseLogger)
Add comment 15 Plus ILogger<NativeAuthenticationStateProvider> logger, IPlatformPreferenceService PlatformPreferenceService) : AppAuthenticationStateProvider(logger, PlatformPreferenceService)
Add comment 16 {
Add comment 17 public override async Task<AuthenticationState> GetAuthenticationStateAsync()
Add comment 18 {
Add comment 21
Add comment 22 if (user.Identity != null && user.Identity.IsAuthenticated && user.Identity.AuthenticationType != "anonymous")
Add comment 23 {
Add comment 24 Minus Logger.Info("starting background check..");
Add comment 24 Plus Logger.LogInformation("starting background check..");
Add comment 25 Timer? timer = null;
Add comment 26
Add comment 27 timer = new Timer(async _ =>
Add comment 29 var currentUser = await GetUser(false);
Add comment 30 if (currentUser.Identity?.IsAuthenticated == false)
Add comment 31 {
Add comment 32 Minus Logger.Info("user logged out");
Add comment 32 Plus Logger.LogInformation("user logged out");
Add comment 33 NotifyAuthenticationStateChanged(Task.FromResult(new AuthenticationState(currentUser)));
Add comment 34 await timer!.DisposeAsync();
Add comment 35 }
Add comment 39 return state;
Add comment 40 }
Add comment 41
Add comment 42 Minus protected override Task SignIn(LoginType loginType, string? voucherCode)
Add comment 42 Plus protected override Task SignIn(LoginType loginType, string emailOrVoucherCode)
Add comment 43 {
Add comment 44 var loginTask = SignInWithSilent();
Add comment 45 NotifyAuthenticationStateChanged(loginTask);
Add comment 50 {
Add comment 51 if (!(await SilentSignInCore()).User?.Identity?.IsAuthenticated ?? false)
Add comment 52 {
Add comment 53 Minus var user = await SignInCore(loginType, voucherCode);
Add comment 53 Plus var user = await SignInCore(loginType, emailOrVoucherCode);
Add comment 54 cachedUser = user;
Add comment 55 }
Add comment 56 return new AuthenticationState(cachedUser);
Add comment 57 }
Add comment 58 }
Add comment 59
Add comment 60 Minus private async Task<ClaimsPrincipal> SignInCore(LoginType loginType, string? voucherCode)
Add comment 60 Plus private async Task<ClaimsPrincipal> SignInCore(LoginType loginType, string emailOrVoucherCode)
Add comment 61 {
Add comment 62 Minus var noCustomerLogin = loginType is LoginType.Employee && SettingsProvider.AuthenticationType is AuthenticationType.Infsoft;
Add comment 63 62 var scope = SettingsProvider.DevModeEnabled && SettingsProvider.AppEnvironment is not AppEnvironment.Prod ? "env.test" : "env.prod";
Add comment 64 Minus var authResult = await AuthClient.SignIn(scope, !noCustomerLogin, loginType is LoginType.Visitor ? voucherCode : null);
Add comment 63 Plus var authResult = await AuthClient.SignIn(loginType, scope, emailOrVoucherCode);
Add comment 65 64
Add comment 66 65 if (authResult.IsError)
Add comment 67 66 return NoUser;
Add comment 103 102 return cachedUser;
Add comment 104 103 else if (now < userLastCheck + UserCacheRefreshInterval)
Add comment 105 104 {
Add comment 106 Minus Logger.Debug("Taking user from cache");
Add comment 105 Plus Logger.LogDebug("Taking user from cache");
Add comment 107 106 return cachedUser;
Add comment 108 107 }
Add comment 109 108 }
Add comment 110 109
Add comment 111 Minus Logger.Debug("Fetching user");
Add comment 110 Plus Logger.LogDebug("Fetching user");
Add comment 112 111 cachedUser = await ExtractUser();
Add comment 113 112 userLastCheck = now;
Add comment 114 113
Add comment 117 116
Add comment 118 117 private async Task<ClaimsPrincipal> ExtractUser()
Add comment 119 118 {
Add comment 120 Minus Logger.Info("Reading user information from id token");
Add comment 119 Plus Logger.LogInformation("Reading user information from id token");
Add comment 121 120 var token = await TokenManager.GetIdToken();
Add comment 122 121 if (token is null)
Add comment 123 122 return NoUser;
AuthenticationConfiguration.cs
/WPE.App.Authentication.Maui/Models/AuthenticationConfiguration.cs-5/WPE.App.Authentication.Maui/Models/AuthenticationConfiguration.cs
Add comment 11 public required string Authority { get; set; }
Add comment 12
Add comment 13 /// <summary>
Add comment 14 Minus /// Tenant to login for
Add comment 15 Minus /// </summary>
Add comment 16 Minus public required string Tenant { get; set; }
Add comment 17 Minus
Add comment 18 Minus /// <summary>
Add comment 19 14 /// Client id of app
Add comment 20 15 /// </summary>
Add comment 21 16 public required string ClientId { get; set; }
WPE.App.Authentication.Maui.csproj
/WPE.App.Authentication.Maui/WPE.App.Authentication.Maui.csproj-5+4/WPE.App.Authentication.Maui/WPE.App.Authentication.Maui.csproj
Add comment 24 <PackageIcon>icon.png</PackageIcon>
Add comment 25 <PackageReadmeFile>ReadMe.md</PackageReadmeFile>
Add comment 26 <PackageTags>WPE;Workplace Experience;infsoft</PackageTags>
Add comment 27 Minus <Version>0.1.2</Version>
Add comment 27 Plus <Version>0.2.0</Version>
Add comment 28 <GenerateDocumentationFile>true</GenerateDocumentationFile>
Add comment 29 </PropertyGroup>
Add comment 30
Add comment 31 <ItemGroup>
Add comment 32 Minus <PackageReference Include="Infsoft.WPE.App.Authentication.Base" Version="0.2.1" />
Add comment 33 Minus <PackageReference Include="Infsoft.WPE.App.DTO" Version="1.4.0" />
Add comment 34 Minus <PackageReference Include="Microsoft.Extensions.Http" Version="9.0.1" />
Add comment 32 Plus <PackageReference Include="Infsoft.WPE.App.Authentication.Base" Version="0.3.1" />
Add comment 33 Plus <PackageReference Include="Infsoft.WPE.App.DTO" Version="1.4.2" />
Add comment 35 34 <PackageReference Include="IdentityModel.OidcClient" Version="6.0.0" />
Add comment 36 Minus <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.3.1" />
Add comment 35 Plus <PackageReference Include="System.IdentityModel.Tokens.Jwt" Version="8.5.0" />
Add comment 37 36 <PackageReference Include="Microsoft.Maui.Controls" Version="8.0.100" />
Add comment 38 37 <PackageReference Include="Microsoft.Maui.Controls.Compatibility" Version="8.0.100" />
Add comment 39 38 </ItemGroup>