Skip to content

Commit 2d6ee5d

Browse files
Use IPostConfigureOptions for Okta
Use an IPostConfigureOptions implementation to configure the endpoints for Okta (like the Twitter provider), rather than using a custom extension method.
1 parent 3a18349 commit 2d6ee5d

File tree

8 files changed

+125
-87
lines changed

8 files changed

+125
-87
lines changed

src/AspNet.Security.OAuth.Okta/OktaAuthenticationDefaults.cs

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -35,18 +35,18 @@ public static class OktaAuthenticationDefaults
3535
public const string CallbackPath = "/signin-okta";
3636

3737
/// <summary>
38-
/// Default format string to use for <see cref="OAuthOptions.AuthorizationEndpoint"/>.
38+
/// Default path to use for <see cref="OAuthOptions.AuthorizationEndpoint"/>.
3939
/// </summary>
40-
public const string AuthorizationEndpointFormat = "https://{0}/oauth2/default/v1/authorize";
40+
public const string AuthorizationEndpointPath = "/oauth2/default/v1/authorize";
4141

4242
/// <summary>
43-
/// Default format string to use for <see cref="OAuthOptions.TokenEndpoint"/>.
43+
/// Default path to use for <see cref="OAuthOptions.TokenEndpoint"/>.
4444
/// </summary>
45-
public const string TokenEndpointFormat = "https://{0}/oauth2/default/v1/token";
45+
public const string TokenEndpointPath = "/oauth2/default/v1/token";
4646

4747
/// <summary>
48-
/// Default format string to use for <see cref="OAuthOptions.UserInformationEndpoint"/>.
48+
/// Default path to use for <see cref="OAuthOptions.UserInformationEndpoint"/>.
4949
/// </summary>
50-
public const string UserInformationEndpointFormat = "https://{0}/oauth2/default/v1/userinfo";
50+
public const string UserInformationEndpointPath = "/oauth2/default/v1/userinfo";
5151
}
5252
}

src/AspNet.Security.OAuth.Okta/OktaAuthenticationExtensions.cs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
using AspNet.Security.OAuth.Okta;
99
using JetBrains.Annotations;
1010
using Microsoft.AspNetCore.Authentication;
11+
using Microsoft.Extensions.DependencyInjection.Extensions;
12+
using Microsoft.Extensions.Options;
1113

1214
namespace Microsoft.Extensions.DependencyInjection
1315
{
@@ -72,6 +74,7 @@ public static AuthenticationBuilder AddOkta(
7274
[CanBeNull] string caption,
7375
[NotNull] Action<OktaAuthenticationOptions> configuration)
7476
{
77+
builder.Services.TryAddSingleton<IPostConfigureOptions<OktaAuthenticationOptions>, OktaPostConfigureOptions>();
7578
return builder.AddOAuth<OktaAuthenticationOptions, OktaAuthenticationHandler>(scheme, caption, configuration);
7679
}
7780
}

src/AspNet.Security.OAuth.Okta/OktaAuthenticationOptions.cs

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -24,10 +24,6 @@ public OktaAuthenticationOptions()
2424
ClaimsIssuer = OktaAuthenticationDefaults.Issuer;
2525
CallbackPath = OktaAuthenticationDefaults.CallbackPath;
2626

27-
AuthorizationEndpoint = OktaAuthenticationDefaults.AuthorizationEndpointFormat;
28-
TokenEndpoint = OktaAuthenticationDefaults.TokenEndpointFormat;
29-
UserInformationEndpoint = OktaAuthenticationDefaults.UserInformationEndpointFormat;
30-
3127
Scope.Add("openid");
3228
Scope.Add("profile");
3329
Scope.Add("email");
@@ -39,9 +35,16 @@ public OktaAuthenticationOptions()
3935
ClaimActions.MapJsonKey(ClaimTypes.Surname, "family_name");
4036
}
4137

38+
/// <summary>
39+
/// Gets or sets the Okta domain (Org URL) to use for authentication.
40+
/// </summary>
41+
public string? Domain { get; set; }
42+
4243
/// <inheritdoc/>
4344
public override void Validate()
4445
{
46+
base.Validate();
47+
4548
if (!Uri.TryCreate(AuthorizationEndpoint, UriKind.Absolute, out var _))
4649
{
4750
throw new ArgumentException(
@@ -62,8 +65,6 @@ public override void Validate()
6265
$"The '{nameof(UserInformationEndpoint)}' option must be set to a valid URI.",
6366
nameof(UserInformationEndpoint));
6467
}
65-
66-
base.Validate();
6768
}
6869
}
6970
}

src/AspNet.Security.OAuth.Okta/OktaAuthenticationOptionsExtensions.cs

