Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
Commits
Show all changes
32 commits
Select commit Hold shift + click to select a range
a3e4fe5
Add shopify provider (#326)
sbiaudet Jul 21, 2019
6807cf1
Shopify - Fix TokenEndpoint format (#330)
sbiaudet Jul 22, 2019
86f0878
Gitlab (#329)
twsl Jul 23, 2019
81d383e
incorrect LoginPath
mgroves Sep 3, 2019
a652881
Basic Apple provider
martincostello Jun 6, 2019
c78b5e5
Implement Apple provider
martincostello Jun 8, 2019
b4b76d0
Enable Sign In with Apple
martincostello Jun 8, 2019
62d4e79
Update tests
martincostello Jun 8, 2019
06dfc7b
Enable token lifetime validation
martincostello Jun 8, 2019
4a9f230
Add null annotations
martincostello Jun 9, 2019
2c6a89c
Improve exception handling
martincostello Jun 9, 2019
cf3705a
Extend integration tests
martincostello Jun 9, 2019
c65e54f
Move expiry period to options
martincostello Jun 9, 2019
58e642c
Add ClientSecretExpiresAfter validation
martincostello Jun 9, 2019
93965e3
Add tests for options validation
martincostello Jun 9, 2019
fc95923
Add unit tests for client secret
martincostello Jun 9, 2019
38e71ab
Make KeyId required
martincostello Jun 9, 2019
0a55575
Fix test
martincostello Jun 9, 2019
991356d
Fix Linux and macOS secret generation
martincostello Jun 9, 2019
b660372
Add password option for pfx files
martincostello Jun 9, 2019
938e9ad
Fix flaky test
martincostello Jun 9, 2019
56abbbf
Add UsePrivateKey() method
martincostello Jun 9, 2019
d2e2f24
Bump System.IdentityModel.Tokens.Jwt
martincostello Jun 9, 2019
0d2f184
Set response_mode to form_post
martincostello Sep 8, 2019
2dcfb65
Use latest C# version
martincostello Sep 8, 2019
d083871
Retrieve user details after sign-in
martincostello Sep 8, 2019
88c8faa
Update branding
martincostello Sep 15, 2019
221bfeb
Access events via options
martincostello Sep 15, 2019
8ee2f76
Resolve logging TODO
martincostello Sep 15, 2019
15078f4
Comment out Apple option
martincostello Sep 20, 2019
bb83201
Merge branch 'dev' into Sign-In-With-Apple-300
martincostello Sep 20, 2019
99e3185
Update Sign in with Apple provider for ASP.NET Core 3.0
martincostello Sep 20, 2019
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Prev Previous commit
Next Next commit
Add password option for pfx files
Add an option for specifying a password for PFX files.
Add a test private key that has a password for use on macOS.
  • Loading branch information
martincostello committed Sep 20, 2019
commit b660372a8f14ceff8ee088250d92a97363401b0c
4 changes: 4 additions & 0 deletions .gitattributes
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,10 @@
###############################################################################
* text=auto

*.cer binary
*.p8 binary
*.pfx binary

###############################################################################
# Set default behavior for command prompt diff.
#
Expand Down
5 changes: 5 additions & 0 deletions src/AspNet.Security.OAuth.Apple/AppleAuthenticationOptions.cs
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ public AppleAuthenticationOptions()
/// </remarks>
public Func<string, Task<byte[]>> PrivateKeyBytes { get; set; }

/// <summary>
/// Gets or sets the password/passphrase associated with the private key, if any.
/// </summary>
public string PrivateKeyPassword { get; set; } = string.Empty;

/// <summary>
/// Gets or sets the Team ID for your Apple Developer account.
/// </summary>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,7 @@ public override async Task<string> GenerateAsync([NotNull] AppleGenerateClientSe
byte[] keyBlob = await _keyStore.LoadPrivateKeyAsync(context);
string clientSecret;

using (var algorithm = CreateAlgorithm(keyBlob))
using (var algorithm = CreateAlgorithm(keyBlob, context.Options.PrivateKeyPassword))
{
tokenDescriptor.SigningCredentials = CreateSigningCredentials(context.Options.KeyId, algorithm);

Expand All @@ -93,18 +93,19 @@ public override async Task<string> GenerateAsync([NotNull] AppleGenerateClientSe
return (clientSecret, expiresAt);
}

private ECDsa CreateAlgorithm(byte[] keyBlob)
private ECDsa CreateAlgorithm(byte[] keyBlob, string password)
{
// This becomes xplat in .NET Core 3.0: https://github.com/dotnet/corefx/pull/30271
return RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
CreateAlgorithmWindows(keyBlob) :
CreateAlgorithmLinuxOrMac(keyBlob);
CreateAlgorithmLinuxOrMac(keyBlob, password);
}

private ECDsa CreateAlgorithmLinuxOrMac(byte[] keyBlob)
private ECDsa CreateAlgorithmLinuxOrMac(byte[] keyBlob, string password)
{
// Does not support .p8 files in .NET Core 2.x as-per https://github.com/dotnet/corefx/issues/18733#issuecomment-296723615
using (var cert = new X509Certificate2(keyBlob, string.Empty))
// Unlike Linux, macOS does not support empty passwords for .pfx files.
using (var cert = new X509Certificate2(keyBlob, password))
{
return cert.GetECDsaPrivateKey();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
using System;
using System.IdentityModel.Tokens.Jwt;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Authentication;
using Microsoft.AspNetCore.Builder;
Expand All @@ -21,11 +20,6 @@ namespace AspNet.Security.OAuth.Apple
{
public static class AppleClientSecretGeneratorTests
{
internal static readonly byte[] TestPrivateKey =
RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ?
Convert.FromBase64String("MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgU208KCg/doqiSzsVF5sknVtYSgt8/3oiYGbvryIRrzSgCgYIKoZIzj0DAQehRANCAAQfrvDWizEnWAzB2Hx2r/NyvIBO6KGBDL7wkZoKnz4Sm4+1P1dhD9fVEhbsdoq9RKEf8dvzTOZMaC/iLqZFKSN6") :
Convert.FromBase64String("MIIEagIBAzCCBDAGCSqGSIb3DQEHAaCCBCEEggQdMIIEGTCCAw8GCSqGSIb3DQEHBqCCAwAwggL8AgEAMIIC9QYJKoZIhvcNAQcBMBwGCiqGSIb3DQEMAQYwDgQIim9BBUiXkDgCAggAgIICyGrISvsUV1o7fVvskmnfWPQqScCIDx/P93fqnes0EwtQTMIaCFIR1i9TH/6NlrusAD9qHXE7W3i/3gNsiuMGC4cYim4ym8GBhyPxWJqcubSpOVv5adja5gmz7G1v6iB6H9mg2TNLR2RQx4dddjA4Q4vSXYp2DOrvGIB8Fw9Cjx5BrQYEL78IMzB3DmNtLoFs/ny7a0WjoxnyCeT8272rzOgiuPN4Fhtj+pJYorIeoEcyJ3DAvZur6CotHANYVhSETI8tUejX5oqpg2LuY5yzxRrgy81PbabWA92TvvNMy/3/WuFSO3RyhAj4+87+ru8q6cgNT3IcZ16YXMG+XQX9eyp1EwxeDe5he3/4FEfbQH0tMD7E3MAQUjUXI/wzhCCI5HvpLXtf0N4tc8b178ykc+1oZ3zpYs4mOEHjv/xcM27C6L5YeZD36MX0LHjUQGe7ezNSYewqMR8LRwqzZ6QpLY5Dv+izx297hcKuh2bvA7MatJuI+VlK0g7gTrKYp6OHaQH6Us8F7BlO+jI8AzippV8wsJKo1VwgMVPMku4ufKAdxTuSoJP55azbze5nEebzhOidazVMgttyPrKB8QzCa19iHEzqqjWEEvDerZt5K5Em7CVxnnNVwaS/Cm70/oe4W2hpS7D4Rrj7Q4pOK6paJhxa/RZGUWtaddCSVyvI8Er4aGI0xJy1rOXBEs2yBl9z30PxsPl2ddvCV2ax0E5semTDKkFhS35OHsiFAjLUyDc0tE/Erh2+u16rzk0ceKRpmK0kanCQtqS5Amd6zaPk/D+fOIZY5tHUATHX5OGfCpT4vfTFSnJX66IAeLBOKO67sveiXrFfZanxnHAEWc6a5xHCBPZvSKlGdCsm86rZR/tXESGLWEbO3T7X7L694Rtq37yg2HjW8SQ6Y5hzYWjjLjU2F5kBJj5q9UVGIuBRUucnT+8YPJ95/00wggECBgkqhkiG9w0BBwGggfQEgfEwge4wgesGCyqGSIb3DQEMCgECoIG0MIGxMBwGCiqGSIb3DQEMAQMwDgQICc6+CvVyhsMCAggABIGQWzkumOp4ivI9Y8uECZwnmhXGn2YoZyfBjH6LC1lBtjQC6qs5PcoeqmL0Ig/6ZyPGKZ56kZXLJfWv7hnLAGcBAgHhMrLl73XNxtiIdA/FVWZvNGrzETM9U5JpfhFOZvWgoAZDeGnOirrHjBOtihaCMBscel5ZmULjJXiIr5kiVfByYX+RBrSIjaAwWRQWfK6hMSUwIwYJKoZIhvcNAQkVMRYEFF7DK7P8nOIRMzRro6ajG9hRWl04MDEwITAJBgUrDgMCGgUABBTqJGte1FPTsA+57DXU5WGnb+NfiQQIZ5eHWqzCYQwCAggA");

[Fact]
public static async Task GenerateAsync_Generates_Valid_Signed_Jwt()
{
Expand All @@ -36,7 +30,8 @@ public static async Task GenerateAsync_Generates_Valid_Signed_Jwt()
ClientSecretExpiresAfter = TimeSpan.FromMinutes(1),
KeyId = "my-key-id",
TeamId = "my-team-id",
PrivateKeyBytes = (keyId) => Task.FromResult(TestPrivateKey),
PrivateKeyBytes = (keyId) => TestKeys.GetPrivateKeyBytesAsync(),
PrivateKeyPassword = TestKeys.GetPrivateKeyPassword(),
};

await GenerateTokenAsync(options, async (generator, context) =>
Expand Down Expand Up @@ -84,7 +79,8 @@ public static async Task GenerateAsync_Caches_Jwt_Until_Expired()
ClientSecretExpiresAfter = TimeSpan.FromSeconds(1),
KeyId = "my-key-id",
TeamId = "my-team-id",
PrivateKeyBytes = (keyId) => Task.FromResult(TestPrivateKey),
PrivateKeyBytes = (keyId) => TestKeys.GetPrivateKeyBytesAsync(),
PrivateKeyPassword = TestKeys.GetPrivateKeyPassword(),
};

await GenerateTokenAsync(options, async (generator, context) =>
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -78,10 +78,11 @@ void ConfigureServices(IServiceCollection services)
options.KeyId = "my-key-id";
options.TeamId = "my-team-id";
options.ValidateTokens = true;
options.PrivateKeyBytes = (keyId) =>
options.PrivateKeyPassword = TestKeys.GetPrivateKeyPassword();
options.PrivateKeyBytes = async (keyId) =>
{
Assert.Equal("my-key-id", keyId);
return Task.FromResult(AppleClientSecretGeneratorTests.TestPrivateKey);
return await TestKeys.GetPrivateKeyBytesAsync();
};
});
}
Expand Down
45 changes: 45 additions & 0 deletions test/AspNet.Security.OAuth.Providers.Tests/Apple/TestKeys.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
/*
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
* for more information concerning the license and the contributors participating to this project.
*/

using System;
using System.IO;
using System.Linq;
using System.Runtime.InteropServices;
using System.Threading.Tasks;

namespace AspNet.Security.OAuth.Apple
{
internal static class TestKeys
{
private static readonly bool IsWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows);

internal static async Task<byte[]> GetPrivateKeyBytesAsync()
{
byte[] privateKey;

if (IsWindows)
{
string content = await File.ReadAllTextAsync(Path.Combine("Apple", "test.p8"));

if (content.StartsWith("-----BEGIN PRIVATE KEY-----", StringComparison.Ordinal))
{
string[] keyLines = content.Split('\n');
content = string.Join(string.Empty, keyLines.Skip(1).Take(keyLines.Length - 2));
}

privateKey = Convert.FromBase64String(content);
}
else
{
privateKey = await File.ReadAllBytesAsync(Path.Combine("Apple", "test.pfx"));
}

return privateKey;
}

internal static string GetPrivateKeyPassword() => IsWindows ? string.Empty : "passw0rd";
}
}
15 changes: 15 additions & 0 deletions test/AspNet.Security.OAuth.Providers.Tests/Apple/test.cer
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
-----BEGIN CERTIFICATE-----
MIICYzCCAgmgAwIBAgIJAI8YasGI5F6oMAoGCCqGSM49BAMCMIGNMQswCQYDVQQG
EwJHQjEQMA4GA1UECAwHRW5nbGFuZDEPMA0GA1UEBwwGTG9uZG9uMRcwFQYDVQQK
DA5hc3BuZXQtY29udHJpYjEYMBYGA1UEAwwPTWFydGluIENvc3RlbGxvMSgwJgYJ
KoZIhvcNAQkBFhltYXJ0aW5AbWFydGluY29zdGVsbG8uY29tMB4XDTE5MDYwOTEy
NTgyNVoXDTE5MDcwOTEyNTgyNVowgY0xCzAJBgNVBAYTAkdCMRAwDgYDVQQIDAdF
bmdsYW5kMQ8wDQYDVQQHDAZMb25kb24xFzAVBgNVBAoMDmFzcG5ldC1jb250cmli
MRgwFgYDVQQDDA9NYXJ0aW4gQ29zdGVsbG8xKDAmBgkqhkiG9w0BCQEWGW1hcnRp
bkBtYXJ0aW5jb3N0ZWxsby5jb20wWTATBgcqhkjOPQIBBggqhkjOPQMBBwNCAAQf
rvDWizEnWAzB2Hx2r/NyvIBO6KGBDL7wkZoKnz4Sm4+1P1dhD9fVEhbsdoq9RKEf
8dvzTOZMaC/iLqZFKSN6o1AwTjAdBgNVHQ4EFgQUZT6ij94pUE1x0rrWWCwswodX
FRMwHwYDVR0jBBgwFoAUZT6ij94pUE1x0rrWWCwswodXFRMwDAYDVR0TBAUwAwEB
/zAKBggqhkjOPQQDAgNIADBFAiAOR5YjLHk5QFPdq9BflblpV4/gwwYcxLM+OuNf
a4PsUQIhAJhh1DNlUH0kvG6/L1gXtd+pi41tjM96RVi4kvrf0xzN
-----END CERTIFICATE-----
3 changes: 3 additions & 0 deletions test/AspNet.Security.OAuth.Providers.Tests/Apple/test.p8
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
-----BEGIN PRIVATE KEY-----
MIGTAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBHkwdwIBAQQgU208KCg/doqiSzsVF5sknVtYSgt8/3oiYGbvryIRrzSgCgYIKoZIzj0DAQehRANCAAQfrvDWizEnWAzB2Hx2r/NyvIBO6KGBDL7wkZoKnz4Sm4+1P1dhD9fVEhbsdoq9RKEf8dvzTOZMaC/iLqZFKSN6
-----END PRIVATE KEY-----
Binary file not shown.
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@
</PropertyGroup>
<ItemGroup>
<Content Include="xunit.runner.json;**\bundle.json" Exclude="bin\**\bundle.json" CopyToOutputDirectory="PreserveNewest" />
<Content Include="Apple\test.p8;Apple\test.pfx" CopyToOutputDirectory="PreserveNewest" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\..\src\AspNet.Security.OAuth.*\*.csproj" />
Expand Down