Allow empty token endpoint for implicit flow (#45038)

When using the implicit flow in OpenID Connect, the
op.token_endpoint_url should not be mandatory as there is no need
to contact the token endpoint of the OP.
This commit is contained in:
Ioannis Kakavas 2019-08-08 12:48:16 +03:00
parent ddcc38cf1c
commit 99ddb8b3d8
3 changed files with 31 additions and 8 deletions

View File

@ -23,9 +23,10 @@ public class OpenIdConnectProviderConfiguration {
private final String jwkSetPath; private final String jwkSetPath;
public OpenIdConnectProviderConfiguration(Issuer issuer, String jwkSetPath, URI authorizationEndpoint, public OpenIdConnectProviderConfiguration(Issuer issuer, String jwkSetPath, URI authorizationEndpoint,
URI tokenEndpoint, @Nullable URI userinfoEndpoint, @Nullable URI endsessionEndpoint) { @Nullable URI tokenEndpoint, @Nullable URI userinfoEndpoint,
@Nullable URI endsessionEndpoint) {
this.authorizationEndpoint = Objects.requireNonNull(authorizationEndpoint, "Authorization Endpoint must be provided"); this.authorizationEndpoint = Objects.requireNonNull(authorizationEndpoint, "Authorization Endpoint must be provided");
this.tokenEndpoint = Objects.requireNonNull(tokenEndpoint, "Token Endpoint must be provided"); this.tokenEndpoint = tokenEndpoint;
this.userinfoEndpoint = userinfoEndpoint; this.userinfoEndpoint = userinfoEndpoint;
this.endsessionEndpoint = endsessionEndpoint; this.endsessionEndpoint = endsessionEndpoint;
this.issuer = Objects.requireNonNull(issuer, "OP Issuer must be provided"); this.issuer = Objects.requireNonNull(issuer, "OP Issuer must be provided");

View File

@ -281,25 +281,31 @@ public class OpenIdConnectRealm extends Realm implements Releasable {
// This should never happen as it's already validated in the settings // This should never happen as it's already validated in the settings
throw new SettingsException("Invalid URI: " + OP_AUTHORIZATION_ENDPOINT.getKey(), e); throw new SettingsException("Invalid URI: " + OP_AUTHORIZATION_ENDPOINT.getKey(), e);
} }
String responseType = require(config, RP_RESPONSE_TYPE);
String tokenEndpointString = config.getSetting(OP_TOKEN_ENDPOINT);
if (responseType.equals("code") && tokenEndpointString.isEmpty()) {
throw new SettingsException("The configuration setting [" + OP_TOKEN_ENDPOINT.getConcreteSettingForNamespace(name()).getKey()
+ "] is required when [" + RP_RESPONSE_TYPE.getConcreteSettingForNamespace(name()).getKey() + "] is set to \"code\"");
}
URI tokenEndpoint; URI tokenEndpoint;
try { try {
tokenEndpoint = new URI(require(config, OP_TOKEN_ENDPOINT)); tokenEndpoint = tokenEndpointString.isEmpty() ? null : new URI(tokenEndpointString);
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
// This should never happen as it's already validated in the settings // This should never happen as it's already validated in the settings
throw new SettingsException("Invalid URL: " + OP_TOKEN_ENDPOINT.getKey(), e); throw new SettingsException("Invalid URL: " + OP_TOKEN_ENDPOINT.getKey(), e);
} }
URI userinfoEndpoint; URI userinfoEndpoint;
try { try {
userinfoEndpoint = (config.getSetting(OP_USERINFO_ENDPOINT, () -> null) == null) ? null : userinfoEndpoint = (config.getSetting(OP_USERINFO_ENDPOINT).isEmpty()) ? null :
new URI(config.getSetting(OP_USERINFO_ENDPOINT, () -> null)); new URI(config.getSetting(OP_USERINFO_ENDPOINT));
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
// This should never happen as it's already validated in the settings // This should never happen as it's already validated in the settings
throw new SettingsException("Invalid URI: " + OP_USERINFO_ENDPOINT.getKey(), e); throw new SettingsException("Invalid URI: " + OP_USERINFO_ENDPOINT.getKey(), e);
} }
URI endsessionEndpoint; URI endsessionEndpoint;
try { try {
endsessionEndpoint = (config.getSetting(OP_ENDSESSION_ENDPOINT, () -> null) == null) ? null : endsessionEndpoint = (config.getSetting(OP_ENDSESSION_ENDPOINT).isEmpty()) ? null :
new URI(config.getSetting(OP_ENDSESSION_ENDPOINT, () -> null)); new URI(config.getSetting(OP_ENDSESSION_ENDPOINT));
} catch (URISyntaxException e) { } catch (URISyntaxException e) {
// This should never happen as it's already validated in the settings // This should never happen as it's already validated in the settings
throw new SettingsException("Invalid URI: " + OP_ENDSESSION_ENDPOINT.getKey(), e); throw new SettingsException("Invalid URI: " + OP_ENDSESSION_ENDPOINT.getKey(), e);

View File

@ -86,7 +86,7 @@ public class OpenIdConnectRealmSettingsTests extends ESTestCase {
Matchers.containsString(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_AUTHORIZATION_ENDPOINT))); Matchers.containsString(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_AUTHORIZATION_ENDPOINT)));
} }
public void testMissingTokenEndpointThrowsError() { public void testMissingTokenEndpointThrowsErrorInCodeFlow() {
final Settings.Builder settingsBuilder = Settings.builder() final Settings.Builder settingsBuilder = Settings.builder()
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_AUTHORIZATION_ENDPOINT), "https://op.example.com/login") .put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_AUTHORIZATION_ENDPOINT), "https://op.example.com/login")
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_ISSUER), "https://op.example.com") .put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_ISSUER), "https://op.example.com")
@ -103,6 +103,22 @@ public class OpenIdConnectRealmSettingsTests extends ESTestCase {
Matchers.containsString(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_TOKEN_ENDPOINT))); Matchers.containsString(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_TOKEN_ENDPOINT)));
} }
public void testMissingTokenEndpointIsAllowedInImplicitFlow() {
final Settings.Builder settingsBuilder = Settings.builder()
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_AUTHORIZATION_ENDPOINT), "https://op.example.com/login")
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_ISSUER), "https://op.example.com")
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_JWKSET_PATH), "https://op.example.com/jwks.json")
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.PRINCIPAL_CLAIM.getClaim()), "sub")
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.RP_REDIRECT_URI), "https://rp.my.com")
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.RP_CLIENT_ID), "rp-my")
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.RP_RESPONSE_TYPE), "id_token token");
settingsBuilder.setSecureSettings(getSecureSettings());
final OpenIdConnectRealm realm = new OpenIdConnectRealm(buildConfig(settingsBuilder.build()), null, null);
assertNotNull(realm);
}
public void testInvalidTokenEndpointThrowsError() { public void testInvalidTokenEndpointThrowsError() {
final Settings.Builder settingsBuilder = Settings.builder() final Settings.Builder settingsBuilder = Settings.builder()
.put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_AUTHORIZATION_ENDPOINT), "https://op.example.com/login") .put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_AUTHORIZATION_ENDPOINT), "https://op.example.com/login")