Lines changed: 0 additions & 45 deletions
This file was deleted.
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
3+
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
4+
* for more information concerning the license and the contributors participating to this project.
5+
*/
6+
7+
using System;
8+
using Microsoft.Extensions.Options;
9+
10+
namespace AspNet.Security.OAuth.Okta
11+
{
12+
/// <summary>
13+
/// A class used to setup defaults for all <see cref="OktaAuthenticationOptions"/>.
14+
/// </summary>
15+
public class OktaPostConfigureOptions : IPostConfigureOptions<OktaAuthenticationOptions>
16+
{
17+
/// <inheritdoc/>
18+
public void PostConfigure(string name, OktaAuthenticationOptions options)
19+
{
20+
if (string.IsNullOrWhiteSpace(options.Domain))
21+
{
22+
throw new ArgumentException("No Okta domain configured.", nameof(options));
23+
}
24+
25+
options.AuthorizationEndpoint = CreateUrl(options.Domain, OktaAuthenticationDefaults.AuthorizationEndpointPath);
26+
options.TokenEndpoint = CreateUrl(options.Domain, OktaAuthenticationDefaults.TokenEndpointPath);
27+
options.UserInformationEndpoint = CreateUrl(options.Domain, OktaAuthenticationDefaults.UserInformationEndpointPath);
28+
}
29+
30+
private static string CreateUrl(string domain, string path)
31+
{
32+
// Enforce use of HTTPS
33+
var builder = new UriBuilder(domain)
34+
{
35+
Path = path,
36+
Port = -1,
37+
Scheme = "https",
38+
};
39+
40+
return builder.Uri.ToString();
41+
}
42+
}
43+
}

test/AspNet.Security.OAuth.Providers.Tests/Okta/OktaAuthenticationOptionsTests.cs

Lines changed: 0 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -5,41 +5,12 @@
55
*/
66

77
using System;
8-
using Shouldly;
98
using Xunit;
109

1110
namespace AspNet.Security.OAuth.Okta
1211
{
1312
public static class OktaAuthenticationOptionsTests
1413
{
15-
[Fact]
16-
public static void UseDomain_Sets_Valid_Endpoints()
17-
{
18-
// Arrange
19-
var options = new OktaAuthenticationOptions();
20-
21-
// Act
22-
options.UseDomain("okta.local");
23-
24-
// Assert
25-
options.AuthorizationEndpoint.ShouldStartWith("https://okta.local/");
26-
options.TokenEndpoint.ShouldStartWith("https://okta.local/");
27-
options.UserInformationEndpoint.ShouldStartWith("https://okta.local/");
28-
}
29-
30-
[Theory]
31-
[InlineData(null)]
32-
[InlineData("")]
33-
[InlineData(" ")]
34-
public static void UseDomain_Throws_If_Domain_Is_Invald(string value)
35-
{
36-
// Arrange
37-
var options = new OktaAuthenticationOptions();
38-
39-
// Act and Assert
40-
Assert.Throws<ArgumentException>("domain", () => options.UseDomain(value));
41-
}
42-
4314
[Fact]
4415
public static void Validate_Throws_If_AuthorizationEndpoint_Not_Set()
4516
{
Lines changed: 65 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
/*
2+
* Licensed under the Apache License, Version 2.0 (http://www.apache.org/licenses/LICENSE-2.0)
3+
* See https://github.com/aspnet-contrib/AspNet.Security.OAuth.Providers
4+
* for more information concerning the license and the contributors participating to this project.
5+
*/
6+
7+
using System;
8+
using Shouldly;
9+
using Xunit;
10+
11+
namespace AspNet.Security.OAuth.Okta
12+
{
13+
public static class OktaPostConfigureOptionsTests
14+
{
15+
[Theory]
16+
[InlineData("okta.local")]
17+
[InlineData("http://okta.local")]
18+
[InlineData("http://okta.local/")]
19+
[InlineData("https://okta.local")]
20+
[InlineData("https://okta.local/")]
21+
public static void PostConfigure_Configures_Valid_Endpoints(string domain)
22+
{
23+
// Arrange
24+
string name = "Okta";
25+
var target = new OktaPostConfigureOptions();
26+
27+
var options = new OktaAuthenticationOptions()
28+
{
29+
Domain = domain,
30+
};
31+
32+
// Act
33+
target.PostConfigure(name, options);
34+
35+
// Assert
36+
options.AuthorizationEndpoint.ShouldStartWith("https://okta.local/oauth2/default/v1/");
37+
Uri.TryCreate(options.AuthorizationEndpoint, UriKind.Absolute, out var _).ShouldBeTrue();
38+
39+
options.TokenEndpoint.ShouldStartWith("https://okta.local/oauth2/default/v1/");
40+
Uri.TryCreate(options.TokenEndpoint, UriKind.Absolute, out var _).ShouldBeTrue();
41+
42+
options.UserInformationEndpoint.ShouldStartWith("https://okta.local/oauth2/default/v1/");
43+
Uri.TryCreate(options.UserInformationEndpoint, UriKind.Absolute, out var _).ShouldBeTrue();
44+
}
45+
46+
[Theory]
47+
[InlineData(null)]
48+
[InlineData("")]
49+
[InlineData(" ")]
50+
public static void PostConfigure_Throws_If_Domain_Is_Invalid(string value)
51+
{
52+
// Arrange
53+
string name = "Okta";
54+
var target = new OktaPostConfigureOptions();
55+
56+
var options = new OktaAuthenticationOptions()
57+
{
58+
Domain = value,
59+
};
60+
61+
// Act and Assert
62+
Assert.Throws<ArgumentException>("options", () => target.PostConfigure(name, options));
63+
}
64+
}
65+
}

test/AspNet.Security.OAuth.Providers.Tests/Okta/OktaTests.cs

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -27,7 +27,7 @@ protected internal override void RegisterAuthentication(AuthenticationBuilder bu
2727
builder.AddOkta(options =>
2828
{
2929
ConfigureDefaults(builder, options);
30-
options.UseDomain("okta.local");
30+
options.Domain = "okta.local";
3131
});
3232
}
3333

0 commit comments

Comments
 (0)