Skip to content

Commit

Permalink
Add .NET analyzer authgear#44
Browse files Browse the repository at this point in the history
  • Loading branch information
roxk committed Jun 1, 2022
1 parent 23e94cf commit 77eefc0
Show file tree
Hide file tree
Showing 26 changed files with 127 additions and 75 deletions.
37 changes: 36 additions & 1 deletion Authgear.Xamarin/.editorconfig
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,39 @@ 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 = 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
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 @@ -10,9 +11,9 @@ public class AuthenticateOptions
public string? RedirectUri { get; set; }
public string? State { get; set; }
public string? ResponseType { get; set; }
public List<PromptOption>? PromptOptions { get; set; }
public ReadOnlyCollection<PromptOption>? PromptOptions { get; set; }
public string? LoginHint { get; set; }
public List<string>? UiLocales { get; set; }
public ReadOnlyCollection<string>? UiLocales { get; set; }
public ColorScheme? ColorScheme { get; set; }
public AuthenticatePage? Page { get; set; }

Expand Down
3 changes: 2 additions & 1 deletion Authgear.Xamarin/Authgear.Xamarin.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -19,14 +19,15 @@
<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>
</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
15 changes: 9 additions & 6 deletions Authgear.Xamarin/AuthgearSdk.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.Net;
using System.Net.Http;
Expand Down Expand Up @@ -63,9 +64,9 @@ public SessionState SessionState
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<SessionStateChangeReason> SessionStateChange;
private bool ShouldSuppressIDPSessionCookie
{
Expand All @@ -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
Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -538,10 +541,10 @@ public async Task<UserInfo> 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);
}
Expand Down
3 changes: 3 additions & 0 deletions Authgear.Xamarin/AuthgearSdk.ios.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
{
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
26 changes: 14 additions & 12 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 @@ -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 @@ -196,13 +198,13 @@ public async Task<string> 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)
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
12 changes: 7 additions & 5 deletions Authgear.Xamarin/Data/Biometric.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 System.Threading.Tasks;
using Authgear.Xamarin.DeviceInfo;
Expand All @@ -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}";
Expand Down Expand Up @@ -41,7 +43,7 @@ internal Biometric()
public Task<string> 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,
Expand Down Expand Up @@ -75,7 +77,7 @@ public async Task<BiometricEnableResult> 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 ?? "");
Expand Down Expand Up @@ -112,7 +114,7 @@ public async Task<BiometricEnableResult> 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
Expand All @@ -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))
{
Expand All @@ -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,
Expand Down
Loading

0 comments on commit 77eefc0

Please sign in to comment.