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 2015/04/22 18:15:56 UTC

cxf git commit: [CXF-6280] Prototyping an Implcit confidenatial grant service which returns a token directly to a JS client which is used by a huna user to copy tokens to confidential clients

Repository: cxf
Updated Branches:
  refs/heads/master a802b442c -> 982bdbc9d


[CXF-6280] Prototyping an Implcit confidenatial grant service which returns a token directly to a JS client which is used by a huna user to copy tokens to confidential clients


Project: http://git-wip-us.apache.org/repos/asf/cxf/repo
Commit: http://git-wip-us.apache.org/repos/asf/cxf/commit/982bdbc9
Tree: http://git-wip-us.apache.org/repos/asf/cxf/tree/982bdbc9
Diff: http://git-wip-us.apache.org/repos/asf/cxf/diff/982bdbc9

Branch: refs/heads/master
Commit: 982bdbc9dc2127906d0a1ca06ae181c87c38bbfa
Parents: a802b44
Author: Sergey Beryozkin <sb...@talend.com>
Authored: Wed Apr 22 17:15:39 2015 +0100
Committer: Sergey Beryozkin <sb...@talend.com>
Committed: Wed Apr 22 17:15:39 2015 +0100

----------------------------------------------------------------------
 .../oauth2/filters/OAuthRequestFilter.java      |  16 +-
 .../services/AbstractImplicitGrantService.java  | 163 +++++++++++++++++++
 .../ImplicitConfidentialGrantService.java       |  51 ++++++
 .../oauth2/services/ImplicitGrantService.java   | 130 +--------------
 .../services/RedirectionBasedGrantService.java  |   4 +-
 .../security/oauth2/utils/OAuthConstants.java   |   4 +
 6 files changed, 236 insertions(+), 132 deletions(-)
----------------------------------------------------------------------


http://git-wip-us.apache.org/repos/asf/cxf/blob/982bdbc9/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java
index fe638be..22af72c 100644
--- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java
+++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/filters/OAuthRequestFilter.java
@@ -28,7 +28,6 @@ import javax.annotation.Priority;
 import javax.servlet.http.HttpServletRequest;
 import javax.ws.rs.HttpMethod;
 import javax.ws.rs.Priorities;
-import javax.ws.rs.WebApplicationException;
 import javax.ws.rs.container.ContainerRequestContext;
 import javax.ws.rs.container.ContainerRequestFilter;
 import javax.ws.rs.container.PreMatching;
@@ -40,6 +39,7 @@ import javax.ws.rs.ext.Provider;
 import org.apache.cxf.common.logging.LogUtils;
 import org.apache.cxf.common.security.SimplePrincipal;
 import org.apache.cxf.jaxrs.provider.FormEncodingProvider;
+import org.apache.cxf.jaxrs.utils.ExceptionUtils;
 import org.apache.cxf.jaxrs.utils.FormUtils;
 import org.apache.cxf.jaxrs.utils.JAXRSUtils;
 import org.apache.cxf.message.Message;
@@ -71,6 +71,7 @@ public class OAuthRequestFilter extends AbstractAccessTokenValidator
     private boolean checkFormData;
     private List<String> requiredScopes = Collections.emptyList();
     private boolean allPermissionsMatch;
+    private boolean blockPublicClients;
     
     public void filter(ContainerRequestContext context) {
         validateRequest(JAXRSUtils.getCurrentMessage());
@@ -111,7 +112,7 @@ public class OAuthRequestFilter extends AbstractAccessTokenValidator
             || !requiredScopes.isEmpty() && requiredScopes.size() != matchingPermissions.size()) {
             String message = "Client has no valid permissions";
             LOG.warning(message);
-            throw new WebApplicationException(403);
+            throw ExceptionUtils.toForbiddenException(null, null);
         }
       
         if (accessTokenV.getClientIpAddress() != null) {
@@ -119,9 +120,14 @@ public class OAuthRequestFilter extends AbstractAccessTokenValidator
             if (remoteAddress == null || accessTokenV.getClientIpAddress().matches(remoteAddress)) {
                 String message = "Client IP Address is invalid";
                 LOG.warning(message);
-                throw new WebApplicationException(403);
+                throw ExceptionUtils.toForbiddenException(null, null);
             }
         }
