#1152 adjust native login
6887cb42
Michael Schmitz
committed
succeeded
6 changed files
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>