Skip to content

Commit

Permalink
Add .NET analyzer
Browse files Browse the repository at this point in the history
ref #44
  • Loading branch information
louischan-oursky committed Jun 2, 2022
2 parents b5c05a3 + 8e11a3f commit 4e0f493
Show file tree
Hide file tree
Showing 36 changed files with 336 additions and 244 deletions.
40 changes: 39 additions & 1 deletion Authgear.Xamarin/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -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
csharp_new_line_before_finally = true

# CA1032: Implement standard exception constructors
dotnet_diagnostic.CA1032.severity = none

# CA1031: Do not catch general exception types
dotnet_diagnostic.CA1031.severity = none

# CA1307: Specify StringComparison for clarity
# Some methods are not available in netstandard
dotnet_diagnostic.CA1307.severity = none

# CA1056: URI-like properties should not be strings
dotnet_diagnostic.CA1056.severity = none

# CA2227: Collection properties should be read only
dotnet_diagnostic.CA2227.severity = none

# CA1003: Use generic event handler instances
dotnet_diagnostic.CA1003.severity = none

# CA1062: Validate arguments of public methods
# Not needed with NRT
dotnet_diagnostic.CA1062.severity = none

# CA2208: Instantiate argument exceptions correctly
dotnet_diagnostic.CA2208.severity = none

# CA2208: Type names should not match namespaces
dotnet_diagnostic.CA1724.severity = none

# CA1054: URI-like parameters should not be strings
dotnet_diagnostic.CA1054.severity = none

# CA2237: Mark ISerializable types with SerializableAttribute
dotnet_diagnostic.CA2237.severity = none

# CA1014: Mark assemblies with CLSCompliantAttribute
dotnet_diagnostic.CA1014.severity = none
3 changes: 2 additions & 1 deletion Authgear.Xamarin/AnonymousUserIosException.ios.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
using System;
using System.Globalization;
using Foundation;
using Security;

Expand All @@ -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)))
{

}
Expand Down
2 changes: 1 addition & 1 deletion Authgear.Xamarin/AnonymousUserNotFoundException.cs
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

namespace Authgear.Xamarin
{
internal class AnonymousUserNotFoundException : Exception
public class AnonymousUserNotFoundException : Exception
{
public AnonymousUserNotFoundException() : base() { }
}
Expand Down
5 changes: 3 additions & 2 deletions Authgear.Xamarin/AuthenticateOptions.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Text;
using Authgear.Xamarin.Oauth;

Expand All @@ -9,9 +10,9 @@ public class AuthenticateOptions
{
public string RedirectUri { get; set; }
public string? State { get; set; }
public List<PromptOption>? PromptOptions { get; set; }
public IReadOnlyCollection<PromptOption>? PromptOptions { get; set; }
public string? LoginHint { get; set; }
public List<string>? UiLocales { get; set; }
public IReadOnlyCollection<string>? UiLocales { get; set; }
public ColorScheme? ColorScheme { get; set; }
public AuthenticatePage? Page { get; set; }

Expand Down
4 changes: 3 additions & 1 deletion Authgear.Xamarin/Authgear.Xamarin.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,16 @@
<PackageLicenseFile>LICENSE</PackageLicenseFile>
<PackageRequireLicenseAcceptance>true</PackageRequireLicenseAcceptance>
<PackageProjectUrl>https://github.com/authgear/authgear-sdk-xamarin</PackageProjectUrl>
<!-- TODO: Can we use 9.0 for init only setters? -->
<LangVersion>8.0</LangVersion>
<Nullable>enable</Nullable>
<AnalysisMode>AllEnabledByDefault</AnalysisMode>
<TreatWarningsAsErrors>true</TreatWarningsAsErrors>
</PropertyGroup>

<ItemGroup>
<PackageReference Include="System.Text.Json" Version="6.0.3" />
<PackageReference Include="Xamarin.Essentials" Version="1.7.2" />
<PackageReference Include="Microsoft.CodeAnalysis.NetAnalyzers" Version="6.0.0" PrivateAssets="all" />
</ItemGroup>

<ItemGroup>
Expand Down
2 changes: 1 addition & 1 deletion Authgear.Xamarin/AuthgearException.netstandard.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ namespace Authgear.Xamarin
{
public partial class AuthgearException
{
internal static Exception? PlatformWrap(Exception ex)
internal static Exception? PlatformWrap(Exception _)
{
return null;
}
Expand Down
163 changes: 86 additions & 77 deletions Authgear.Xamarin/AuthgearSdk.cs

Large diffs are not rendered by default.

5 changes: 4 additions & 1 deletion Authgear.Xamarin/AuthgearSdk.ios.cs
Original file line number Diff line number Diff line change
Expand Up @@ -13,13 +13,16 @@ public partial class AuthgearSdk
/// </summary>
/// <param name="app">Dummy tag argument to denote this constructor is for ios</param>
/// <param name="options"></param>
public AuthgearSdk(UIApplication app, AuthgearOptions options) : this(options)
public AuthgearSdk(UIApplication _, AuthgearOptions options) : this(options)
{
biometric = new Biometric();
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
{
Expand Down
3 changes: 2 additions & 1 deletion Authgear.Xamarin/BiometricIosException.ios.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using Foundation;
using Security;
Expand All @@ -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)))
{

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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) { }
Expand Down
9 changes: 5 additions & 4 deletions Authgear.Xamarin/CodeVerifier.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Linq;
using System.Security.Cryptography;
using System.Text;
Expand All @@ -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));
}
}
Expand Down
30 changes: 16 additions & 14 deletions Authgear.Xamarin/Data/Biometric.android.cs
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.Text;
using System.Threading.Tasks;
using Android.Content;
Expand All @@ -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;
Expand Down Expand Up @@ -82,7 +84,7 @@ internal Biometric(Context context)
this.context = context;
}

