Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
Original file line number Diff line number Diff line change
Expand Up @@ -26,5 +26,11 @@ public class SecurityConfiguration
public string ServicePrincipalClientId { get; set; }

public AddAuthenticationLibraryMethod AddAuthenticationLibrary { get; set; }

public string IntrospectionEndpoint { get; set; }

public string ManagementEndpoint { get; set; }

public string RevocationEndpoint { get; set; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,41 @@ protected GetSmartConfigurationResponse Handle(GetSmartConfigurationRequest requ
"online_access",
};

return new GetSmartConfigurationResponse(authorizationEndpoint, tokenEndpoint, capabilities, scopesSupported);
ICollection<string> codeChallengeMethodsSupported = new List<string>
{
"S256",
};

ICollection<string> grantTypesSupported = new List<string>
{
"authorization_code",
"client_credentials",
};

ICollection<string> tokenEndpointAuthMethodsSupported = new List<string>
{
"client_secret_basic",
"client_secret_jwt",
"none",
};

ICollection<string> responseTypesSupported = new List<string>
{
"code",
};

return new GetSmartConfigurationResponse(
authorizationEndpoint,
tokenEndpoint,
capabilities,
scopesSupported,
codeChallengeMethodsSupported,
grantTypesSupported,
tokenEndpointAuthMethodsSupported,
responseTypesSupported,
_securityConfiguration.IntrospectionEndpoint,
_securityConfiguration.ManagementEndpoint,
_securityConfiguration.RevocationEndpoint);
}
catch (Exception e) when (e is ArgumentNullException || e is UriFormatException)
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,18 @@ public SmartConfigurationResult(Uri authorizationEndpoint, Uri tokenEndpoint, IC
Capabilities = capabilities;
}

