public static async Task GenerateAsync_Caches_Jwt_Until_Expired()
        {
            // Arrange
            var options = new AppleAuthenticationOptions()
            {
                ClientId = "my-client-id",
                ClientSecretExpiresAfter = TimeSpan.FromSeconds(2),
                KeyId           = "my-key-id",
                TeamId          = "my-team-id",
                PrivateKeyBytes = (keyId) => TestKeys.GetPrivateKeyBytesAsync(),
            };

            await GenerateTokenAsync(options, async (generator, context) =>
            {
                // Act
                string token1 = await generator.GenerateAsync(context);
                string token2 = await generator.GenerateAsync(context);

                // Assert
                token2.ShouldBe(token1);

                // Act
                await Task.Delay(options.ClientSecretExpiresAfter.Add(options.ClientSecretExpiresAfter));
                string token3 = await generator.GenerateAsync(context);

                // Assert
                token3.ShouldNotBe(token1);
            });
        }
 // Arrange
 static void Configure(AppleAuthenticationOptions options)
 {
     options.ClientId = "my-client-id";
     options.ClientSecretExpiresAfter = TimeSpan.FromMinutes(1);
     options.KeyId           = "my-key-id";
     options.TeamId          = "my-team-id";
     options.PrivateKeyBytes = (_) => TestKeys.GetPrivateKeyBytesAsync();
 }
Exemplo n.º 3
0
        public static void Validate_Throws_If_ClientSecret_Is_Null_With_No_Secret_Generation()
        {
            // Arrange
            var options = new AppleAuthenticationOptions()
            {
                ClientId     = "my-client-id",
                ClientSecret = null,
            };

            // Act and Assert
            Assert.Throws <ArgumentException>("ClientSecret", () => options.Validate());
        }
Exemplo n.º 4
0
        public static void Validate_Throws_If_CallbackPath_Is_Null()
        {
            // Arrange
            var options = new AppleAuthenticationOptions()
            {
                ClientId             = "my-client-id",
                GenerateClientSecret = true,
                CallbackPath         = null,
            };

            // Act and Assert
            Assert.Throws <ArgumentException>("CallbackPath", () => options.Validate());
        }
Exemplo n.º 5
0
        public static void Validate_Throws_If_AuthorizationEndpoint_Is_Null()
        {
            // Arrange
            var options = new AppleAuthenticationOptions()
            {
                ClientId              = "my-client-id",
                GenerateClientSecret  = true,
                AuthorizationEndpoint = null,
            };

            // Act and Assert
            Assert.Throws <ArgumentException>("AuthorizationEndpoint", () => options.Validate());
        }
Exemplo n.º 6
0
        public static void Validate_Throws_If_PublicKeyEndpoint_Is_Null_With_Token_Validation()
        {
            // Arrange
            var options = new AppleAuthenticationOptions()
            {
                ClientId          = "my-client-id",
                ClientSecret      = "my-client-secret",
                PublicKeyEndpoint = null,
            };

            // Act and Assert
            Assert.Throws <ArgumentException>("PublicKeyEndpoint", () => options.Validate());
        }
Exemplo n.º 7
0
        public static void Validate_Throws_If_TeamId_Is_Null_With_Secret_Generation()
        {
            // Arrange
            var options = new AppleAuthenticationOptions()
            {
                ClientId             = "my-client-id",
                GenerateClientSecret = true,
                KeyId  = "my-key-id",
                TeamId = null,
            };

            // Act and Assert
            Assert.Throws <ArgumentException>("TeamId", () => options.Validate());
        }
Exemplo n.º 8
0
        public static void Validate_Throws_If_ClientSecretExpiresAfter_Is_Zero_With_Secret_Generation()
        {
            // Arrange
            var options = new AppleAuthenticationOptions()
            {
                ClientId             = "my-client-id",
                GenerateClientSecret = true,
                KeyId  = "my-key-id",
                TeamId = "my-team-id",
                ClientSecretExpiresAfter = TimeSpan.Zero,
            };

            // Act and Assert
            Assert.Throws <ArgumentOutOfRangeException>("ClientSecretExpiresAfter", () => options.Validate());
        }
        public static async Task GenerateAsync_Generates_Valid_Signed_Jwt()
        {
            // Arrange
            var options = new AppleAuthenticationOptions()
            {
                ClientId = "my-client-id",
                ClientSecretExpiresAfter = TimeSpan.FromMinutes(1),
                KeyId           = "my-key-id",
                TeamId          = "my-team-id",
                PrivateKeyBytes = (keyId) => TestKeys.GetPrivateKeyBytesAsync(),
            };

            await GenerateTokenAsync(options, async (generator, context) =>
            {
                var utcNow = DateTimeOffset.UtcNow;

                // Act
                string token = await generator.GenerateAsync(context);

                // Assert
                token.ShouldNotBeNullOrWhiteSpace();
                token.Count((c) => c == '.').ShouldBe(2); // Format: "{header}.{body}.{signature}"

                // Act
                var validator     = new JwtSecurityTokenHandler();
                var securityToken = validator.ReadJwtToken(token);

                // Assert - See https://developer.apple.com/documentation/signinwithapplerestapi/generate_and_validate_tokens
                securityToken.ShouldNotBeNull();

                securityToken.Header.ShouldNotBeNull();
                securityToken.Header.ShouldContainKeyAndValue("alg", "ES256");
                securityToken.Header.ShouldContainKeyAndValue("kid", "my-key-id");

                securityToken.Payload.ShouldNotBeNull();
                securityToken.Payload.ShouldContainKey("exp");
                securityToken.Payload.ShouldContainKey("iat");
                securityToken.Payload.ShouldContainKeyAndValue("aud", "https://appleid.apple.com");
                securityToken.Payload.ShouldContainKeyAndValue("iss", "my-team-id");
                securityToken.Payload.ShouldContainKeyAndValue("sub", "my-client-id");
                securityToken.Payload.Iat.HasValue.ShouldBeTrue();
                securityToken.Payload.Exp.HasValue.ShouldBeTrue();

                ((long)securityToken.Payload.Iat !.Value).ShouldBeGreaterThanOrEqualTo(utcNow.ToUnixTimeSeconds());
                ((long)securityToken.Payload.Exp !.Value).ShouldBeGreaterThanOrEqualTo(utcNow.AddSeconds(60).ToUnixTimeSeconds());
                ((long)securityToken.Payload.Exp.Value).ShouldBeLessThanOrEqualTo(utcNow.AddSeconds(70).ToUnixTimeSeconds());
            });
        }
        private static async Task GenerateTokenAsync(
            AppleAuthenticationOptions options,
            Func <AppleClientSecretGenerator, AppleGenerateClientSecretContext, Task> actAndAssert)
        {
            // Arrange
            var builder = new WebHostBuilder()
                          .Configure((app) => app.UseAuthentication())
                          .ConfigureServices((services) =>
            {
                services.AddAuthentication()
                .AddApple();
            });

            using var host = builder.Build();

            var httpContext = new DefaultHttpContext();
            var scheme      = new AuthenticationScheme("Apple", "Apple", typeof(AppleAuthenticationHandler));

            var context   = new AppleGenerateClientSecretContext(httpContext, scheme, options);
            var generator = host.Services.GetRequiredService <AppleClientSecretGenerator>();

            await actAndAssert(generator, context);
        }
Exemplo n.º 11
0
 internal AppleEmailClaimAction(AppleAuthenticationOptions options)
     : base(ClaimTypes.Email, ClaimValueTypes.String)
 {
     _options = options;
 }