+        if (blockPublicClients && !accessTokenV.isClientConfidential()) {
+            String message = "Only Confidential Clients are supported";
+            LOG.warning(message);
+            throw ExceptionUtils.toForbiddenException(null, null);
+        }
         
         // Create the security context and make it available on the message
         SecurityContext sc = createSecurityContext(req, accessTokenV);
@@ -273,5 +279,9 @@ public class OAuthRequestFilter extends AbstractAccessTokenValidator
     public void setAllPermissionsMatch(boolean allPermissionsMatch) {
         this.allPermissionsMatch = allPermissionsMatch;
     }
+
+    public void setBlockPublicClients(boolean blockPublicClients) {
+        this.blockPublicClients = blockPublicClients;
+    }
     
 }

http://git-wip-us.apache.org/repos/asf/cxf/blob/982bdbc9/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractImplicitGrantService.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractImplicitGrantService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractImplicitGrantService.java
new file mode 100644
index 0000000..63fcfa2
--- /dev/null
+++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/AbstractImplicitGrantService.java
@@ -0,0 +1,163 @@
+/**
+ * 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.rs.security.oauth2.services;
+
+import java.net.URI;
+import java.util.LinkedList;
+import java.util.List;
+import java.util.Map;
+
+import javax.ws.rs.core.Response;
+
+import org.apache.cxf.common.util.StringUtils;
+import org.apache.cxf.jaxrs.utils.HttpUtils;
+import org.apache.cxf.rs.security.oauth2.common.AccessTokenRegistration;
+import org.apache.cxf.rs.security.oauth2.common.Client;
+import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken;
+import org.apache.cxf.rs.security.oauth2.common.OAuthRedirectionState;
+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.AccessTokenResponseFilter;
+import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
+import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils;
+
+
+public abstract class AbstractImplicitGrantService extends RedirectionBasedGrantService {
+    // For a client to validate that this client is a targeted recipient.
+    private boolean reportClientId;
+    private List<AccessTokenResponseFilter> responseHandlers = new LinkedList<AccessTokenResponseFilter>();
+    
+    protected AbstractImplicitGrantService(String supportedResponseType,
+                                           String supportedGrantType) {
+        super(supportedResponseType, supportedGrantType);
+    }
+    
+    protected Response createGrant(OAuthRedirectionState state,
+                                   Client client,
+                                   List<String> requestedScope,
+                                   List<String> approvedScope,
+                                   UserSubject userSubject,
+                                   ServerAccessToken preAuthorizedToken) {
+        ServerAccessToken token = null;
+        if (preAuthorizedToken == null) {
+            AccessTokenRegistration reg = new AccessTokenRegistration();
+            reg.setClient(client);
+            reg.setGrantType(super.getSupportedGrantType());
+            reg.setSubject(userSubject);
+            reg.setRequestedScope(requestedScope);        
+            if (approvedScope != null && approvedScope.isEmpty()) {
+                // no down-scoping done by a user, all of the requested scopes have been authorized
+                reg.setApprovedScope(requestedScope);
+            } else {
+                reg.setApprovedScope(approvedScope);
+            }
+            reg.setAudience(state.getAudience());
+            token = getDataProvider().createAccessToken(reg);
+        } else {
+            token = preAuthorizedToken;
+        }
+        
+        ClientAccessToken clientToken = OAuthUtils.toClientAccessToken(token, isWriteOptionalParameters());
+        processClientAccessToken(clientToken, token);
+   
+        // return the token by appending it as a fragment parameter to the redirect URI
+        
+        StringBuilder sb = getUriWithFragment(state.getRedirectUri());
+        
+        sb.append(OAuthConstants.ACCESS_TOKEN).append("=").append(clientToken.getTokenKey());
+        if (state.getState() != null) {
+            sb.append("&");
+            sb.append(OAuthConstants.STATE).append("=").append(state.getState());   
+        }
+        sb.append("&")
+            .append(OAuthConstants.ACCESS_TOKEN_TYPE).append("=").append(clientToken.getTokenType());
+        
+        if (isWriteOptionalParameters()) {
+            sb.append("&").append(OAuthConstants.ACCESS_TOKEN_EXPIRES_IN)
+                .append("=").append(clientToken.getExpiresIn());
+            if (!StringUtils.isEmpty(clientToken.getApprovedScope())) {
+                sb.append("&").append(OAuthConstants.SCOPE).append("=")
+                    .append(HttpUtils.queryEncode(clientToken.getApprovedScope()));
+            }
+            for (Map.Entry<String, String> entry : clientToken.getParameters().entrySet()) {
+                sb.append("&").append(entry.getKey()).append("=").append(HttpUtils.queryEncode(entry.getValue()));
+            }
+        }
+        if (token.getRefreshToken() != null) {
+            processRefreshToken(sb, token.getRefreshToken());
+        }
+        if (reportClientId) {
+            sb.append("&").append(OAuthConstants.CLIENT_ID).append("=").append(client.getClientId());
+        }
+        
+        return Response.seeOther(URI.create(sb.toString())).build();
+    }
+    protected void processRefreshToken(StringBuilder sb, String refreshToken) {
+        LOG.warning("Implicit grant tokens MUST not have refresh tokens, refresh token will not be reported");
+    }
+
+    protected void processClientAccessToken(ClientAccessToken clientToken, ServerAccessToken serverToken) {
+        for (AccessTokenResponseFilter filter : responseHandlers) {
+            filter.process(clientToken, serverToken); 
+        }
+    }
+    protected Response createErrorResponse(String state,
+                                           String redirectUri,
+                                           String error) {
+        StringBuilder sb = getUriWithFragment(redirectUri);
+        sb.append(OAuthConstants.ERROR_KEY).append("=").append(error);
+        if (state != null) {
+            sb.append("&");
+            sb.append(OAuthConstants.STATE).append("=").append(state);   
+        }
+        
+        return Response.seeOther(URI.create(sb.toString())).build();
+    }
+    
+    private StringBuilder getUriWithFragment(String redirectUri) {
+        StringBuilder sb = new StringBuilder();
+        sb.append(redirectUri);
+        sb.append("#");
+        return sb;
+    }
+
+    public void setReportClientId(boolean reportClientId) {
+        this.reportClientId = reportClientId;
+    }
+    
+    public void setResponseFilters(List<AccessTokenResponseFilter> handlers) {
+        this.responseHandlers = handlers;
+    }
+    
+    public void setResponseFilter(AccessTokenResponseFilter responseHandler) {
+        responseHandlers.add(responseHandler);
+    }
+
+    @Override
+    protected boolean canRedirectUriBeEmpty(Client c) {
+        return false;
+    }
+    @Override
+    protected boolean canSupportPublicClient(Client c) {
+        return true;
+    }
+}
+
+

http://git-wip-us.apache.org/repos/asf/cxf/blob/982bdbc9/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitConfidentialGrantService.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitConfidentialGrantService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitConfidentialGrantService.java
new file mode 100644
index 0000000..1847fc7
--- /dev/null
+++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitConfidentialGrantService.java
@@ -0,0 +1,51 @@
+/**
+ * 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.rs.security.oauth2.services;
+
+import javax.ws.rs.Path;
+
+import org.apache.cxf.rs.security.oauth2.common.Client;
+import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
+
+
+/**
+ * Redirection-based Implicit Grant Service which returns tokens for the confidential clients
+ * directly to a human user. 
+ */
+@Path("/implicit-confidential")
+public class ImplicitConfidentialGrantService extends AbstractImplicitGrantService {
+
+    public ImplicitConfidentialGrantService() {
+        super(OAuthConstants.TOKEN_RESPONSE_TYPE, OAuthConstants.IMPLICIT_CONFIDENTIAL_GRANT);
+    }
+    
+    @Override
+    protected void processRefreshToken(StringBuilder sb, String refreshToken) {
+        sb.append("&").append(OAuthConstants.REFRESH_TOKEN).append("=").append(refreshToken);
+    }
+    @Override
+    protected boolean canSupportPublicClient(Client c) {
+        return false;
+    }
+    
+    
+}
+
+