private void EnsureApiLevel()
private static void EnsureApiLevel()
{
if (Build.VERSION.SdkInt < BuildVersionCodes.M)
{
Expand All @@ -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);
Expand Down Expand Up @@ -146,7 +148,7 @@ public async Task<BiometricEnableResult> 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!);
Expand All @@ -160,7 +162,7 @@ public async Task<BiometricEnableResult> EnableBiometricAsync(BiometricOptions o
var payload = new JwtPayload(DateTimeOffset.Now, challenge, "setup", deviceInfo);
var lockedSignature = KeyRepo.MakeSignature(keyPair.Private!);
var cryptoObject = new CryptoObject(lockedSignature);
var jwt = await Authenticate(promptInfo, cryptoObject, header, payload);
var jwt = await Authenticate(promptInfo, cryptoObject, header, payload).ConfigureAwait(false);
return new BiometricEnableResult { Kid = kid, Jwt = jwt };
}

Expand All @@ -173,7 +175,7 @@ public async Task<string> 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);
Expand All @@ -188,21 +190,21 @@ public async Task<string> AuthenticateBiometricAsync(BiometricOptions options, s
var payload = new JwtPayload(DateTimeOffset.Now, challenge, "authenticate", deviceInfo);
var lockedSignature = KeyRepo.MakeSignature(keyPair.Private!);
var cryptoObject = new CryptoObject(lockedSignature);
return await Authenticate(promptInfo, cryptoObject, header, payload);
return await Authenticate(promptInfo, cryptoObject, header, payload).ConfigureAwait(false);
}
catch (Exception ex)
{
throw AuthgearException.Wrap(ex);
}
}

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)
Expand All @@ -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)
Expand All @@ -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)
{
Expand Down Expand Up @@ -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<string> Authenticate(PromptInfo promptInfo, CryptoObject cryptoObject, JwtHeader header, JwtPayload payload)
private static Task<string> Authenticate(PromptInfo promptInfo, CryptoObject cryptoObject, JwtHeader header, JwtPayload payload)
{
var taskSource = new TaskCompletionSource<string>(TaskCreationOptions.RunContinuationsAsynchronously);
var prompt = new BiometricPrompt(Platform.CurrentActivity as FragmentActivity, new AuthenticationCallbackImpl(taskSource, header, payload));
Expand Down
Loading

0 comments on commit 4e0f493

Please sign in to comment.