From 99ddb8b3d8efd53c6795c7f4d9815a1a0bd0b3e2 Mon Sep 17 00:00:00 2001 From: Ioannis Kakavas Date: Thu, 8 Aug 2019 12:48:16 +0300 Subject: [PATCH] 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. --- .../OpenIdConnectProviderConfiguration.java | 5 +++-- .../authc/oidc/OpenIdConnectRealm.java | 16 +++++++++++----- .../oidc/OpenIdConnectRealmSettingsTests.java | 18 +++++++++++++++++- 3 files changed, 31 insertions(+), 8 deletions(-) diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectProviderConfiguration.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectProviderConfiguration.java index f5f8c8592b22a..f0eb9bebdf302 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectProviderConfiguration.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectProviderConfiguration.java @@ -23,9 +23,10 @@ public class OpenIdConnectProviderConfiguration { private final String jwkSetPath; 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.tokenEndpoint = Objects.requireNonNull(tokenEndpoint, "Token Endpoint must be provided"); + this.tokenEndpoint = tokenEndpoint; this.userinfoEndpoint = userinfoEndpoint; this.endsessionEndpoint = endsessionEndpoint; this.issuer = Objects.requireNonNull(issuer, "OP Issuer must be provided"); diff --git a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectRealm.java b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectRealm.java index b9a7355e92967..77ea1d57ad16f 100644 --- a/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectRealm.java +++ b/x-pack/plugin/security/src/main/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectRealm.java @@ -281,25 +281,31 @@ private OpenIdConnectProviderConfiguration buildOpenIdConnectProviderConfigurati // This should never happen as it's already validated in the settings 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; try { - tokenEndpoint = new URI(require(config, OP_TOKEN_ENDPOINT)); + tokenEndpoint = tokenEndpointString.isEmpty() ? null : new URI(tokenEndpointString); } catch (URISyntaxException e) { // This should never happen as it's already validated in the settings throw new SettingsException("Invalid URL: " + OP_TOKEN_ENDPOINT.getKey(), e); } URI userinfoEndpoint; try { - userinfoEndpoint = (config.getSetting(OP_USERINFO_ENDPOINT, () -> null) == null) ? null : - new URI(config.getSetting(OP_USERINFO_ENDPOINT, () -> null)); + userinfoEndpoint = (config.getSetting(OP_USERINFO_ENDPOINT).isEmpty()) ? null : + new URI(config.getSetting(OP_USERINFO_ENDPOINT)); } catch (URISyntaxException e) { // This should never happen as it's already validated in the settings throw new SettingsException("Invalid URI: " + OP_USERINFO_ENDPOINT.getKey(), e); } URI endsessionEndpoint; try { - endsessionEndpoint = (config.getSetting(OP_ENDSESSION_ENDPOINT, () -> null) == null) ? null : - new URI(config.getSetting(OP_ENDSESSION_ENDPOINT, () -> null)); + endsessionEndpoint = (config.getSetting(OP_ENDSESSION_ENDPOINT).isEmpty()) ? null : + new URI(config.getSetting(OP_ENDSESSION_ENDPOINT)); } catch (URISyntaxException e) { // This should never happen as it's already validated in the settings throw new SettingsException("Invalid URI: " + OP_ENDSESSION_ENDPOINT.getKey(), e); diff --git a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectRealmSettingsTests.java b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectRealmSettingsTests.java index 341cf07b0dd7b..e8b02c815c964 100644 --- a/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectRealmSettingsTests.java +++ b/x-pack/plugin/security/src/test/java/org/elasticsearch/xpack/security/authc/oidc/OpenIdConnectRealmSettingsTests.java @@ -86,7 +86,7 @@ public void testInvalidAuthorizationEndpointThrowsError() { Matchers.containsString(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_AUTHORIZATION_ENDPOINT))); } - public void testMissingTokenEndpointThrowsError() { + public void testMissingTokenEndpointThrowsErrorInCodeFlow() { 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") @@ -103,6 +103,22 @@ public void testMissingTokenEndpointThrowsError() { 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() { final Settings.Builder settingsBuilder = Settings.builder() .put(getFullSettingKey(REALM_NAME, OpenIdConnectRealmSettings.OP_AUTHORIZATION_ENDPOINT), "https://op.example.com/login")