From 5ba5aec666f6ab67a958f7f9b2212e14b84a86c7 Mon Sep 17 00:00:00 2001 From: Roxk Date: Wed, 1 Jun 2022 14:36:14 +0800 Subject: [PATCH] Add .NET analyzer #44 --- Authgear.Xamarin/.editorconfig | 40 +++++++++++- .../AnonymousUserIosException.ios.cs | 3 +- .../AnonymousUserNotFoundException.cs | 2 +- Authgear.Xamarin/AuthenticateOptions.cs | 5 +- Authgear.Xamarin/Authgear.Xamarin.csproj | 3 +- Authgear.Xamarin/AuthgearSdk.cs | 63 ++++++++++--------- Authgear.Xamarin/AuthgearSdk.ios.cs | 3 + Authgear.Xamarin/BiometricIosException.ios.cs | 3 +- ...icPromptAuthenticationException.android.cs | 2 +- Authgear.Xamarin/CodeVerifier.cs | 9 +-- Authgear.Xamarin/Data/Biometric.android.cs | 26 ++++---- Authgear.Xamarin/Data/Biometric.ios.cs | 14 +++-- .../Data/HttpResponseMessagExtensions.cs | 4 +- Authgear.Xamarin/Data/KeyRepo.android.cs | 10 +-- Authgear.Xamarin/Data/KeyRepo.ios.cs | 12 ++-- Authgear.Xamarin/Data/Oauth/OauthRepoHttp.cs | 18 +++--- .../Data/PersistentContainerStorage.cs | 1 - .../DeviceInfo/DeviceInfoAndroid.android.cs | 7 ++- .../DeviceInfo/DeviceInfoIos.ios.cs | 6 +- .../Oauth/OidcAuthenticationRequest.cs | 5 +- Authgear.Xamarin/OauthException.cs | 2 +- Authgear.Xamarin/PromoteOptions.cs | 2 +- Authgear.Xamarin/PropertyNameAttribute.cs | 8 --- Authgear.Xamarin/ReauthenticateOptions.cs | 2 +- Authgear.Xamarin/ReauthenticateResult.cs | 17 ----- Authgear.Xamarin/ServerException.cs | 2 +- Authgear.Xamarin/SettingsOptions.cs | 2 +- Authgear.Xamarin/UserInfo.cs | 4 +- Authgear.Xamarin/WebView.android.cs | 9 ++- Authgear.Xamarin/WebView.ios.cs | 2 +- 30 files changed, 162 insertions(+), 124 deletions(-) delete mode 100644 Authgear.Xamarin/PropertyNameAttribute.cs delete mode 100644 Authgear.Xamarin/ReauthenticateResult.cs diff --git a/Authgear.Xamarin/.editorconfig b/Authgear.Xamarin/.editorconfig index c63e239..fcf8c97 100644 --- a/Authgear.Xamarin/.editorconfig +++ b/Authgear.Xamarin/.editorconfig @@ -4,4 +4,42 @@ dotnet_separate_import_directive_groups = false csharp_new_line_before_open_brace = all csharp_new_line_before_catch = true csharp_new_line_before_else = true -csharp_new_line_before_finally = true \ No newline at end of file +csharp_new_line_before_finally = true + +# CA1032: Implement standard exception constructors +dotnet_diagnostic.CA1032.severity = silent + +# CA1031: Do not catch general exception types +dotnet_diagnostic.CA1031.severity = silent + +# CA1307: Specify StringComparison for clarity +# Some methods are not available in netstandard +dotnet_diagnostic.CA1307.severity = silent + +# CA1056: URI-like properties should not be strings +dotnet_diagnostic.CA1056.severity = silent + +# CA2227: Collection properties should be read only +dotnet_diagnostic.CA2227.severity = silent + +# CA1003: Use generic event handler instances +dotnet_diagnostic.CA1003.severity = silent + +# CA1062: Validate arguments of public methods +# Not needed with NRT +dotnet_diagnostic.CA1062.severity = silent + +# CA2208: Instantiate argument exceptions correctly +dotnet_diagnostic.CA2208.severity = silent + +# CA2208: Type names should not match namespaces +dotnet_diagnostic.CA1724.severity = silent + +# CA1054: URI-like parameters should not be strings +dotnet_diagnostic.CA1054.severity = silent + +# CA2237: Mark ISerializable types with SerializableAttribute +dotnet_diagnostic.CA2237.severity = silent + +# CA1014: Mark assemblies with CLSCompliantAttribute +dotnet_diagnostic.CA1014.severity = silent diff --git a/Authgear.Xamarin/AnonymousUserIosException.ios.cs b/Authgear.Xamarin/AnonymousUserIosException.ios.cs index 8f6bec7..f6afc19 100644 --- a/Authgear.Xamarin/AnonymousUserIosException.ios.cs +++ b/Authgear.Xamarin/AnonymousUserIosException.ios.cs @@ -1,4 +1,5 @@ using System; +using System.Globalization; using Foundation; using Security; @@ -13,7 +14,7 @@ public AnonymousUserIosException(NSError error) : base("") Error = error; } - public AnonymousUserIosException(SecStatusCode code) : this(new NSError(NSError.OsStatusErrorDomain, Convert.ToInt32(code))) + public AnonymousUserIosException(SecStatusCode code) : this(new NSError(NSError.OsStatusErrorDomain, Convert.ToInt32(code, CultureInfo.InvariantCulture))) { } diff --git a/Authgear.Xamarin/AnonymousUserNotFoundException.cs b/Authgear.Xamarin/AnonymousUserNotFoundException.cs index af6f069..cc03a9a 100644 --- a/Authgear.Xamarin/AnonymousUserNotFoundException.cs +++ b/Authgear.Xamarin/AnonymousUserNotFoundException.cs @@ -4,7 +4,7 @@ namespace Authgear.Xamarin { - internal class AnonymousUserNotFoundException : Exception + public class AnonymousUserNotFoundException : Exception { public AnonymousUserNotFoundException() : base() { } } diff --git a/Authgear.Xamarin/AuthenticateOptions.cs b/Authgear.Xamarin/AuthenticateOptions.cs index fbf5d51..2b458b6 100644 --- a/Authgear.Xamarin/AuthenticateOptions.cs +++ b/Authgear.Xamarin/AuthenticateOptions.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Text; using Authgear.Xamarin.Oauth; @@ -9,9 +10,9 @@ public class AuthenticateOptions { public string RedirectUri { get; set; } public string? State { get; set; } - public List? PromptOptions { get; set; } + public ReadOnlyCollection? PromptOptions { get; set; } public string? LoginHint { get; set; } - public List? UiLocales { get; set; } + public ReadOnlyCollection? UiLocales { get; set; } public ColorScheme? ColorScheme { get; set; } public AuthenticatePage? Page { get; set; } diff --git a/Authgear.Xamarin/Authgear.Xamarin.csproj b/Authgear.Xamarin/Authgear.Xamarin.csproj index 7b64c09..cdc284b 100644 --- a/Authgear.Xamarin/Authgear.Xamarin.csproj +++ b/Authgear.Xamarin/Authgear.Xamarin.csproj @@ -19,14 +19,15 @@ LICENSE true https://github.com/authgear/authgear-sdk-xamarin - 8.0 enable + AllEnabledByDefault + diff --git a/Authgear.Xamarin/AuthgearSdk.cs b/Authgear.Xamarin/AuthgearSdk.cs index ed68ee6..fb166af 100644 --- a/Authgear.Xamarin/AuthgearSdk.cs +++ b/Authgear.Xamarin/AuthgearSdk.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Net; using System.Net.Http; @@ -63,9 +64,9 @@ public DateTimeOffset? AuthTime private readonly IBiometric biometric; private readonly IWebView webView; private readonly string name; - private bool isInitialized = false; - private string? refreshToken = null; - private Task? refreshAccessTokenTask = null; + private bool isInitialized; + private string? refreshToken; + private Task? refreshAccessTokenTask; public event EventHandler SessionStateChange; private bool ShouldSuppressIDPSessionCookie { @@ -83,6 +84,8 @@ public bool CanReauthenticate } } private readonly object tokenStateLock = new object(); + + // These fields are configured in the platform's respective constructors // For brevity, they are not passed into this contructor. If it's proven that bugs are caused by some platform missing initializing // constructors, change the constructor to accept all those dependencies (think of it as each platform's constructor injecting @@ -144,9 +147,9 @@ private void UpdateSessionState(SessionState state, SessionStateChangeReason rea public async Task AuthenticateAnonymouslyAsync() { EnsureIsInitialized(); - var challengeResponse = await oauthRepo.OauthChallengeAsync("anonymous_request"); + var challengeResponse = await oauthRepo.OauthChallengeAsync("anonymous_request").ConfigureAwait(false); var challenge = challengeResponse.Token!; - var keyId = await containerStorage.GetAnonymousKeyIdAsync(name); + var keyId = await containerStorage.GetAnonymousKeyIdAsync(name).ConfigureAwait(false); var deviceInfo = PlatformGetDeviceInfo(); var jwtResult = await keyRepo.GetOrCreateAnonymousJwtAsync(keyId, challenge, deviceInfo).ConfigureAwait(false); keyId = jwtResult.KeyId; @@ -154,8 +157,8 @@ public async Task AuthenticateAnonymouslyAsync() var tokenResponse = await oauthRepo.OidcTokenRequestAsync(new OidcTokenRequest(GrantType.Anonymous, ClientId, GetDeviceInfoString(deviceInfo)) { Jwt = jwt - }); - var userInfo = await oauthRepo.OidcUserInfoRequestAsync(tokenResponse.AccessToken!); + }).ConfigureAwait(false); + var userInfo = await oauthRepo.OidcUserInfoRequestAsync(tokenResponse.AccessToken!).ConfigureAwait(false); SaveToken(tokenResponse, SessionStateChangeReason.Authenciated); await DisableBiometricAsync().ConfigureAwait(false); containerStorage.SetAnonymousKeyId(name, keyId); @@ -175,10 +178,10 @@ public async Task PromoteAnonymousUserAsync(PromoteOptions options) throw new ArgumentNullException(nameof(options.RedirectUri)); } EnsureIsInitialized(); - var keyId = (await containerStorage.GetAnonymousKeyIdAsync(name)) ?? throw new AnonymousUserNotFoundException(); - var challengeResponse = await oauthRepo.OauthChallengeAsync("anonymous_request"); + var keyId = (await containerStorage.GetAnonymousKeyIdAsync(name).ConfigureAwait(false)) ?? throw new AnonymousUserNotFoundException(); + var challengeResponse = await oauthRepo.OauthChallengeAsync("anonymous_request").ConfigureAwait(false); var challenge = challengeResponse.Token!; - var jwt = await keyRepo.PromoteAnonymousUserAsync(keyId, challenge, PlatformGetDeviceInfo()); + var jwt = await keyRepo.PromoteAnonymousUserAsync(keyId, challenge, PlatformGetDeviceInfo()).ConfigureAwait(false); var jwtValue = WebUtility.UrlEncode(jwt); var loginHint = $"https://authgear.com/login_hint?type=anonymous&jwt={jwtValue}"; var codeVerifier = new CodeVerifier(new RNGCryptoServiceProvider()); @@ -218,7 +221,7 @@ public async Task ReauthenticateAsync(ReauthenticateOptions options, B EnsureIsInitialized(); if (await GetIsBiometricEnabledAsync().ConfigureAwait(false) && biometricOptions != null) { - return await AuthenticateBiometricAsync(biometricOptions); + return await AuthenticateBiometricAsync(biometricOptions).ConfigureAwait(false); } if (!CanReauthenticate) { @@ -257,8 +260,8 @@ public async Task LogoutAsync(bool? force = null) public async Task OpenUrlAsync(string path, SettingsOptions? options = null) { EnsureIsInitialized(); - var refreshToken = await tokenStorage.GetRefreshTokenAsync(name) ?? ""; - var appSessionTokenResponse = await oauthRepo.OauthAppSessionTokenAsync(refreshToken); + var refreshToken = await tokenStorage.GetRefreshTokenAsync(name).ConfigureAwait(false) ?? ""; + var appSessionTokenResponse = await oauthRepo.OauthAppSessionTokenAsync(refreshToken).ConfigureAwait(false); var token = appSessionTokenResponse.AppSessionToken; var query = new Dictionary(); @@ -280,7 +283,7 @@ public async Task OpenUrlAsync(string path, SettingsOptions? options = null) builder.Query = query.ToQueryParameter(); var url = builder.Uri; - var loginHint = string.Format(LoginHintFormat, WebUtility.UrlEncode(token)); + var loginHint = string.Format(CultureInfo.InvariantCulture, LoginHintFormat, WebUtility.UrlEncode(token)); if (options == null) { options = new SettingsOptions(); } var request = options.ToRequest(url.ToString(), loginHint, ShouldSuppressIDPSessionCookie); var authorizeUrl = await GetAuthorizeEndpointAsync(request, null).ConfigureAwait(false); @@ -399,7 +402,7 @@ private async Task OpenAuthorizeUrlAsync(string redirectUrl, string auth return builder.ToString(); } - private async Task<(UserInfo userInfo, OidcTokenResponse tokenResponse, string state)> ParseDeepLinkAndGetUserAsync(string deepLink, string codeVerifier) + private async Task<(UserInfo userInfo, OidcTokenResponse tokenResponse)> ParseDeepLinkAndGetUserAsync(string deepLink, string codeVerifier) { var uri = new Uri(deepLink); var path = uri.LocalPath == "/" ? "" : uri.LocalPath; @@ -423,22 +426,22 @@ private async Task OpenAuthorizeUrlAsync(string redirectUrl, string auth Code = code, RedirectUri = redirectUri, CodeVerifier = codeVerifier ?? "", - }); - var userInfo = await oauthRepo.OidcUserInfoRequestAsync(tokenResponse.AccessToken!); - return (userInfo, tokenResponse, state); + }).ConfigureAwait(false); + var userInfo = await oauthRepo.OidcUserInfoRequestAsync(tokenResponse.AccessToken!).ConfigureAwait(false); + return (userInfo, tokenResponse); } private async Task FinishAuthenticationAsync(string deepLink, string codeVerifier) { - var (userInfo, tokenResponse, _) = await ParseDeepLinkAndGetUserAsync(deepLink, codeVerifier); + var (userInfo, tokenResponse) = await ParseDeepLinkAndGetUserAsync(deepLink, codeVerifier).ConfigureAwait(false); SaveToken(tokenResponse, SessionStateChangeReason.Authenciated); - await DisableBiometricAsync(); + await DisableBiometricAsync().ConfigureAwait(false); return userInfo; } private async Task FinishReauthenticationAsync(string deepLink, string codeVerifier) { - var (userInfo, tokenResponse, _) = await ParseDeepLinkAndGetUserAsync(deepLink, codeVerifier); + var (userInfo, tokenResponse) = await ParseDeepLinkAndGetUserAsync(deepLink, codeVerifier).ConfigureAwait(false); if (tokenResponse.IdToken != null) { IdTokenHint = tokenResponse.IdToken; @@ -482,10 +485,10 @@ public async Task EnableBiometricAsync(BiometricOptions options) EnsureIsInitialized(); await RefreshAccessTokenIfNeededAsync().ConfigureAwait(false); var accessToken = AccessToken ?? throw new UnauthenticatedUserException(); - var challengeResponse = await oauthRepo.OauthChallengeAsync("biometric_request"); + var challengeResponse = await oauthRepo.OauthChallengeAsync("biometric_request").ConfigureAwait(false); var challenge = challengeResponse.Token!; - var result = await biometric.EnableBiometricAsync(options, challenge, PlatformGetDeviceInfo()); - await oauthRepo.BiometricSetupRequestAsync(accessToken, ClientId, result.Jwt); + var result = await biometric.EnableBiometricAsync(options, challenge, PlatformGetDeviceInfo()).ConfigureAwait(false); + await oauthRepo.BiometricSetupRequestAsync(accessToken, ClientId, result.Jwt).ConfigureAwait(false); containerStorage.SetBiometricKeyId(name, result.Kid); } @@ -498,8 +501,8 @@ public async Task EnableBiometricAsync(BiometricOptions options) public async Task AuthenticateBiometricAsync(BiometricOptions options) { EnsureIsInitialized(); - var kid = await containerStorage.GetBiometricKeyIdAsync(name) ?? throw new BiometricPrivateKeyNotFoundException(); - var challengeResponse = await oauthRepo.OauthChallengeAsync("biometric_request"); + var kid = await containerStorage.GetBiometricKeyIdAsync(name).ConfigureAwait(false) ?? throw new BiometricPrivateKeyNotFoundException(); + var challengeResponse = await oauthRepo.OauthChallengeAsync("biometric_request").ConfigureAwait(false); var challenge = challengeResponse.Token!; try { @@ -510,8 +513,8 @@ public async Task AuthenticateBiometricAsync(BiometricOptions options) var tokenResponse = await oauthRepo.OidcTokenRequestAsync(new OidcTokenRequest(GrantType.Biometric, ClientId, GetDeviceInfoString()) { Jwt = jwt - }); - var userInfo = await oauthRepo.OidcUserInfoRequestAsync(tokenResponse.AccessToken!); + }).ConfigureAwait(false); + var userInfo = await oauthRepo.OidcUserInfoRequestAsync(tokenResponse.AccessToken!).ConfigureAwait(false); SaveToken(tokenResponse, SessionStateChangeReason.Authenciated); return userInfo; } @@ -538,10 +541,10 @@ public async Task AuthenticateBiometricAsync(BiometricOptions options) private string GetDeviceInfoString() { - return ConvertExtensions.ToBase64UrlSafeString(JsonSerializer.Serialize(PlatformGetDeviceInfo()), Encoding.UTF8); + return GetDeviceInfoString(PlatformGetDeviceInfo()); } - private string GetDeviceInfoString(DeviceInfoRoot deviceInfo) + private static string GetDeviceInfoString(DeviceInfoRoot deviceInfo) { return ConvertExtensions.ToBase64UrlSafeString(JsonSerializer.Serialize(deviceInfo), Encoding.UTF8); } diff --git a/Authgear.Xamarin/AuthgearSdk.ios.cs b/Authgear.Xamarin/AuthgearSdk.ios.cs index ad5dc4f..ef955de 100644 --- a/Authgear.Xamarin/AuthgearSdk.ios.cs +++ b/Authgear.Xamarin/AuthgearSdk.ios.cs @@ -19,7 +19,10 @@ public AuthgearSdk(UIApplication app, AuthgearOptions options) : this(options) keyRepo = new KeyRepo(); webView = new WebView(); } + // Other platform's implementation is not static +#pragma warning disable CA1822 // Mark members as static private DeviceInfoRoot PlatformGetDeviceInfo() +#pragma warning restore CA1822 // Mark members as static { return new DeviceInfoRoot { diff --git a/Authgear.Xamarin/BiometricIosException.ios.cs b/Authgear.Xamarin/BiometricIosException.ios.cs index 106b873..fb26d80 100644 --- a/Authgear.Xamarin/BiometricIosException.ios.cs +++ b/Authgear.Xamarin/BiometricIosException.ios.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Text; using Foundation; using Security; @@ -15,7 +16,7 @@ public BiometricIosException(NSError error) : base("") Error = error; } - public BiometricIosException(SecStatusCode code) : this(new NSError(NSError.OsStatusErrorDomain, Convert.ToInt32(code))) + public BiometricIosException(SecStatusCode code) : this(new NSError(NSError.OsStatusErrorDomain, Convert.ToInt32(code, CultureInfo.InvariantCulture))) { } diff --git a/Authgear.Xamarin/BiometricPromptAuthenticationException.android.cs b/Authgear.Xamarin/BiometricPromptAuthenticationException.android.cs index 9efb711..e58f3f3 100644 --- a/Authgear.Xamarin/BiometricPromptAuthenticationException.android.cs +++ b/Authgear.Xamarin/BiometricPromptAuthenticationException.android.cs @@ -5,7 +5,7 @@ namespace Authgear.Xamarin { - internal class BiometricPromptAuthenticationException : Exception + public class BiometricPromptAuthenticationException : Exception { public int ErrorCode { get; private set; } public BiometricPromptAuthenticationException(string message) : base(message) { } diff --git a/Authgear.Xamarin/CodeVerifier.cs b/Authgear.Xamarin/CodeVerifier.cs index 8e298a2..4d87e76 100644 --- a/Authgear.Xamarin/CodeVerifier.cs +++ b/Authgear.Xamarin/CodeVerifier.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Linq; using System.Security.Cryptography; using System.Text; @@ -18,20 +19,20 @@ public CodeVerifier(RandomNumberGenerator generator) using (var provider = generator) { provider.GetBytes(bytes); - Verifier = string.Join("", bytes.Select(x => x.ToString("x2"))); + Verifier = string.Join("", bytes.Select(x => x.ToString("x2", CultureInfo.InvariantCulture))); } Challenge = ComputeCodeChallenge(Verifier); } - private string ComputeCodeChallenge(string verifier) + private static string ComputeCodeChallenge(string verifier) { var hash = Sha256(verifier); return ConvertExtensions.ToBase64UrlSafeString(hash); } - private byte[] Sha256(string input) + private static byte[] Sha256(string input) { - var sha256 = SHA256.Create(); + using var sha256 = SHA256.Create(); return sha256.ComputeHash(Encoding.UTF8.GetBytes(input)); } } diff --git a/Authgear.Xamarin/Data/Biometric.android.cs b/Authgear.Xamarin/Data/Biometric.android.cs index d2de313..92bd5e0 100644 --- a/Authgear.Xamarin/Data/Biometric.android.cs +++ b/Authgear.Xamarin/Data/Biometric.android.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Text; using System.Threading.Tasks; using Android.Content; @@ -14,6 +15,7 @@ namespace Authgear.Xamarin.Data { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "Xamarin objects are managed")] internal class Biometric : IBiometric { private const int BiometricOnly = BiometricManager.Authenticators.BiometricStrong; @@ -82,7 +84,7 @@ internal Biometric(Context context) this.context = context; } - private void EnsureApiLevel() + private static void EnsureApiLevel() { if (Build.VERSION.SdkInt < BuildVersionCodes.M) { @@ -102,17 +104,17 @@ public void EnsureIsSupported(BiometricOptions options) public void RemoveBiometric(string kid) { - var alias = string.Format(AliasFormat, kid); + var alias = string.Format(CultureInfo.InvariantCulture, AliasFormat, kid); RemovePrivateKey(alias); } - private void RemovePrivateKey(string alias) + private static void RemovePrivateKey(string alias) { var keystore = KeyStore.GetInstance(AndroidKeyStore)!; keystore.Load(null); keystore.DeleteEntry(alias); } - private KeyPair GetPrivateKey(string alias) + private static KeyPair GetPrivateKey(string alias) { var keyStore = KeyStore.GetInstance(AndroidKeyStore)!; keyStore.Load(null); @@ -146,7 +148,7 @@ public async Task EnableBiometricAsync(BiometricOptions o var optionsAn = options.Android; var promptInfo = BuildPromptInfo(optionsAn); var kid = Guid.NewGuid().ToString(); - var alias = string.Format(AliasFormat, kid); + var alias = string.Format(CultureInfo.InvariantCulture, AliasFormat, kid); var spec = MakeGenerateKeyPairSpec(alias, ToKeyPropertiesAuthType(optionsAn.AccessConstraint), optionsAn.InvalidatedByBiometricEnrollment); var keyPair = CreateKeyPair(spec); var jwk = Jwk.FromPublicKey(kid, keyPair.Public!); @@ -173,7 +175,7 @@ public async Task AuthenticateBiometricAsync(BiometricOptions options, s EnsureApiLevel(); EnsureCanAuthenticate(options); var promptInfo = BuildPromptInfo(options.Android); - var alias = string.Format(AliasFormat, kid); + var alias = string.Format(CultureInfo.InvariantCulture, AliasFormat, kid); try { var keyPair = GetPrivateKey(alias); @@ -196,13 +198,13 @@ public async Task AuthenticateBiometricAsync(BiometricOptions options, s } } - private PromptInfo BuildPromptInfo(BiometricOptionsAndroid options) + private static PromptInfo BuildPromptInfo(BiometricOptionsAndroid options) { var authenticators = ToAuthenticators(options.AccessConstraint); return BuildPromptInfo(options.Title, options.Subtitle, options.Description, options.NegativeButtonText, authenticators); } - private PromptInfo BuildPromptInfo(string? title, string? subtitle, string? description, string? negativeButtonText, int authenticators) + private static PromptInfo BuildPromptInfo(string? title, string? subtitle, string? description, string? negativeButtonText, int authenticators) { var builder = new PromptInfo.Builder() .SetTitle(title) @@ -219,7 +221,7 @@ private PromptInfo BuildPromptInfo(string? title, string? subtitle, string? desc return builder.Build(); } - private KeyGenParameterSpec MakeGenerateKeyPairSpec(string alias, KeyPropertiesAuthType type, bool invalidatedByBiometricEnrollment) + private static KeyGenParameterSpec MakeGenerateKeyPairSpec(string alias, KeyPropertiesAuthType type, bool invalidatedByBiometricEnrollment) { var builder = new KeyGenParameterSpec.Builder(alias, KeyStorePurpose.Sign | KeyStorePurpose.Verify) .SetKeySize(KeySize) @@ -228,7 +230,7 @@ private KeyGenParameterSpec MakeGenerateKeyPairSpec(string alias, KeyPropertiesA .SetUserAuthenticationRequired(true); if (Build.VERSION.SdkInt >= BuildVersionCodes.R) { - builder.SetUserAuthenticationParameters(0 /* 0 means require authentication every time */, Convert.ToInt32(type)); + builder.SetUserAuthenticationParameters(0 /* 0 means require authentication every time */, Convert.ToInt32(type, CultureInfo.InvariantCulture)); } if (Build.VERSION.SdkInt >= BuildVersionCodes.N) { @@ -265,14 +267,14 @@ private KeyGenParameterSpec MakeGenerateKeyPairSpec(string alias, KeyPropertiesA return builder.Build(); } - private KeyPair CreateKeyPair(KeyGenParameterSpec spec) + private static KeyPair CreateKeyPair(KeyGenParameterSpec spec) { var generator = KeyPairGenerator.GetInstance(KeyProperties.KeyAlgorithmRsa, AndroidKeyStore)!; generator.Initialize(spec); return generator.GenerateKeyPair()!; } - private Task Authenticate(PromptInfo promptInfo, CryptoObject cryptoObject, JwtHeader header, JwtPayload payload) + private static Task Authenticate(PromptInfo promptInfo, CryptoObject cryptoObject, JwtHeader header, JwtPayload payload) { var taskSource = new TaskCompletionSource(TaskCreationOptions.RunContinuationsAsynchronously); var prompt = new BiometricPrompt(Platform.CurrentActivity as FragmentActivity, new AuthenticationCallbackImpl(taskSource, header, payload)); diff --git a/Authgear.Xamarin/Data/Biometric.ios.cs b/Authgear.Xamarin/Data/Biometric.ios.cs index 6b33fd1..fee07dc 100644 --- a/Authgear.Xamarin/Data/Biometric.ios.cs +++ b/Authgear.Xamarin/Data/Biometric.ios.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Text; using System.Threading.Tasks; using Authgear.Xamarin.DeviceInfo; @@ -10,6 +11,7 @@ namespace Authgear.Xamarin.Data { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "Xamarin objects are managed")] internal class Biometric : IBiometric { private const string TagFormat = "com.authgear.keys.biometric.{0}"; @@ -41,7 +43,7 @@ internal Biometric() public Task AuthenticateBiometricAsync(BiometricOptions options, string kid, string challenge, DeviceInfoRoot deviceInfo) { EnsureIsSupported(options); - var tag = string.Format(TagFormat, kid); + var tag = string.Format(CultureInfo.InvariantCulture, TagFormat, kid); var record = new SecRecord(SecKind.Key) { KeyType = SecKeyType.RSA, @@ -75,10 +77,10 @@ public async Task EnableBiometricAsync(BiometricOptions o } EnsureIsSupported(options); var kid = Guid.NewGuid().ToString(); - var tag = string.Format(TagFormat, kid); + var tag = string.Format(CultureInfo.InvariantCulture, TagFormat, kid); var flags = ToFlags(options.Ios.AccessConstraint); var context = new LAContext(); - var (_, error) = await context.EvaluatePolicyAsync(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, options.Ios.LocalizedReason ?? ""); + var (_, error) = await context.EvaluatePolicyAsync(LAPolicy.DeviceOwnerAuthenticationWithBiometrics, options.Ios.LocalizedReason ?? "").ConfigureAwait(false); if (error != null) { throw AuthgearException.Wrap(new BiometricIosException(error)); @@ -112,7 +114,7 @@ public async Task EnableBiometricAsync(BiometricOptions o Jwt = jwt, }; } - private string SignJwt(string kid, SecKey privateKey, string challenge, string action, DeviceInfoRoot deviceInfo) + private static string SignJwt(string kid, SecKey privateKey, string challenge, string action, DeviceInfoRoot deviceInfo) { var jwk = Jwk.FromPrivateKey(kid, privateKey); var header = new JwtHeader @@ -127,7 +129,7 @@ private string SignJwt(string kid, SecKey privateKey, string challenge, string a return jwt; } - private void EnsureApiLevel() + private static void EnsureApiLevel() { if (!UIDevice.CurrentDevice.CheckSystemVersion(11, 3)) { @@ -148,7 +150,7 @@ public void EnsureIsSupported(BiometricOptions options) public void RemoveBiometric(string kid) { - var tag = string.Format(TagFormat, kid); + var tag = string.Format(CultureInfo.InvariantCulture, TagFormat, kid); var record = new SecRecord(SecKind.Key) { KeyType = SecKeyType.RSA, diff --git a/Authgear.Xamarin/Data/HttpResponseMessagExtensions.cs b/Authgear.Xamarin/Data/HttpResponseMessagExtensions.cs index 27fa108..7a558e1 100644 --- a/Authgear.Xamarin/Data/HttpResponseMessagExtensions.cs +++ b/Authgear.Xamarin/Data/HttpResponseMessagExtensions.cs @@ -46,8 +46,8 @@ public static async Task EnsureSuccessOrAuthgearExceptionAsync(this HttpResponse } public static async Task GetJsonAsync(this HttpResponseMessage responseMessage) { - await responseMessage.EnsureSuccessOrAuthgearExceptionAsync(); - var responseStream = await responseMessage.Content.ReadAsStreamAsync(); + await responseMessage.EnsureSuccessOrAuthgearExceptionAsync().ConfigureAwait(false); + var responseStream = await responseMessage.Content.ReadAsStreamAsync().ConfigureAwait(false); return JsonSerializer.Deserialize(responseStream)!; } } diff --git a/Authgear.Xamarin/Data/KeyRepo.android.cs b/Authgear.Xamarin/Data/KeyRepo.android.cs index 57214e5..113bc13 100644 --- a/Authgear.Xamarin/Data/KeyRepo.android.cs +++ b/Authgear.Xamarin/Data/KeyRepo.android.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Text; using System.Threading.Tasks; using Android.Security.Keystore; @@ -9,6 +10,7 @@ namespace Authgear.Xamarin { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "Xamarin objects are managed")] internal class KeyRepo : IKeyRepo { private const string AliasFormat = "com.authgear.keys.anonymous.{0}"; @@ -67,9 +69,9 @@ public static Signature MakeSignature(IPrivateKey privateKey) return signature; } - private KeyPair GenerateAnonymousKey(string keyId) + private static KeyPair GenerateAnonymousKey(string keyId) { - string alias = string.Format(AliasFormat, keyId); + string alias = string.Format(CultureInfo.InvariantCulture, AliasFormat, keyId); var kpg = KeyPairGenerator.GetInstance(KeyProperties.KeyAlgorithmRsa, AndroidKeyStore)!; var spec = new KeyGenParameterSpec.Builder(alias, KeyStorePurpose.Sign | KeyStorePurpose.Verify) .SetDigests(KeyProperties.DigestSha256) @@ -79,9 +81,9 @@ private KeyPair GenerateAnonymousKey(string keyId) return kpg.GenerateKeyPair()!; } - private KeyPair GetAnonymousKey(string keyId) + private static KeyPair GetAnonymousKey(string keyId) { - var alias = string.Format(AliasFormat, keyId); + var alias = string.Format(CultureInfo.InvariantCulture, AliasFormat, keyId); var ks = KeyStore.GetInstance(AndroidKeyStore)!; ks.Load(null); var entry = ks.GetEntry(alias, null); diff --git a/Authgear.Xamarin/Data/KeyRepo.ios.cs b/Authgear.Xamarin/Data/KeyRepo.ios.cs index bce5aac..1407ba8 100644 --- a/Authgear.Xamarin/Data/KeyRepo.ios.cs +++ b/Authgear.Xamarin/Data/KeyRepo.ios.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Text; using System.Threading.Tasks; using Authgear.Xamarin.DeviceInfo; @@ -8,6 +9,7 @@ namespace Authgear.Xamarin.Data { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "Xamarin Interop objects are managed")] internal class KeyRepo : IKeyRepo { private const string TagFormat = "com.authgear.keys.biometric.{0}"; @@ -20,12 +22,12 @@ public Task GetOrCreateAnonymousJwtAsync(string keyId, string chal if (keyId == null) { keyId = Guid.NewGuid().ToString(); - var tag = string.Format(TagFormat, keyId); + var tag = string.Format(CultureInfo.InvariantCulture, TagFormat, keyId); privateKey = GeneratePrivateKey(tag); } else { - var tag = string.Format(TagFormat, keyId); + var tag = string.Format(CultureInfo.InvariantCulture, TagFormat, keyId); privateKey = GetPrivateKey(tag); if (privateKey == null) { @@ -54,7 +56,7 @@ public Task PromoteAnonymousUserAsync(string keyId, string challenge, De throw new NotImplementedException(); } - private void EnsureApiLevel() + private static void EnsureApiLevel() { if (!UIDevice.CurrentDevice.CheckSystemVersion(11, 3)) { @@ -62,7 +64,7 @@ private void EnsureApiLevel() } } - private SecKey GeneratePrivateKey(string tag) + private static SecKey GeneratePrivateKey(string tag) { var keyGenParam = new SecKeyGenerationParameters() { @@ -86,7 +88,7 @@ private SecKey GeneratePrivateKey(string tag) return privateKey; } - private SecKey? GetPrivateKey(string tag) + private static SecKey? GetPrivateKey(string tag) { var secRecord = new SecRecord(SecKind.Key) { diff --git a/Authgear.Xamarin/Data/Oauth/OauthRepoHttp.cs b/Authgear.Xamarin/Data/Oauth/OauthRepoHttp.cs index 81fa452..f2615fc 100644 --- a/Authgear.Xamarin/Data/Oauth/OauthRepoHttp.cs +++ b/Authgear.Xamarin/Data/Oauth/OauthRepoHttp.cs @@ -56,8 +56,8 @@ public async Task BiometricSetupRequestAsync(string accessToken, string clientId ["grant_type"] = GrantType.Biometric.GetDescription(), ["jwt"] = jwt }; - var content = new FormUrlEncodedContent(body); - var request = new HttpRequestMessage(HttpMethod.Post, config.TokenEndpoint); + using var content = new FormUrlEncodedContent(body); + using var request = new HttpRequestMessage(HttpMethod.Post, config.TokenEndpoint); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); request.Content = content; var responseMessage = await httpClient.SendAsync(request).ConfigureAwait(false); @@ -70,7 +70,7 @@ public async Task OauthAppSessionTokenAsync(string refr { ["refresh_token"] = refreshToken, }; - var content = new StringContent(JsonSerializer.Serialize(body), Encoding.UTF8, "application/json"); + using var content = new StringContent(JsonSerializer.Serialize(body), Encoding.UTF8, "application/json"); var responseMessage = await httpClient.PostAsync(new Uri(new Uri(Endpoint), "/oauth2/app_session_token"), content).ConfigureAwait(false); var result = await responseMessage.GetJsonAsync().ConfigureAwait(false); return result.Result!; @@ -82,7 +82,7 @@ public async Task OauthChallengeAsync(string purpose) { ["purpose"] = purpose }; - var content = new StringContent(JsonSerializer.Serialize(body), Encoding.UTF8, "application/json"); + using var content = new StringContent(JsonSerializer.Serialize(body), Encoding.UTF8, "application/json"); var responseMessage = await httpClient.PostAsync(new Uri(new Uri(Endpoint), "/oauth2/challenge"), content).ConfigureAwait(false); var result = await responseMessage.GetJsonAsync().ConfigureAwait(false); return result.Result!; @@ -95,8 +95,8 @@ public async Task OidcRevocationRequestAsync(string refreshToken) { ["token"] = refreshToken }; - var content = new FormUrlEncodedContent(body); - var responseMessage = await httpClient.PostAsync(config.RevocationEndpoint, content).ConfigureAwait(false); + using var content = new FormUrlEncodedContent(body); + var responseMessage = await httpClient.PostAsync(new Uri(config.RevocationEndpoint), content).ConfigureAwait(false); await responseMessage.EnsureSuccessOrAuthgearExceptionAsync().ConfigureAwait(false); } @@ -129,8 +129,8 @@ public async Task OidcTokenRequestAsync(OidcTokenRequest requ { body["jwt"] = request.Jwt; } - var content = new FormUrlEncodedContent(body); - var httpRequest = new HttpRequestMessage(HttpMethod.Post, config.TokenEndpoint); + using var content = new FormUrlEncodedContent(body); + using var httpRequest = new HttpRequestMessage(HttpMethod.Post, config.TokenEndpoint); if (request.AccessToken != null) { httpRequest.Headers.Authorization = new AuthenticationHeaderValue("Bearer", request.AccessToken); @@ -143,7 +143,7 @@ public async Task OidcTokenRequestAsync(OidcTokenRequest requ public async Task OidcUserInfoRequestAsync(string accessToken) { var config = await GetOidcConfigurationAsync().ConfigureAwait(false); - var request = new HttpRequestMessage(HttpMethod.Get, config.UserInfoEndpoint); + using var request = new HttpRequestMessage(HttpMethod.Get, config.UserInfoEndpoint); request.Headers.Authorization = new AuthenticationHeaderValue("Bearer", accessToken); var responseMessage = await httpClient.SendAsync(request).ConfigureAwait(false); return await responseMessage.GetJsonAsync().ConfigureAwait(false); diff --git a/Authgear.Xamarin/Data/PersistentContainerStorage.cs b/Authgear.Xamarin/Data/PersistentContainerStorage.cs index 69b7dea..5f32b4c 100644 --- a/Authgear.Xamarin/Data/PersistentContainerStorage.cs +++ b/Authgear.Xamarin/Data/PersistentContainerStorage.cs @@ -8,7 +8,6 @@ namespace Authgear.Xamarin.Data { internal class PersistentContainerStorage : IContainerStorage { - private const string KeyVerifier = "verifier"; private const string KeyAnonymousId = "anonymousId"; private const string KeyBiometricKeyId = "biometricKeyId"; public void DeleteAnonymousKeyId(string aNamespace) diff --git a/Authgear.Xamarin/DeviceInfo/DeviceInfoAndroid.android.cs b/Authgear.Xamarin/DeviceInfo/DeviceInfoAndroid.android.cs index 22fba56..8d3fa06 100644 --- a/Authgear.Xamarin/DeviceInfo/DeviceInfoAndroid.android.cs +++ b/Authgear.Xamarin/DeviceInfo/DeviceInfoAndroid.android.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Globalization; using System.Text; using Android.Content; using Android.Content.PM; @@ -22,12 +23,12 @@ public static DeviceInfoAndroid Get(Context context) { baseOs = AndroidBuild.VERSION.BaseOs ?? ""; securityPatch = AndroidBuild.VERSION.SecurityPatch ?? ""; - previewSdkInt = AndroidBuild.VERSION.PreviewSdkInt.ToString(); + previewSdkInt = AndroidBuild.VERSION.PreviewSdkInt.ToString(CultureInfo.InvariantCulture); } var longVersionCode = ""; if (AndroidBuild.VERSION.SdkInt >= Android.OS.BuildVersionCodes.P) { - longVersionCode = packageInfo.LongVersionCode.ToString(); + longVersionCode = packageInfo.LongVersionCode.ToString(CultureInfo.InvariantCulture); } var releaseOrCodeName = ""; if (AndroidBuild.VERSION.SdkInt >= Android.OS.BuildVersionCodes.R) @@ -66,7 +67,7 @@ public static DeviceInfoAndroid Get(Context context) PackageName = context.PackageName ?? "", VersionName = packageInfo.VersionName ?? "", #pragma warning disable CS0618 // Type or member is obsolete - VersionCode = packageInfo.VersionCode.ToString(), + VersionCode = packageInfo.VersionCode.ToString(CultureInfo.InvariantCulture), #pragma warning restore CS0618 // Type or member is obsolete LongVersionCode = longVersionCode }, diff --git a/Authgear.Xamarin/DeviceInfo/DeviceInfoIos.ios.cs b/Authgear.Xamarin/DeviceInfo/DeviceInfoIos.ios.cs index 9fa9020..85624e1 100644 --- a/Authgear.Xamarin/DeviceInfo/DeviceInfoIos.ios.cs +++ b/Authgear.Xamarin/DeviceInfo/DeviceInfoIos.ios.cs @@ -10,12 +10,14 @@ namespace Authgear.Xamarin.DeviceInfo internal partial class DeviceInfoIos { [DllImport(Constants.SystemLibrary, EntryPoint = "sysctlbyname")] +#pragma warning disable CA2101 // Specify marshaling for P/Invoke string arguments internal static extern int SysctlByName([MarshalAs(UnmanagedType.LPStr)] string property, IntPtr output, IntPtr oldLen, IntPtr newp, uint newlen); +#pragma warning restore CA2101 // Specify marshaling for P/Invoke string arguments internal static string GetSystemLibraryProperty(string property) { var lengthPtr = Marshal.AllocHGlobal(sizeof(int)); - SysctlByName(property, IntPtr.Zero, lengthPtr, IntPtr.Zero, 0); + _ = SysctlByName(property, IntPtr.Zero, lengthPtr, IntPtr.Zero, 0); var propertyLength = Marshal.ReadInt32(lengthPtr); @@ -26,7 +28,7 @@ internal static string GetSystemLibraryProperty(string property) } var valuePtr = Marshal.AllocHGlobal(propertyLength); - SysctlByName(property, valuePtr, lengthPtr, IntPtr.Zero, 0); + _ = SysctlByName(property, valuePtr, lengthPtr, IntPtr.Zero, 0); var returnValue = Marshal.PtrToStringAnsi(valuePtr); diff --git a/Authgear.Xamarin/Oauth/OidcAuthenticationRequest.cs b/Authgear.Xamarin/Oauth/OidcAuthenticationRequest.cs index eb64961..fd3a3db 100644 --- a/Authgear.Xamarin/Oauth/OidcAuthenticationRequest.cs +++ b/Authgear.Xamarin/Oauth/OidcAuthenticationRequest.cs @@ -1,5 +1,6 @@ using System; using System.Collections.Generic; +using System.Collections.ObjectModel; using System.Linq; using System.Runtime.CompilerServices; using System.Text; @@ -14,10 +15,10 @@ internal class OidcAuthenticationRequest public string ResponseType { get; set; } public List Scope { get; set; } public string? State { get; set; } - public List? Prompt { get; set; } + public IReadOnlyCollection? Prompt { get; set; } public int? MaxAge { get; set; } public string? LoginHint { get; set; } - public List? UiLocales { get; set; } + public IReadOnlyCollection? UiLocales { get; set; } public ColorScheme? ColorScheme { get; set; } public string? IdTokenHint { get; set; } public AuthenticatePage? Page { get; set; } diff --git a/Authgear.Xamarin/OauthException.cs b/Authgear.Xamarin/OauthException.cs index 289ada3..980eacc 100644 --- a/Authgear.Xamarin/OauthException.cs +++ b/Authgear.Xamarin/OauthException.cs @@ -4,7 +4,7 @@ namespace Authgear.Xamarin { - internal class OauthException : Exception + public class OauthException : Exception { static internal string FormatOauthExceptionMessage(string error, string? errorDescription) { diff --git a/Authgear.Xamarin/PromoteOptions.cs b/Authgear.Xamarin/PromoteOptions.cs index 1e0780f..c44eaf6 100644 --- a/Authgear.Xamarin/PromoteOptions.cs +++ b/Authgear.Xamarin/PromoteOptions.cs @@ -9,7 +9,7 @@ public class PromoteOptions { public string RedirectUri { get; set; } public string? State { get; set; } - public List? UiLocales { get; set; } + public IReadOnlyCollection? UiLocales { get; set; } public ColorScheme? ColorScheme { get; set; } public PromoteOptions(string redirectUri) diff --git a/Authgear.Xamarin/PropertyNameAttribute.cs b/Authgear.Xamarin/PropertyNameAttribute.cs deleted file mode 100644 index 2658aef..0000000 --- a/Authgear.Xamarin/PropertyNameAttribute.cs +++ /dev/null @@ -1,8 +0,0 @@ -using System; - -namespace Authgear.Xamarin -{ - internal class PropertyNameAttribute : Attribute - { - } -} \ No newline at end of file diff --git a/Authgear.Xamarin/ReauthenticateOptions.cs b/Authgear.Xamarin/ReauthenticateOptions.cs index ae6e7ca..aa786ec 100644 --- a/Authgear.Xamarin/ReauthenticateOptions.cs +++ b/Authgear.Xamarin/ReauthenticateOptions.cs @@ -9,7 +9,7 @@ public class ReauthenticateOptions { public string RedirectUri { get; set; } public string? State { get; set; } - public List? UiLocales { get; set; } + public IReadOnlyCollection? UiLocales { get; set; } public ColorScheme? ColorScheme { get; set; } public int? MaxAge { get; set; } public ReauthenticateOptions(string redirectUri) diff --git a/Authgear.Xamarin/ReauthenticateResult.cs b/Authgear.Xamarin/ReauthenticateResult.cs deleted file mode 100644 index c16ce79..0000000 --- a/Authgear.Xamarin/ReauthenticateResult.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace Authgear.Xamarin -{ - public class ReauthenticateResult - { - public UserInfo UserInfo { get; } - public string State { get; } - internal ReauthenticateResult(UserInfo userInfo, string state) - { - UserInfo = userInfo; - State = state; - } - } -} diff --git a/Authgear.Xamarin/ServerException.cs b/Authgear.Xamarin/ServerException.cs index e96182b..46ead3e 100644 --- a/Authgear.Xamarin/ServerException.cs +++ b/Authgear.Xamarin/ServerException.cs @@ -5,7 +5,7 @@ namespace Authgear.Xamarin { - internal class ServerException : Exception + public class ServerException : Exception { public string Name { get; private set; } public string Reason { get; private set; } diff --git a/Authgear.Xamarin/SettingsOptions.cs b/Authgear.Xamarin/SettingsOptions.cs index f2f7f7a..7dff8e7 100644 --- a/Authgear.Xamarin/SettingsOptions.cs +++ b/Authgear.Xamarin/SettingsOptions.cs @@ -8,7 +8,7 @@ namespace Authgear.Xamarin public class SettingsOptions { public ColorScheme? ColorScheme { get; set; } - public List? UiLocales { get; set; } + public IReadOnlyCollection? UiLocales { get; set; } internal OidcAuthenticationRequest ToRequest(string url, string loginHint, bool suppressIdpSessionCookie) { return new OidcAuthenticationRequest(url, "none", new List { "openid", "offline_access", "https://authgear.com/scopes/full-access" }) diff --git a/Authgear.Xamarin/UserInfo.cs b/Authgear.Xamarin/UserInfo.cs index 3f11825..daeebbd 100644 --- a/Authgear.Xamarin/UserInfo.cs +++ b/Authgear.Xamarin/UserInfo.cs @@ -10,8 +10,8 @@ public class UserInfo [JsonPropertyName("sub")] public string Sub { get; set; } = ""; [JsonPropertyName("https://authgear.com/claims/user/is_anonymous")] - public bool? IsAnonymous { get; set; } + public bool IsAnonymous { get; set; } [JsonPropertyName("https://authgear.com/claims/user/is_verified")] - public bool? IsVerified { get; set; } + public bool IsVerified { get; set; } } } diff --git a/Authgear.Xamarin/WebView.android.cs b/Authgear.Xamarin/WebView.android.cs index a85ae5f..d269602 100644 --- a/Authgear.Xamarin/WebView.android.cs +++ b/Authgear.Xamarin/WebView.android.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Diagnostics; +using System.Globalization; using System.Text; using System.Threading.Tasks; using Android.Content; @@ -12,6 +13,7 @@ namespace Authgear.Xamarin { + [System.Diagnostics.CodeAnalysis.SuppressMessage("Reliability", "CA2000:Dispose objects before losing scope", Justification = "Xamarin Interop objects are managed")] internal class WebView : IWebView { private const string HookFragmentTagFormat = "hookFragment.{0}"; @@ -74,15 +76,16 @@ public WebView() Debug.WriteLine("Calling ShowAsync or initiating authgear without a valid activity in use, fragment setup ignored."); return null; } - var fragment = activity.SupportFragmentManager.FindFragmentByTag(string.Format(HookFragmentTagFormat, TagGuid)) as HookFragment; + var fragment = activity.SupportFragmentManager.FindFragmentByTag(string.Format(CultureInfo.InvariantCulture, HookFragmentTagFormat, TagGuid)) as HookFragment; if (fragment != null) { return fragment; } fragment = new HookFragment { Owner = new WeakReference(this) }; - activity.SupportFragmentManager.BeginTransaction().Add(fragment, string.Format(HookFragmentTagFormat, TagGuid)).CommitNow(); + activity.SupportFragmentManager.BeginTransaction().Add(fragment, string.Format(CultureInfo.InvariantCulture, HookFragmentTagFormat, TagGuid)).CommitNow(); return fragment; } + public async Task ShowAsync(string url) { var taskSource = new TaskCompletionSource(); @@ -98,7 +101,7 @@ public async Task ShowAsync(string url) var intent = new CustomTabsIntent.Builder() .Build(); intent.LaunchUrl(Platform.CurrentActivity, Android.Net.Uri.Parse(url)); - await taskSource.Task; + await taskSource.Task.ConfigureAwait(false); } } } diff --git a/Authgear.Xamarin/WebView.ios.cs b/Authgear.Xamarin/WebView.ios.cs index 31021f2..c556c4b 100644 --- a/Authgear.Xamarin/WebView.ios.cs +++ b/Authgear.Xamarin/WebView.ios.cs @@ -17,7 +17,7 @@ public async Task ShowAsync(string url) Url = new Uri(url), CallbackUrl = new Uri("nocallback:///"), PrefersEphemeralWebBrowserSession = true - }); + }).ConfigureAwait(false); } catch (OperationCanceledException) {