You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@cxf.apache.org by se...@apache.org on 2017/02/02 17:36:49 UTC
cxf-fediz git commit: [FEDIZ-187] Prototyping a logout service,
avoiding some checkstyle issues in the client reg service
Repository: cxf-fediz
Updated Branches:
refs/heads/master 3d43586d6 -> 620b6b95b
[FEDIZ-187] Prototyping a logout service, avoiding some checkstyle issues in the client reg service
Project: http://git-wip-us.apache.org/repos/asf/cxf-fediz/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf-fediz/commit/620b6b95
Tree: http://git-wip-us.apache.org/repos/asf/cxf-fediz/tree/620b6b95
Diff: http://git-wip-us.apache.org/repos/asf/cxf-fediz/diff/620b6b95
Branch: refs/heads/master
Commit: 620b6b95bac0bcfe745d512898071e58c0d3a304
Parents: 3d43586
Author: Sergey Beryozkin <sb...@gmail.com>
Authored: Thu Feb 2 17:36:27 2017 +0000
Committer: Sergey Beryozkin <sb...@gmail.com>
Committed: Thu Feb 2 17:36:27 2017 +0000
----------------------------------------------------------------------
.../oidc/clients/ClientRegistrationService.java | 157 ++++++++++---------
.../clients/InvalidRegistrationException.java | 28 ++++
.../service/oidc/logout/LogoutHandler.java | 26 +++
.../service/oidc/logout/LogoutService.java | 149 ++++++++++++++++++
.../oidc/logout/TokenCleanupHandler.java | 55 +++++++
.../main/webapp/WEB-INF/applicationContext.xml | 5 +
.../src/main/webapp/WEB-INF/views/client.jsp | 14 +-
.../webapp/WEB-INF/views/registerClient.jsp | 6 +
8 files changed, 366 insertions(+), 74 deletions(-)
----------------------------------------------------------------------
http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/620b6b95/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/clients/ClientRegistrationService.java
----------------------------------------------------------------------
diff --git a/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/clients/ClientRegistrationService.java b/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/clients/ClientRegistrationService.java
index cbebdb4..fb60d98 100644
--- a/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/clients/ClientRegistrationService.java
+++ b/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/clients/ClientRegistrationService.java
@@ -219,89 +219,102 @@ public class ClientRegistrationService {
@Produces(MediaType.TEXT_HTML)
@Path("/")
public Response registerForm(@FormParam("client_name") String appName,
- @FormParam("client_type") String appType,
- @FormParam("client_audience") String audience,
- @FormParam("client_redirectURI") String redirectURI,
- @FormParam("client_homeRealm") String homeRealm
+ @FormParam("client_type") String appType,
+ @FormParam("client_audience") String audience,
+ @FormParam("client_redirectURI") String redirectURI,
+ @FormParam("client_logoutURI") String logoutURI,
+ @FormParam("client_homeRealm") String homeRealm
) {
-
- // Client Name
- if (StringUtils.isEmpty(appName)) {
- return invalidRegistrationResponse("The client name must not be empty");
- }
- // Client Type
- if (StringUtils.isEmpty(appType)) {
- return invalidRegistrationResponse("The client type must not be empty");
- }
- if (!("confidential".equals(appType) || "public".equals(appType))) {
- return invalidRegistrationResponse("An invalid client type was specified: " + appType);
- }
- // Client ID
- String clientId = generateClientId();
- boolean isConfidential = "confidential".equals(appType);
- // Client Secret
- String clientSecret = isConfidential
- ? generateClientSecret()
- : null;
-
- Client newClient = new Client(clientId, clientSecret, isConfidential, appName);
-
- // User who registered this client
- String userName = sc.getUserPrincipal().getName();
- UserSubject userSubject = new OidcUserSubject(userName);
- newClient.setResourceOwnerSubject(userSubject);
-
- // Client Registration Time
- newClient.setRegisteredAt(System.currentTimeMillis() / 1000);
-
- // Client Realm
- if (homeRealm != null) {
- newClient.setHomeRealm(homeRealm);
- if (homeRealms.containsKey(homeRealm)) {
- newClient.getProperties().put("homeRealmAlias", homeRealms.get(homeRealm));
+ try {
+ // Client Name
+ if (StringUtils.isEmpty(appName)) {
+ throwInvalidRegistrationException("The client name must not be empty");
}
- }
-
- // Client Redirect URIs
- if (!StringUtils.isEmpty(redirectURI)) {
- String[] allUris = redirectURI.trim().split(" ");
- List<String> redirectUris = new LinkedList<String>();
- for (String uri : allUris) {
- if (!StringUtils.isEmpty(uri)) {
- if (!isValidURI(uri, false)) {
- return invalidRegistrationResponse("An invalid redirect URI was specified: " + uri);
+ // Client Type
+ if (StringUtils.isEmpty(appType)) {
+ throwInvalidRegistrationException("The client type must not be empty");
+ }
+ if (!("confidential".equals(appType) || "public".equals(appType))) {
+ throwInvalidRegistrationException("An invalid client type was specified: " + appType);
+ }
+ // Client ID
+ String clientId = generateClientId();
+ boolean isConfidential = "confidential".equals(appType);
+ // Client Secret
+ String clientSecret = isConfidential
+ ? generateClientSecret()
+ : null;
+
+ Client newClient = new Client(clientId, clientSecret, isConfidential, appName);
+
+ // User who registered this client
+ String userName = sc.getUserPrincipal().getName();
+ UserSubject userSubject = new OidcUserSubject(userName);
+ newClient.setResourceOwnerSubject(userSubject);
+
+ // Client Registration Time
+ newClient.setRegisteredAt(System.currentTimeMillis() / 1000);
+
+ // Client Realm
+ if (homeRealm != null) {
+ newClient.setHomeRealm(homeRealm);
+ if (homeRealms.containsKey(homeRealm)) {
+ newClient.getProperties().put("homeRealmAlias", homeRealms.get(homeRealm));
+ }
+ }
+
+ // Client Redirect URIs
+ if (!StringUtils.isEmpty(redirectURI)) {
+ String[] allUris = redirectURI.trim().split(" ");
+ List<String> redirectUris = new LinkedList<String>();
+ for (String uri : allUris) {
+ if (!StringUtils.isEmpty(uri)) {
+ if (!isValidURI(uri, false)) {
+ throwInvalidRegistrationException("An invalid redirect URI was specified: " + uri);
+ }
+ redirectUris.add(uri);
}
- redirectUris.add(uri);
}
+ newClient.setRedirectUris(redirectUris);
}
- newClient.setRedirectUris(redirectUris);
- }
-
- // Client Audience URIs
- if (!StringUtils.isEmpty(audience)) {
- String[] auds = audience.trim().split(" ");
- List<String> registeredAuds = new LinkedList<String>();
- for (String aud : auds) {
- if (!StringUtils.isEmpty(aud)) {
- if (!isValidURI(aud, true)) {
- return invalidRegistrationResponse("An invalid audience URI was specified: " + aud);
+ // Client Logout URI
+ if (!StringUtils.isEmpty(logoutURI)) {
+ if (!isValidURI(logoutURI, false)) {
+ throwInvalidRegistrationException("An invalid logout URI was specified: " + logoutURI);
+ }
+ //TODO: replace this code with newClient.setLogoutUri() once it becomes available
+ newClient.getProperties().put("client_logout_uri", logoutURI);
+ }
+
+ // Client Audience URIs
+ if (!StringUtils.isEmpty(audience)) {
+ String[] auds = audience.trim().split(" ");
+ List<String> registeredAuds = new LinkedList<String>();
+ for (String aud : auds) {
+ if (!StringUtils.isEmpty(aud)) {
+ if (!isValidURI(aud, true)) {
+ throwInvalidRegistrationException("An invalid audience URI was specified: " + aud);
+ }
+ registeredAuds.add(aud);
}
- registeredAuds.add(aud);
}
+ newClient.setRegisteredAudiences(registeredAuds);
}
- newClient.setRegisteredAudiences(registeredAuds);
- }
-
- // Client Scopes
- if (clientScopes != null && !clientScopes.isEmpty()) {
- newClient.setRegisteredScopes(new ArrayList<String>(clientScopes.keySet()));
+
+ // Client Scopes
+ if (clientScopes != null && !clientScopes.isEmpty()) {
+ newClient.setRegisteredScopes(new ArrayList<String>(clientScopes.keySet()));
+ }
+ return Response.ok(registerNewClient(newClient)).build();
+ } catch (InvalidRegistrationException ex) {
+ // For the view handlers to handle it
+ return Response.ok(new InvalidRegistration(ex.getMessage())).build();
}
-
- return Response.ok(registerNewClient(newClient)).build();
}
- private Response invalidRegistrationResponse(String error) {
- return Response.ok(new InvalidRegistration(error)).build();
+
+ private void throwInvalidRegistrationException(String error) {
+ throw new InvalidRegistrationException(error);
}
private boolean isValidURI(String uri, boolean requireHttps) {
http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/620b6b95/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/clients/InvalidRegistrationException.java
----------------------------------------------------------------------
diff --git a/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/clients/InvalidRegistrationException.java b/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/clients/InvalidRegistrationException.java
new file mode 100644
index 0000000..945fd4f
--- /dev/null
+++ b/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/clients/InvalidRegistrationException.java
@@ -0,0 +1,28 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+package org.apache.cxf.fediz.service.oidc.clients;
+
+public class InvalidRegistrationException extends RuntimeException {
+ private static final long serialVersionUID = 1L;
+
+ public InvalidRegistrationException(String message) {
+ super(message);
+ }
+}
\ No newline at end of file
http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/620b6b95/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/logout/LogoutHandler.java
----------------------------------------------------------------------
diff --git a/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/logout/LogoutHandler.java b/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/logout/LogoutHandler.java
new file mode 100644
index 0000000..3d4e1ba
--- /dev/null
+++ b/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/logout/LogoutHandler.java
@@ -0,0 +1,26 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.fediz.service.oidc.logout;
+
+import org.apache.cxf.rs.security.oauth2.common.Client;
+import org.apache.cxf.rs.security.oauth2.common.UserSubject;
+
+public interface LogoutHandler {
+ void handleLogout(Client client, UserSubject subject);
+}
http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/620b6b95/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/logout/LogoutService.java
----------------------------------------------------------------------
diff --git a/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/logout/LogoutService.java b/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/logout/LogoutService.java
new file mode 100644
index 0000000..6583f81
--- /dev/null
+++ b/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/logout/LogoutService.java
@@ -0,0 +1,149 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.fediz.service.oidc.logout;
+
+import java.net.URI;
+import java.util.Collections;
+import java.util.List;
+
+import javax.ws.rs.BadRequestException;
+import javax.ws.rs.GET;
+import javax.ws.rs.POST;
+import javax.ws.rs.Path;
+import javax.ws.rs.core.Context;
+import javax.ws.rs.core.MultivaluedMap;
+import javax.ws.rs.core.Response;
+import javax.ws.rs.core.UriBuilder;
+
+import org.apache.cxf.fediz.service.oidc.FedizSubjectCreator;
+import org.apache.cxf.jaxrs.ext.MessageContext;
+import org.apache.cxf.rs.security.oauth2.common.Client;
+import org.apache.cxf.rs.security.oauth2.common.UserSubject;
+import org.apache.cxf.rs.security.oauth2.provider.OAuthDataProvider;
+import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
+
+@Path("/logout")
+public class LogoutService {
+ private static final String CLIENT_LOGOUT_URI = "client_logout_uri";
+ @Context
+ private MessageContext mc;
+ private String relativeIdpLogoutUri;
+ private OAuthDataProvider dataProvider;
+ private FedizSubjectCreator subjectCreator = new FedizSubjectCreator();
+
+ private List<LogoutHandler> logoutHandlers;
+
+ @POST
+ public Response initiateLogoutPost(MultivaluedMap<String, String> params) {
+ return doInitiateLogout(params);
+ }
+ @GET
+ public Response initiateLogoutGet() {
+ return doInitiateLogout(mc.getUriInfo().getQueryParameters());
+ }
+
+ protected Response doInitiateLogout(MultivaluedMap<String, String> params) {
+ Client client = getClient(params);
+ UserSubject subject = subjectCreator.createUserSubject(mc, params);
+
+ if (logoutHandlers != null) {
+
+ for (LogoutHandler handler : logoutHandlers) {
+ handler.handleLogout(client, subject);
+ }
+ }
+ // Clear OIDC session now if core IDP will itself redirect to the client logout URI
+
+ // Redirect to the core IDP
+ URI idpLogoutUri = getAbsoluteIdpLogoutUri(client);
+ return Response.seeOther(idpLogoutUri).build();
+ }
+
+ @GET
+ @Path("/finalize")
+ protected Response finalizeLogoutGet() {
+ // This method won't be needed if IDP will itself redirect to the client logout URI
+ return doFinalizeLogout(mc.getUriInfo().getQueryParameters());
+ }
+ @POST
+ @Path("/finalize")
+ protected Response finalizeLogoutPost(MultivaluedMap<String, String> params) {
+ // This method won't be needed if IDP will itself redirect to the client logout URI
+ return doFinalizeLogout(params);
+ }
+ protected Response doFinalizeLogout(MultivaluedMap<String, String> params) {
+
+ // This method won't be needed if IDP will itself redirect to the client logout URI
+
+
+ // Ensure this method is not called by skipping the initiate logout which is
+ // why it may be simpler let IDP redirect directly to the client logout uri ?
+
+ // Clear the OIDC session
+
+ Client client = getClient(params);
+ URI clientLogoutUri = getClientLogoutUri(client);
+ return Response.seeOther(clientLogoutUri).build();
+ }
+
+ private URI getClientLogoutUri(Client client) {
+ return URI.create(client.getProperties().get(CLIENT_LOGOUT_URI));
+ }
+ private Client getClient(MultivaluedMap<String, String> params) {
+ String clientId = params.getFirst(OAuthConstants.CLIENT_ID);
+ if (clientId == null) {
+ throw new BadRequestException();
+ }
+ Client c = dataProvider.getClient(clientId);
+ if (c == null) {
+ throw new BadRequestException();
+ }
+ if (c.getProperties().get(CLIENT_LOGOUT_URI) == null) {
+ //TODO: Possibly default to something ?
+ throw new BadRequestException();
+ }
+ return c;
+ }
+ private URI getAbsoluteIdpLogoutUri(Client client) {
+ UriBuilder ub = mc.getUriInfo().getAbsolutePathBuilder();
+ ub.segment(relativeIdpLogoutUri);
+ //TODO: include a logout uri as a uri parameter, either
+ // 1. "/finalize" URI for the IDP to redirect to this service again
+ // or
+ // 2. may be let IDP redirect straight to getClientLogoutUri(client) ?
+ return ub.build();
+ }
+
+ public void setRelativeIdpLogoutUri(String relativeIdpLogoutUri) {
+ this.relativeIdpLogoutUri = relativeIdpLogoutUri;
+ }
+
+ public void setLogoutHandlers(List<LogoutHandler> logoutHandlers) {
+ this.logoutHandlers = logoutHandlers;
+ }
+ public void setLogoutHandler(LogoutHandler logoutHandler) {
+ setLogoutHandlers(Collections.singletonList(logoutHandler));
+ }
+ public void setDataProvider(OAuthDataProvider dataProvider) {
+ this.dataProvider = dataProvider;
+ }
+ public void setSubjectCreator(FedizSubjectCreator subjectCreator) {
+ this.subjectCreator = subjectCreator;
+ }
+}
http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/620b6b95/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/logout/TokenCleanupHandler.java
----------------------------------------------------------------------
diff --git a/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/logout/TokenCleanupHandler.java b/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/logout/TokenCleanupHandler.java
new file mode 100644
index 0000000..3a3c356
--- /dev/null
+++ b/services/oidc/src/main/java/org/apache/cxf/fediz/service/oidc/logout/TokenCleanupHandler.java
@@ -0,0 +1,55 @@
+/**
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements. See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership. The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License. You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied. See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+package org.apache.cxf.fediz.service.oidc.logout;
+
+import java.util.List;
+
+import org.apache.cxf.rs.security.oauth2.common.Client;
+import org.apache.cxf.rs.security.oauth2.common.ServerAccessToken;
+import org.apache.cxf.rs.security.oauth2.common.UserSubject;
+import org.apache.cxf.rs.security.oauth2.provider.OAuthDataProvider;
+import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
+
+public class TokenCleanupHandler implements LogoutHandler {
+ private OAuthDataProvider dataProvider;
+ private boolean removeTokensForAllClients;
+
+ @Override
+ public void handleLogout(Client client, UserSubject subject) {
+ if (removeTokensForAllClients) {
+ client = null;
+ }
+ List<ServerAccessToken> accessTokens = dataProvider.getAccessTokens(client, subject);
+ for (ServerAccessToken at : accessTokens) {
+ dataProvider.revokeToken(client, at.getTokenKey(), OAuthConstants.ACCESS_TOKEN);
+ // Removing the access token should remove the refresh token which links to it
+ // If necessary, refresh tokens can also be explicitly revoked
+ }
+
+ }
+
+ public void setDataProvider(OAuthDataProvider dataProvider) {
+ this.dataProvider = dataProvider;
+ }
+
+ public void setRemoveTokensForAllClients(boolean removeTokensForAllClients) {
+ this.removeTokensForAllClients = removeTokensForAllClients;
+ }
+
+}
http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/620b6b95/services/oidc/src/main/webapp/WEB-INF/applicationContext.xml
----------------------------------------------------------------------
diff --git a/services/oidc/src/main/webapp/WEB-INF/applicationContext.xml b/services/oidc/src/main/webapp/WEB-INF/applicationContext.xml
index 97b27a8..51bb044 100644
--- a/services/oidc/src/main/webapp/WEB-INF/applicationContext.xml
+++ b/services/oidc/src/main/webapp/WEB-INF/applicationContext.xml
@@ -70,10 +70,15 @@
<property name="services" ref="oidcServices"/>
</bean>
+ <bean id="logoutService" class="org.apache.cxf.fediz.service.oidc.logout.LogoutService">
+ <property name="dataProvider" ref="oauthProvider"/>
+ </bean>
+
<!-- Service supporting all OIDC Core flows -->
<jaxrs:server address="/idp">
<jaxrs:serviceBeans>
<ref bean="authorizationService"/>
+ <ref bean="logoutService"/>
</jaxrs:serviceBeans>
<jaxrs:providers>
<ref bean="viewProvider"/>
http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/620b6b95/services/oidc/src/main/webapp/WEB-INF/views/client.jsp
----------------------------------------------------------------------
diff --git a/services/oidc/src/main/webapp/WEB-INF/views/client.jsp b/services/oidc/src/main/webapp/WEB-INF/views/client.jsp
index 6bdd74d..6940715 100644
--- a/services/oidc/src/main/webapp/WEB-INF/views/client.jsp
+++ b/services/oidc/src/main/webapp/WEB-INF/views/client.jsp
@@ -130,7 +130,7 @@
<b>Redirect URL</b>
</td>
<td>
-<% if(client.getRedirectUris() != null) {
+<% if (client.getRedirectUris() != null) {
for (String redirectURI : client.getRedirectUris()) {
%>
<%= redirectURI %><br/>
@@ -143,7 +143,7 @@
<b>Audience URL</b>
</td>
<td>
-<% if(client.getRegisteredAudiences() != null) {
+<% if (client.getRegisteredAudiences() != null) {
for (String audURI : client.getRegisteredAudiences()) {
%>
<%= audURI %><br/>
@@ -151,6 +151,16 @@
} %>
</td>
</tr>
+<tr>
+<td>
+<b>Logout URL</b>
+</td>
+<td>
+<% if (client.getProperties().get("client_logout_uri") != null) { %>
+ <%= client.getProperties().get("client_logout_uri") %>
+<% } %>
+</td>
+</tr>
</table>
<br/>
<p>
http://git-wip-us.apache.org/repos/asf/cxf-fediz/blob/620b6b95/services/oidc/src/main/webapp/WEB-INF/views/registerClient.jsp
----------------------------------------------------------------------
diff --git a/services/oidc/src/main/webapp/WEB-INF/views/registerClient.jsp b/services/oidc/src/main/webapp/WEB-INF/views/registerClient.jsp
index eb51409..47796c3 100644
--- a/services/oidc/src/main/webapp/WEB-INF/views/registerClient.jsp
+++ b/services/oidc/src/main/webapp/WEB-INF/views/registerClient.jsp
@@ -91,6 +91,12 @@ input, select, button {
id="input_7" data-type="input-textbox" />
</div>
<div class="form-line">
+ <label for="client_logoutURI" id="label_logout" class="form-label"> Logout URL </label>
+ <input type="text" value="" size="40" name="client_logoutURI"
+ placeholder="URL of the client to finalize OIDC logout process"
+ id="input_6" data-type="input-textbox" />
+ </div>
+ <div class="form-line">
<label for="client_homeRealm" id="label_homeRealm" class="form-label"> Home Realm </label>
<select name="client_homeRealm" id="input_homeRealm">
<option value="" selected>Default - User selection at login</option>