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>