public SmartConfigurationResult(Uri authorizationEndpoint, Uri tokenEndpoint, ICollection<string> capabilities, ICollection<string> scopesSupported)
public SmartConfigurationResult(
Uri authorizationEndpoint,
Uri tokenEndpoint,
ICollection<string> capabilities,
ICollection<string> scopesSupported,
ICollection<string> codeChallengeMethodsSupported = null,
ICollection<string> grantTypesSupported = null,
ICollection<string> tokenEndpointAuthMethodsSupported = null,
ICollection<string> responseTypesSupported = null,
string introspectionEndpoint = null,
string managementEndpoint = null,
string revocationEndpoint = null)
{
EnsureArg.IsNotNull(authorizationEndpoint, nameof(authorizationEndpoint));
EnsureArg.IsNotNull(tokenEndpoint, nameof(tokenEndpoint));
Expand All @@ -36,23 +47,51 @@ public SmartConfigurationResult(Uri authorizationEndpoint, Uri tokenEndpoint, IC
TokenEndpoint = tokenEndpoint;
Capabilities = capabilities;
ScopesSupported = scopesSupported;
CodeChallengeMethodsSupported = codeChallengeMethodsSupported;
GrantTypesSupported = grantTypesSupported;
TokenEndpointAuthMethodsSupported = tokenEndpointAuthMethodsSupported;
ResponseTypesSupported = responseTypesSupported;
IntrospectionEndpoint = introspectionEndpoint;
ManagementEndpoint = managementEndpoint;
RevocationEndpoint = revocationEndpoint;
}

[JsonConstructor]
public SmartConfigurationResult()
{
}

[JsonProperty("authorizationEndpoint")]
[JsonProperty("authorization_endpoint")]
public Uri AuthorizationEndpoint { get; private set; }

[JsonProperty("tokenEndpoint")]
[JsonProperty("token_endpoint")]
public Uri TokenEndpoint { get; private set; }

[JsonProperty("capabilities")]
public ICollection<string> Capabilities { get; private set; }

[JsonProperty("scopes_supported")]
public ICollection<string> ScopesSupported { get; private set; }

[JsonProperty("code_challenge_methods_supported")]
public ICollection<string> CodeChallengeMethodsSupported { get; }

[JsonProperty("grant_types_supported")]
public ICollection<string> GrantTypesSupported { get; }

[JsonProperty("token_endpoint_auth_methods_supported")]
public ICollection<string> TokenEndpointAuthMethodsSupported { get; }

[JsonProperty("response_types_supported")]
public ICollection<string> ResponseTypesSupported { get; }

[JsonProperty("introspection_endpoint")]
public string IntrospectionEndpoint { get; }

[JsonProperty("management_endpoint")]
public string ManagementEndpoint { get; }

[JsonProperty("revocation_endpoint")]
public string RevocationEndpoint { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,18 @@ public GetSmartConfigurationResponse(Uri authorizationEndpoint, Uri tokenEndpoin
Capabilities = capabilities;
}

public GetSmartConfigurationResponse(Uri authorizationEndpoint, Uri tokenEndpoint, ICollection<string> capabilities, ICollection<string> scopesSupported)
public GetSmartConfigurationResponse(
Uri authorizationEndpoint,
Uri tokenEndpoint,
ICollection<string> capabilities,
ICollection<string> scopesSupported,
ICollection<string> codeChallengeMethodsSupported = null,
ICollection<string> grantTypesSupported = null,
ICollection<string> tokenEndpointAuthMethodsSupported = null,
ICollection<string> responseTypesSupported = null,
string introspectionEndpoint = null,
string managementEndpoint = null,
string revocationEndpoint = null)
{
EnsureArg.IsNotNull(authorizationEndpoint, nameof(authorizationEndpoint));
EnsureArg.IsNotNull(tokenEndpoint, nameof(tokenEndpoint));
Expand All @@ -32,6 +43,13 @@ public GetSmartConfigurationResponse(Uri authorizationEndpoint, Uri tokenEndpoin
TokenEndpoint = tokenEndpoint;
Capabilities = capabilities;
ScopesSupported = scopesSupported;
CodeChallengeMethodsSupported = codeChallengeMethodsSupported;
GrantTypesSupported = grantTypesSupported;
TokenEndpointAuthMethodsSupported = tokenEndpointAuthMethodsSupported;
ResponseTypesSupported = responseTypesSupported;
IntrospectionEndpoint = introspectionEndpoint;
ManagementEndpoint = managementEndpoint;
RevocationEndpoint = revocationEndpoint;
}

public Uri AuthorizationEndpoint { get; }
Expand All @@ -41,5 +59,19 @@ public GetSmartConfigurationResponse(Uri authorizationEndpoint, Uri tokenEndpoin
public ICollection<string> Capabilities { get; }

public ICollection<string> ScopesSupported { get; }

public ICollection<string> CodeChallengeMethodsSupported { get; }

public ICollection<string> GrantTypesSupported { get; }

public ICollection<string> TokenEndpointAuthMethodsSupported { get; }

public ICollection<string> ResponseTypesSupported { get; }

public string IntrospectionEndpoint { get; }

public string ManagementEndpoint { get; }

public string RevocationEndpoint { get; }
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,10 @@ public async Task GivenASmartConfigurationHandler_WhenSecurityConfigurationEnabl

// Verify SMART v2 scopes are included
Assert.NotNull(response.ScopesSupported);
Assert.NotNull(response.CodeChallengeMethodsSupported);
Assert.NotNull(response.GrantTypesSupported);
Assert.NotNull(response.TokenEndpointAuthMethodsSupported);
Assert.NotNull(response.ResponseTypesSupported);
}

[Fact]
Expand All @@ -84,5 +88,38 @@ public async Task GivenASmartConfigurationHandler_WhenBaseEndpointIsInvalid_Then
OperationFailedException exception = await Assert.ThrowsAsync<OperationFailedException>(() => handler.Handle(request, CancellationToken.None));
Assert.Equal(HttpStatusCode.BadRequest, exception.ResponseStatusCode);
}

[Theory]
[InlineData("https://ehr.example.com/user/introspect", null, null)]
[InlineData(null, "https://ehr.example.com/user/manage", null)]
[InlineData(null, null, "https://ehr.example.com/user/revoke")]
[InlineData("https://ehr.example.com/user/introspect", "https://ehr.example.com/user/manage", "https://ehr.example.com/user/revoke")]
public async Task GivenASmartConfigurationHandler_WhenOtherEndpointsAreSpecifired_ThenSmartConfigurationShouldContainsOtherEndpoints(
string introspectionEndpoint,
string managementEndpoint,
string revocationEndpoint)
{
var request = new GetSmartConfigurationRequest();
string baseEndpoint = "http://base.endpoint";

var securityConfiguration = new SecurityConfiguration();
securityConfiguration.Authorization.Enabled = true;
securityConfiguration.Authentication.Authority = baseEndpoint;
securityConfiguration.IntrospectionEndpoint = introspectionEndpoint;
securityConfiguration.ManagementEndpoint = managementEndpoint;
securityConfiguration.RevocationEndpoint = revocationEndpoint;

var handler = new GetSmartConfigurationHandler(Options.Create(securityConfiguration));

GetSmartConfigurationResponse response = await handler.Handle(request, CancellationToken.None);

Assert.Equal(response.AuthorizationEndpoint.ToString(), baseEndpoint + "/authorize");
Assert.Equal(response.TokenEndpoint.ToString(), baseEndpoint + "/token");

// Verify SMART v2 endpoints
Assert.Equal(introspectionEndpoint, response.IntrospectionEndpoint);
Assert.Equal(managementEndpoint, response.ManagementEndpoint);
Assert.Equal(revocationEndpoint, response.RevocationEndpoint);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -173,7 +173,18 @@ public static async Task<SmartConfigurationResult> GetSmartConfigurationAsync(th

var response = await mediator.Send(new GetSmartConfigurationRequest(), cancellationToken);

return new SmartConfigurationResult(response.AuthorizationEndpoint, response.TokenEndpoint, response.Capabilities, response.ScopesSupported);
return new SmartConfigurationResult(
response.AuthorizationEndpoint,
response.TokenEndpoint,
response.Capabilities,
response.ScopesSupported,
response.CodeChallengeMethodsSupported,
response.GrantTypesSupported,
response.TokenEndpointAuthMethodsSupported,
response.ResponseTypesSupported,
response.IntrospectionEndpoint,
response.ManagementEndpoint,
response.RevocationEndpoint);
}

public static async Task<VersionsResult> GetOperationVersionsAsync(this IMediator mediator, CancellationToken cancellationToken = default)
Expand Down
Loading