http://git-wip-us.apache.org/repos/asf/cxf/blob/982bdbc9/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitGrantService.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitGrantService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitGrantService.java
index 10542b8..a73e118 100644
--- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitGrantService.java
+++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/ImplicitGrantService.java
@@ -19,25 +19,9 @@
 
 package org.apache.cxf.rs.security.oauth2.services;
 
-import java.net.URI;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-
 import javax.ws.rs.Path;
-import javax.ws.rs.core.Response;
 
-import org.apache.cxf.common.util.StringUtils;
-import org.apache.cxf.jaxrs.utils.HttpUtils;
-import org.apache.cxf.rs.security.oauth2.common.AccessTokenRegistration;
-import org.apache.cxf.rs.security.oauth2.common.Client;
-import org.apache.cxf.rs.security.oauth2.common.ClientAccessToken;
-import org.apache.cxf.rs.security.oauth2.common.OAuthRedirectionState;
-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.AccessTokenResponseFilter;
 import org.apache.cxf.rs.security.oauth2.utils.OAuthConstants;
-import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils;
 
 
 /**
@@ -50,121 +34,11 @@ import org.apache.cxf.rs.security.oauth2.utils.OAuthUtils;
  * authorization code or implicit grant.
  */
 @Path("/authorize-implicit")
-public class ImplicitGrantService extends RedirectionBasedGrantService {
-    // For a client to validate that this client is a targeted recipient.
-    private boolean reportClientId;
-    private List<AccessTokenResponseFilter> responseHandlers = new LinkedList<AccessTokenResponseFilter>();
-    
+public class ImplicitGrantService extends AbstractImplicitGrantService {
+
     public ImplicitGrantService() {
         super(OAuthConstants.TOKEN_RESPONSE_TYPE, OAuthConstants.IMPLICIT_GRANT);
     }
-    
-    protected Response createGrant(OAuthRedirectionState state,
-                                   Client client,
-                                   List<String> requestedScope,
-                                   List<String> approvedScope,
-                                   UserSubject userSubject,
-                                   ServerAccessToken preAuthorizedToken) {
-        ServerAccessToken token = null;
-        if (preAuthorizedToken == null) {
-            AccessTokenRegistration reg = new AccessTokenRegistration();
-            reg.setClient(client);
-            reg.setGrantType(OAuthConstants.IMPLICIT_GRANT);
-            reg.setSubject(userSubject);
-            reg.setRequestedScope(requestedScope);        
-            if (approvedScope != null && approvedScope.isEmpty()) {
-                // no down-scoping done by a user, all of the requested scopes have been authorized
-                reg.setApprovedScope(requestedScope);
-            } else {
-                reg.setApprovedScope(approvedScope);
-            }
-            reg.setAudience(state.getAudience());
-            token = getDataProvider().createAccessToken(reg);
-        } else {
-            token = preAuthorizedToken;
-        }
-        if (token.getRefreshToken() != null) {
-            LOG.warning("Implicit grant tokens MUST not have refresh tokens, refresh token will not be reported");
-        }
-        ClientAccessToken clientToken = OAuthUtils.toClientAccessToken(token, isWriteOptionalParameters());
-        processClientAccessToken(clientToken, token);
-   
-        // return the token by appending it as a fragment parameter to the redirect URI
-        
-        StringBuilder sb = getUriWithFragment(state.getRedirectUri());
-        
-        sb.append(OAuthConstants.ACCESS_TOKEN).append("=").append(clientToken.getTokenKey());
-        if (state.getState() != null) {
-            sb.append("&");
-            sb.append(OAuthConstants.STATE).append("=").append(state.getState());   
-        }
-        sb.append("&")
-            .append(OAuthConstants.ACCESS_TOKEN_TYPE).append("=").append(clientToken.getTokenType());
-        
-        if (isWriteOptionalParameters()) {
-            sb.append("&").append(OAuthConstants.ACCESS_TOKEN_EXPIRES_IN)
-                .append("=").append(clientToken.getExpiresIn());
-            if (!StringUtils.isEmpty(clientToken.getApprovedScope())) {
-                sb.append("&").append(OAuthConstants.SCOPE).append("=")
-                    .append(HttpUtils.queryEncode(clientToken.getApprovedScope()));
-            }
-            for (Map.Entry<String, String> entry : clientToken.getParameters().entrySet()) {
-                sb.append("&").append(entry.getKey()).append("=").append(HttpUtils.queryEncode(entry.getValue()));
-            }
-        }
-        if (reportClientId) {
-            sb.append("&").append(OAuthConstants.CLIENT_ID).append("=").append(client.getClientId());
-        }
-        
-        return Response.seeOther(URI.create(sb.toString())).build();
-    }
-    protected void processClientAccessToken(ClientAccessToken clientToken, ServerAccessToken serverToken) {
-        for (AccessTokenResponseFilter filter : responseHandlers) {
-            filter.process(clientToken, serverToken); 
-        }
-    }
-    protected Response createErrorResponse(String state,
-                                           String redirectUri,
-                                           String error) {
-        StringBuilder sb = getUriWithFragment(redirectUri);
-        sb.append(OAuthConstants.ERROR_KEY).append("=").append(error);
-        if (state != null) {
-            sb.append("&");
-            sb.append(OAuthConstants.STATE).append("=").append(state);   
-        }
-        
-        return Response.seeOther(URI.create(sb.toString())).build();
-    }
-    
-    private StringBuilder getUriWithFragment(String redirectUri) {
-        StringBuilder sb = new StringBuilder();
-        sb.append(redirectUri);
-        sb.append("#");
-        return sb;
-    }
-
-    public void setReportClientId(boolean reportClientId) {
-        this.reportClientId = reportClientId;
-    }
-    
-    public void setResponseFilters(List<AccessTokenResponseFilter> handlers) {
-        this.responseHandlers = handlers;
-    }
-    
-    public void setResponseFilter(AccessTokenResponseFilter responseHandler) {
-        responseHandlers.add(responseHandler);
-    }
-
-    @Override
-    protected boolean canSupportPublicClient(Client c) {
-        return true;
-    }
-    
-    @Override
-    protected boolean canRedirectUriBeEmpty(Client c) {
-        return false;
-    }
-    
 }
 
 

http://git-wip-us.apache.org/repos/asf/cxf/blob/982bdbc9/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java
index 306df30..be09cc0 100644
--- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java
+++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/services/RedirectionBasedGrantService.java
@@ -431,7 +431,9 @@ public abstract class RedirectionBasedGrantService extends AbstractOAuthService
     protected Client getClient(MultivaluedMap<String, String> params) {
         return this.getClient(params.getFirst(OAuthConstants.CLIENT_ID));
     }
-
+    protected String getSupportedGrantType() {
+        return this.supportedGrantType;
+    }
     public void setResourceOwnerNameProvider(ResourceOwnerNameProvider resourceOwnerNameProvider) {
         this.resourceOwnerNameProvider = resourceOwnerNameProvider;
     }

http://git-wip-us.apache.org/repos/asf/cxf/blob/982bdbc9/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java
----------------------------------------------------------------------
diff --git a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java
index 945c8a9..9f4a801 100644
--- a/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java
+++ b/rt/rs/security/oauth-parent/oauth2/src/main/java/org/apache/cxf/rs/security/oauth2/utils/OAuthConstants.java
@@ -48,6 +48,10 @@ public final class OAuthConstants {
     public static final String RESOURCE_OWNER_GRANT = "password";
     public static final String REFRESH_TOKEN_GRANT = "refresh_token";
     
+    // CXF-specific grant
+    // The token is returned directly to a human user who copies it into a confidential client
+    public static final String IMPLICIT_CONFIDENTIAL_GRANT = "urn:ietf:params:oauth:grant-type:implicit-confidential";
+    
     // Well-known token types
     public static final String BEARER_TOKEN_TYPE = "bearer";
     public static final String HAWK_TOKEN_TYPE = "hawk";