You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@shindig.apache.org by wo...@apache.org on 2011/10/12 22:00:12 UTC

svn commit: r1182560 [2/3] - in /shindig/trunk: content/sampledata/ java/common/conf/ java/server/src/main/webapp/WEB-INF/ java/social-api/src/main/java/org/apache/shindig/social/core/oauth/ java/social-api/src/main/java/org/apache/shindig/social/core/...

Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Types.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Types.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Types.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Types.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,124 @@
+/*
+ * 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.shindig.social.core.oauth2;
+
+/**
+ * A collection of OAuth 2.0's enumerated types.
+ */
+public class OAuth2Types {
+
+  /**
+   * Enumerated error types in the OAuth 2.0 specification.
+   */
+  public static enum ErrorType {
+    INVALID_REQUEST("invalid_request"),
+    INVALID_CLIENT("invalid_client"),
+    INVALID_GRANT("invalid_grant"),
+    UNAUTHORIZED_CLIENT("unauthorized_client"),
+    UNSUPPORTED_GRANT_TYPE("unsupported_grant_type"),
+    INVALID_SCOPE("invalid_scope"), ACCESS_DENIED("access_denied"),
+    UNSUPPORTED_RESPONSE_TYPE("unsupported_response_type"),
+    SERVER_ERROR("server_error"),
+    TEMPORARILY_UNAVAILABLE("temporarily_unavailable");
+
+    private final String name;
+
+    private ErrorType(String name) {
+      this.name = name;
+    }
+
+    public String toString() {
+      return name;
+    }
+  }
+
+  /**
+   * Enumerated grant types in the OAuth 2.0 specification.
+   */
+  public static enum GrantType {
+    REFRESH_TOKEN("refresh_token"),
+    AUTHORIZATION_CODE("authorization_code"),
+    PASSWORD("password"),
+    CLIENT_CREDENTIALS("client_credentials"),
+    CUSTOM("custom");
+
+    private final String name;
+
+    private GrantType(String name) {
+      this.name = name;
+    }
+
+    public String toString() {
+      return name;
+    }
+  }
+
+  /**
+   * Enumerated response types in the OAuth 2.0 specification.
+   */
+  public static enum ResponseType {
+    CODE("code"), TOKEN("token");
+
+    private final String name;
+
+    private ResponseType(String name) {
+      this.name = name;
+    }
+
+    public String toString() {
+      return name;
+    }
+  }
+
+  /**
+   * Enumerated token types in the OAuth 2.0 specification.
+   */
+  public static enum CodeType {
+    AUTHORIZATION_CODE("authorization_code"),
+    ACCESS_TOKEN("access_token"),
+    REFRESH_TOKEN("refresh_token");
+
+    private final String name;
+
+    private CodeType(String name) {
+      this.name = name;
+    }
+
+    public String toString() {
+      return name;
+    }
+  }
+
+  /**
+   * Enumerated token types in the OAuth 2.0 specification.
+   */
+  public static enum TokenFormat {
+    BEARER("bearer"),
+    MAC("mac");
+
+    private final String name;
+
+    private TokenFormat(String name) {
+      this.name = name;
+    }
+
+    public String toString() {
+      return name;
+    }
+  }
+}
\ No newline at end of file

Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Utils.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Utils.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Utils.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/OAuth2Utils.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,59 @@
+/*
+ * 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.shindig.social.core.oauth2;
+
+import java.util.Map;
+
+import org.apache.shindig.common.uri.UriBuilder;
+
+/**
+ * Collection of utility classes to support OAuth 2.0 operations.
+ */
+public class OAuth2Utils {
+
+  /**
+   * Converts a Map<String, String> to a URL query string.
+   * 
+   * @param params represents the Map of query parameters
+   * 
+   * @return String is the URL encoded parameter String
+   */
+  public static String convertQueryString(Map<String, String> params) {
+    if (params == null) return "";
+    UriBuilder builder = new UriBuilder();
+    builder.addQueryParameters(params);
+    return builder.getQuery();
+  }
+
+  /**
+   * Normalizes a URL and parameters. If the URL already contains parameters,
+   * new parameters will be added properly.
+   * 
+   * @param URL is the base URL to normalize
+   * @param queryParams query parameters to add to the URL
+   * @param fragmentParams fragment params to add to the URL
+   */
+  public static String buildUrl(String url, Map<String, String> queryParams,
+      Map<String, String> fragmentParams) {
+    UriBuilder builder = new UriBuilder();
+    builder.setPath(url);
+    if (queryParams != null) builder.addQueryParameters(queryParams);
+    if (fragmentParams != null) builder.addFragmentParameters(fragmentParams);
+    return builder.toString();
+  }
+}
\ No newline at end of file

Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/AccessTokenRequestValidator.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/AccessTokenRequestValidator.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/AccessTokenRequestValidator.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/AccessTokenRequestValidator.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,102 @@
+/*
+ * 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.shindig.social.core.oauth2.validators;
+
+import org.apache.shindig.social.core.oauth2.OAuth2Client;
+import org.apache.shindig.social.core.oauth2.OAuth2DataService;
+import org.apache.shindig.social.core.oauth2.OAuth2Exception;
+import org.apache.shindig.social.core.oauth2.OAuth2NormalizedRequest;
+import org.apache.shindig.social.core.oauth2.OAuth2NormalizedResponse;
+import org.apache.shindig.social.core.oauth2.OAuth2Client.Flow;
+import org.apache.shindig.social.core.oauth2.OAuth2Types.ErrorType;
+
+import javax.servlet.http.HttpServletResponse;
+
+import java.util.ArrayList;
+import java.util.List;
+
+import com.google.inject.Inject;
+
+public class AccessTokenRequestValidator implements OAuth2RequestValidator {
+
+  private OAuth2DataService store = null;
+  private List<OAuth2GrantValidator> grantValidators; // grant validators
+
+  @Inject
+  public AccessTokenRequestValidator(OAuth2DataService store) {
+    this.grantValidators = new ArrayList<OAuth2GrantValidator>();
+    grantValidators.add(new AuthCodeGrantValidator(store));
+    grantValidators.add(new ClientCredentialsGrantValidator(store));
+    this.store = store;
+  }
+
+  public void validateRequest(OAuth2NormalizedRequest req)
+      throws OAuth2Exception {
+    if (req.getGrantType() != null) {
+      for (OAuth2GrantValidator validator : grantValidators) {
+        if (validator.getGrantType().equals(req.getGrantType())) {
+          validator.validateRequest(req);
+          return; // request validated
+        }
+      }
+      OAuth2NormalizedResponse response = new OAuth2NormalizedResponse();
+      response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+      response.setError(ErrorType.UNSUPPORTED_GRANT_TYPE.toString());
+      response.setErrorDescription("Unsupported grant type");
+      response.setBodyReturned(true);
+      throw new OAuth2Exception(response);
+    } else { // implicit flow does not include grant type
+      if (req.getResponseType() == null
+          || !req.getResponseType().equals("token")) {
+        OAuth2NormalizedResponse resp = new OAuth2NormalizedResponse();
+        resp.setError(ErrorType.UNSUPPORTED_RESPONSE_TYPE.toString());
+        resp.setErrorDescription("Unsupported response type");
+        resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
+        throw new OAuth2Exception(resp);
+      }
+      OAuth2Client client = store.getClient(req.getClientId());
+      if (client == null || client.getFlow() != Flow.IMPLICIT) {
+        OAuth2NormalizedResponse resp = new OAuth2NormalizedResponse();
+        resp.setError(ErrorType.INVALID_CLIENT.toString());
+        resp.setErrorDescription(req.getClientId()
+            + " is not a registered implicit client");
+        resp.setBodyReturned(true);
+        resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
+        throw new OAuth2Exception(resp);
+      }
+      if (req.getRedirectURI() == null && client.getRedirectURI() == null) {
+        OAuth2NormalizedResponse resp = new OAuth2NormalizedResponse();
+        resp.setError(ErrorType.INVALID_REQUEST.toString());
+        resp.setErrorDescription("No redirect_uri registered or received in request");
+        resp.setBodyReturned(true);
+        resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
+        throw new OAuth2Exception(resp);
+      }
+      if (req.getRedirectURI() != null
+          && !req.getRedirectURI().equals(client.getRedirectURI())) {
+        OAuth2NormalizedResponse resp = new OAuth2NormalizedResponse();
+        resp.setError(ErrorType.INVALID_REQUEST.toString());
+        resp.setErrorDescription("Redirect URI does not match the one registered for this client");
+        resp.setBodyReturned(true);
+        resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
+        throw new OAuth2Exception(resp);
+      }
+      return; // request validated
+    }
+  }
+}
\ No newline at end of file

Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/AuthCodeGrantValidator.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/AuthCodeGrantValidator.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/AuthCodeGrantValidator.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/AuthCodeGrantValidator.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,90 @@
+/*
+ * 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.shindig.social.core.oauth2.validators;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.shindig.social.core.oauth2.OAuth2Client;
+import org.apache.shindig.social.core.oauth2.OAuth2Code;
+import org.apache.shindig.social.core.oauth2.OAuth2DataService;
+import org.apache.shindig.social.core.oauth2.OAuth2Exception;
+import org.apache.shindig.social.core.oauth2.OAuth2NormalizedRequest;
+import org.apache.shindig.social.core.oauth2.OAuth2NormalizedResponse;
+import org.apache.shindig.social.core.oauth2.OAuth2Client.Flow;
+import org.apache.shindig.social.core.oauth2.OAuth2Types.ErrorType;
+
+import com.google.inject.Inject;
+
+public class AuthCodeGrantValidator implements OAuth2GrantValidator {
+
+  private OAuth2DataService service;
+
+  @Inject
+  public AuthCodeGrantValidator(OAuth2DataService service) {
+    this.service = service;
+  }
+
+  public String getGrantType() {
+    return "authorization_code";
+  }
+
+  public void validateRequest(OAuth2NormalizedRequest servletRequest)
+      throws OAuth2Exception {
+    OAuth2Client client = service.getClient(servletRequest.getClientId());
+    if (client == null || client.getFlow() != Flow.AUTHORIZATION_CODE) {
+      OAuth2NormalizedResponse resp = new OAuth2NormalizedResponse();
+      resp.setError(ErrorType.INVALID_CLIENT.toString());
+      resp.setErrorDescription("Invalid client");
+      resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
+      throw new OAuth2Exception(resp);
+    }
+    OAuth2Code authCode = service.getAuthorizationCode(
+        servletRequest.getClientId(), servletRequest.getAuthorizationCode());
+    if (authCode == null) {
+      OAuth2NormalizedResponse response = new OAuth2NormalizedResponse();
+      response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+      response.setError(ErrorType.INVALID_GRANT.toString());
+      response.setErrorDescription("Bad authorization code");
+      response.setBodyReturned(true);
+      throw new OAuth2Exception(response);
+    }
+    if (servletRequest.getRedirectURI() != null
+        && !servletRequest.getRedirectURI().equals(authCode.getRedirectURI())) {
+      OAuth2NormalizedResponse response = new OAuth2NormalizedResponse();
+      response.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+      response.setError(ErrorType.INVALID_GRANT.toString());
+      response
+          .setErrorDescription("The redirect URI does not match the one used in the authorization request");
+      response.setBodyReturned(true);
+      throw new OAuth2Exception(response);
+    }
+
+    // ensure authorization code has not already been used
+    if (authCode.getRelatedAccessToken() != null) {
+      service.unregisterAccessToken(client.getId(), authCode
+          .getRelatedAccessToken().getValue());
+      OAuth2NormalizedResponse response = new OAuth2NormalizedResponse();
+      response.setStatus(HttpServletResponse.SC_FORBIDDEN);
+      response.setError(ErrorType.INVALID_GRANT.toString());
+      response
+          .setErrorDescription("The authorization code has already been used to generate an access token");
+      response.setBodyReturned(true);
+      throw new OAuth2Exception(response);
+    }
+  }
+}
\ No newline at end of file

Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/AuthorizationCodeRequestValidator.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/AuthorizationCodeRequestValidator.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/AuthorizationCodeRequestValidator.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/AuthorizationCodeRequestValidator.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,73 @@
+/*
+ * 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.shindig.social.core.oauth2.validators;
+
+import org.apache.shindig.social.core.oauth2.OAuth2Client;
+import org.apache.shindig.social.core.oauth2.OAuth2DataService;
+import org.apache.shindig.social.core.oauth2.OAuth2Exception;
+import org.apache.shindig.social.core.oauth2.OAuth2NormalizedRequest;
+import org.apache.shindig.social.core.oauth2.OAuth2NormalizedResponse;
+import org.apache.shindig.social.core.oauth2.OAuth2Types.ErrorType;
+
+import javax.servlet.http.HttpServletResponse;
+
+import com.google.inject.Inject;
+
+public class AuthorizationCodeRequestValidator implements
+    OAuth2RequestValidator {
+
+  private OAuth2DataService store = null;
+
+  @Inject
+  public AuthorizationCodeRequestValidator(OAuth2DataService store) {
+    this.store = store;
+  }
+
+  public void validateRequest(OAuth2NormalizedRequest req)
+      throws OAuth2Exception {
+
+    OAuth2Client client = store.getClient(req.getClientId());
+    if (client == null) {
+      OAuth2NormalizedResponse resp = new OAuth2NormalizedResponse();
+      resp.setError(ErrorType.INVALID_REQUEST.toString());
+      resp.setErrorDescription("The client is invalid or not registered");
+      resp.setBodyReturned(true);
+      resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
+      throw new OAuth2Exception(resp);
+    }
+    String storedURI = client.getRedirectURI();
+    if (storedURI == null && req.getRedirectURI() == null) {
+      OAuth2NormalizedResponse resp = new OAuth2NormalizedResponse();
+      resp.setError(ErrorType.INVALID_REQUEST.toString());
+      resp.setErrorDescription("No redirect_uri registered or received in request");
+      resp.setBodyReturned(true);
+      resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
+      throw new OAuth2Exception(resp);
+    }
+    if (req.getRedirectURI() != null && storedURI != null) {
+      if (!req.getRedirectURI().equals(storedURI)) {
+        OAuth2NormalizedResponse resp = new OAuth2NormalizedResponse();
+        resp.setError(ErrorType.INVALID_REQUEST.toString());
+        resp.setErrorDescription("Redirect URI does not match the one registered for this client");
+        resp.setBodyReturned(true);
+        resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
+        throw new OAuth2Exception(resp);
+      }
+    }
+  }
+}
\ No newline at end of file

Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/ClientCredentialsGrantValidator.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/ClientCredentialsGrantValidator.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/ClientCredentialsGrantValidator.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/ClientCredentialsGrantValidator.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,71 @@
+/*
+ * 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.shindig.social.core.oauth2.validators;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.shindig.social.core.oauth2.OAuth2Client;
+import org.apache.shindig.social.core.oauth2.OAuth2DataService;
+import org.apache.shindig.social.core.oauth2.OAuth2Exception;
+import org.apache.shindig.social.core.oauth2.OAuth2NormalizedRequest;
+import org.apache.shindig.social.core.oauth2.OAuth2NormalizedResponse;
+import org.apache.shindig.social.core.oauth2.OAuth2Client.ClientType;
+import org.apache.shindig.social.core.oauth2.OAuth2Client.Flow;
+import org.apache.shindig.social.core.oauth2.OAuth2Types.ErrorType;
+
+import com.google.inject.Inject;
+
+public class ClientCredentialsGrantValidator implements OAuth2GrantValidator {
+
+  private OAuth2DataService service;
+
+  @Inject
+  public ClientCredentialsGrantValidator(OAuth2DataService service) {
+    this.service = service;
+  }
+
+  public void setOAuth2DataService(OAuth2DataService service) {
+    this.service = service;
+  }
+
+  public String getGrantType() {
+    return "client_credentials";
+  }
+
+  public void validateRequest(OAuth2NormalizedRequest req)
+      throws OAuth2Exception {
+    OAuth2Client cl = service.getClient(req.getClientId());
+    if (cl == null || cl.getFlow() != Flow.CLIENT_CREDENTIALS) {
+      throwAccessDenied("Bad client id or password");
+    }
+    if (cl.getType() != ClientType.CONFIDENTIAL) {
+      throwAccessDenied("Client credentials flow does not support public clients");
+    }
+    if (!cl.getSecret().equals(req.getClientSecret())) {
+      throwAccessDenied("Bad client id or password");
+    }
+  }
+
+  private void throwAccessDenied(String msg) throws OAuth2Exception {
+    OAuth2NormalizedResponse resp = new OAuth2NormalizedResponse();
+    resp.setError(ErrorType.ACCESS_DENIED.toString());
+    resp.setErrorDescription(msg);
+    resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
+    throw new OAuth2Exception(resp);
+  }
+}
\ No newline at end of file

Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/DefaultResourceRequestValidator.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/DefaultResourceRequestValidator.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/DefaultResourceRequestValidator.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/DefaultResourceRequestValidator.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,72 @@
+/*
+ * 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.shindig.social.core.oauth2.validators;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.shindig.social.core.oauth2.OAuth2Code;
+import org.apache.shindig.social.core.oauth2.OAuth2DataService;
+import org.apache.shindig.social.core.oauth2.OAuth2Exception;
+import org.apache.shindig.social.core.oauth2.OAuth2NormalizedRequest;
+import org.apache.shindig.social.core.oauth2.OAuth2NormalizedResponse;
+import org.apache.shindig.social.core.oauth2.OAuth2Types.ErrorType;
+
+import com.google.inject.Inject;
+
+public class DefaultResourceRequestValidator implements
+    OAuth2ProtectedResourceValidator {
+
+  private OAuth2DataService store = null;
+
+  @Inject
+  public DefaultResourceRequestValidator(OAuth2DataService store) {
+    this.store = store;
+  }
+
+  public void validateRequest(OAuth2NormalizedRequest req)
+      throws OAuth2Exception {
+    validateRequestForResource(req, null);
+
+  }
+
+  /**
+   * TODO (Matt): implement scope handling.
+   */
+  public void validateRequestForResource(OAuth2NormalizedRequest req,
+      Object resourceRequest) throws OAuth2Exception {
+
+    OAuth2Code token = store.getAccessToken(req.getAccessToken());
+    if (token == null)
+      throwAccessDenied("Access token is invalid.");
+    if (token.getExpiration() > -1
+        && token.getExpiration() < System.currentTimeMillis()) {
+      throwAccessDenied("Access token has expired.");
+    }
+    if (resourceRequest != null) {
+      // TODO (Matt): validate that requested resource is within scope
+    }
+  }
+
+  private void throwAccessDenied(String msg) throws OAuth2Exception {
+    OAuth2NormalizedResponse resp = new OAuth2NormalizedResponse();
+    resp.setError(ErrorType.ACCESS_DENIED.toString());
+    resp.setErrorDescription(msg);
+    resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
+    throw new OAuth2Exception(resp);
+  }
+}
\ No newline at end of file

Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/OAuth2GrantValidator.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/OAuth2GrantValidator.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/OAuth2GrantValidator.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/OAuth2GrantValidator.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,29 @@
+/*
+ * 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.shindig.social.core.oauth2.validators;
+
+/**
+ * Handles the validation of a grant requests for access tokens.
+ */
+public interface OAuth2GrantValidator extends OAuth2RequestValidator{
+  
+  /**
+   * Indicates the grant type this handler is registered to handle.
+   */
+  public String getGrantType();
+}

Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/OAuth2ProtectedResourceValidator.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/OAuth2ProtectedResourceValidator.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/OAuth2ProtectedResourceValidator.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/OAuth2ProtectedResourceValidator.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,39 @@
+/*
+ * 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.shindig.social.core.oauth2.validators;
+
+import org.apache.shindig.social.core.oauth2.OAuth2Exception;
+import org.apache.shindig.social.core.oauth2.OAuth2NormalizedRequest;
+
+/**
+ * Validator interface for a protected resource.
+ */
+public interface OAuth2ProtectedResourceValidator extends OAuth2RequestValidator {
+
+  /**
+   * Validates a request for a protected resource.
+   * 
+   * @param req is the normalized OAuth 2.0 request
+   * @param resourceRequest identifies the resource being requested
+   * 
+   * @throws OAuth2Exception
+   */
+  public void validateRequestForResource(OAuth2NormalizedRequest req,
+      Object resourceRequest) throws OAuth2Exception;
+
+}
\ No newline at end of file

Added: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/OAuth2RequestValidator.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/OAuth2RequestValidator.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/OAuth2RequestValidator.java (added)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/core/oauth2/validators/OAuth2RequestValidator.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,36 @@
+/*
+ * 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.shindig.social.core.oauth2.validators;
+
+import org.apache.shindig.social.core.oauth2.OAuth2Exception;
+import org.apache.shindig.social.core.oauth2.OAuth2NormalizedRequest;
+
+/**
+ * Validator interface for OAuth 2.0 requests.
+ */
+public interface OAuth2RequestValidator {
+
+  /**
+   * Validates an OAuth 2.0 request.
+   * 
+   * @param req is the normalized OAuth 2.0 request to validate
+   * 
+   * @throws OAuth2Exception if the request failed to validate
+   */
+  public void validateRequest(OAuth2NormalizedRequest req) throws OAuth2Exception;
+}
\ No newline at end of file

Modified: shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/SampleModule.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/SampleModule.java?rev=1182560&r1=1182559&r2=1182560&view=diff
==============================================================================
--- shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/SampleModule.java (original)
+++ shindig/trunk/java/social-api/src/main/java/org/apache/shindig/social/sample/SampleModule.java Wed Oct 12 20:00:11 2011
@@ -17,6 +17,10 @@
  */
 package org.apache.shindig.social.sample;
 
+import org.apache.shindig.social.core.oauth2.OAuth2DataService;
+import org.apache.shindig.social.core.oauth2.OAuth2DataServiceImpl;
+import org.apache.shindig.social.core.oauth2.OAuth2Service;
+import org.apache.shindig.social.core.oauth2.OAuth2ServiceImpl;
 import org.apache.shindig.social.opensocial.oauth.OAuthDataStore;
 import org.apache.shindig.social.opensocial.spi.ActivityService;
 import org.apache.shindig.social.opensocial.spi.ActivityStreamService;
@@ -51,5 +55,7 @@ public class SampleModule extends Abstra
     bind(PersonService.class).to(JsonDbOpensocialService.class);
     bind(MessageService.class).to(JsonDbOpensocialService.class);
     bind(OAuthDataStore.class).to(SampleOAuthDataStore.class);
+    bind(OAuth2Service.class).to(OAuth2ServiceImpl.class);
+    bind(OAuth2DataService.class).to(OAuth2DataServiceImpl.class);
   }
 }

Modified: shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/SocialApiTestsGuiceModule.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/SocialApiTestsGuiceModule.java?rev=1182560&r1=1182559&r2=1182560&view=diff
==============================================================================
--- shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/SocialApiTestsGuiceModule.java (original)
+++ shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/SocialApiTestsGuiceModule.java Wed Oct 12 20:00:11 2011
@@ -28,6 +28,10 @@ import org.apache.shindig.protocol.conve
 import org.apache.shindig.protocol.conversion.BeanJsonConverter;
 import org.apache.shindig.protocol.conversion.BeanXStreamConverter;
 import org.apache.shindig.protocol.conversion.xstream.XStreamConfiguration;
+import org.apache.shindig.social.core.oauth2.OAuth2DataService;
+import org.apache.shindig.social.core.oauth2.OAuth2DataServiceImpl;
+import org.apache.shindig.social.core.oauth2.OAuth2Service;
+import org.apache.shindig.social.core.oauth2.OAuth2ServiceImpl;
 import org.apache.shindig.social.core.util.xstream.XStream081Configuration;
 import org.apache.shindig.social.opensocial.service.ActivityHandler;
 import org.apache.shindig.social.opensocial.service.ActivityStreamHandler;
@@ -86,5 +90,7 @@ public class SocialApiTestsGuiceModule e
     bind(Integer.class).annotatedWith(
         Names.named("shindig.cache.lru.default.capacity"))
         .toInstance(10);
+    bind(OAuth2Service.class).to(OAuth2ServiceImpl.class);
+    bind(OAuth2DataService.class).to(OAuth2DataServiceImpl.class);
   }
 }

Modified: shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/config/SocialApiGuiceModuleTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/config/SocialApiGuiceModuleTest.java?rev=1182560&r1=1182559&r2=1182560&view=diff
==============================================================================
--- shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/config/SocialApiGuiceModuleTest.java (original)
+++ shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/config/SocialApiGuiceModuleTest.java Wed Oct 12 20:00:11 2011
@@ -26,6 +26,8 @@ import com.google.inject.TypeLiteral;
 import org.apache.shindig.auth.AuthenticationHandler;
 import org.apache.shindig.common.PropertiesModule;
 import org.apache.shindig.social.core.oauth.AuthenticationHandlerProvider;
+import org.apache.shindig.social.core.oauth2.OAuth2Service;
+import org.apache.shindig.social.core.oauth2.OAuth2ServiceImpl;
 import org.apache.shindig.social.opensocial.oauth.OAuthDataStore;
 import org.easymock.EasyMock;
 import org.junit.Assert;
@@ -44,6 +46,7 @@ public class SocialApiGuiceModuleTest ex
           @Override
           protected void configure() {
             bind(OAuthDataStore.class).toInstance(EasyMock.createMock(OAuthDataStore.class));
+            bind(OAuth2Service.class).toInstance(EasyMock.createMock(OAuth2ServiceImpl.class));
           }
     });
   }
@@ -57,11 +60,11 @@ public class SocialApiGuiceModuleTest ex
 
     AuthenticationHandlerProvider provider =
         injector.getInstance(AuthenticationHandlerProvider.class);
-    assertEquals(3, provider.get().size());
+    assertEquals(4, provider.get().size());
 
     List<AuthenticationHandler> handlers = injector.getInstance(
         Key.get(new TypeLiteral<List<AuthenticationHandler>>(){}));
 
-    assertEquals(3, handlers.size());
+    assertEquals(4, handlers.size());
   }
 }

Modified: shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/AuthenticationProviderHandlerTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/AuthenticationProviderHandlerTest.java?rev=1182560&r1=1182559&r2=1182560&view=diff
==============================================================================
--- shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/AuthenticationProviderHandlerTest.java (original)
+++ shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/AuthenticationProviderHandlerTest.java Wed Oct 12 20:00:11 2011
@@ -57,7 +57,7 @@ public class AuthenticationProviderHandl
    */
   public static class ProvidesNoHandlers extends AuthenticationHandlerProvider {
     public ProvidesNoHandlers() {
-      super(null, null, null);
+      super(null, null, null, null);
     }
 
     @Override

Added: shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/MockServletOutputStream.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/MockServletOutputStream.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/MockServletOutputStream.java (added)
+++ shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/MockServletOutputStream.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,23 @@
+package org.apache.shindig.social.core.oauth;
+
+import org.apache.http.util.ByteArrayBuffer;
+
+import javax.servlet.ServletOutputStream;
+
+/**
+ * Used to capture the raw request response provided by servlet
+ * 
+ */
+public class MockServletOutputStream extends ServletOutputStream {
+
+  private ByteArrayBuffer buffer = new ByteArrayBuffer(1024);
+
+  @Override
+  public void write(int b) {
+    buffer.append(b);
+  }
+
+  public byte[] getBuffer() {
+    return buffer.toByteArray();
+  }
+}
\ No newline at end of file

Added: shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuth2AuthCodeFlowTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuth2AuthCodeFlowTest.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuth2AuthCodeFlowTest.java (added)
+++ shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuth2AuthCodeFlowTest.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,686 @@
+/*
+ * 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.shindig.social.core.oauth;
+
+import java.io.PrintWriter;
+import java.net.URI;
+import java.net.URLEncoder;
+import java.util.UUID;
+
+import javax.servlet.http.HttpServletResponse;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.shindig.auth.AuthenticationHandler.InvalidAuthenticationException;
+import org.apache.shindig.auth.SecurityToken;
+import org.apache.shindig.common.testing.FakeHttpServletRequest;
+import org.apache.shindig.social.core.oauth2.OAuth2AuthenticationHandler;
+import org.apache.shindig.social.core.oauth2.OAuth2Servlet;
+import org.apache.shindig.social.dataservice.integration.AbstractLargeRestfulTests;
+import org.easymock.Capture;
+import org.easymock.EasyMock;
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+
+public class OAuth2AuthCodeFlowTest extends AbstractLargeRestfulTests {
+
+  protected static final String SIMPLE_ACCESS_TOKEN = "TEST_TOKEN";
+  protected static final String PUBLIC_CLIENT_ID = "testClient";
+  protected static final String PUBLIC_AUTH_CODE = "testClient_authcode_1";
+  protected static final String CONF_CLIENT_ID = "advancedAuthorizationCodeClient";
+  protected static final String CONF_CLIENT_SECRET = "advancedAuthorizationCodeClient_secret";
+  protected static final String CONF_AUTH_CODE = "advancedClient_authcode_1";
+
+  protected static final String PUBLIC_REDIRECT_URI = "http://localhost:8080/oauthclients/AuthorizationCodeClient";
+  protected static final String REDIRECT_URI = "http://localhost:8080/oauthclients/AuthorizationCodeClient/friends";
+
+  protected OAuth2Servlet servlet = null;
+
+  @Before
+  @Override
+  public void abstractLargeRestfulBefore() throws Exception {
+    super.abstractLargeRestfulBefore();
+    servlet = new OAuth2Servlet();
+    injector.injectMembers(servlet);
+  };
+
+  /**
+   * Test retrieving an access token using a public client
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testGetAccessToken() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest(
+        "http://localhost:8080/oauth2");
+    req.setContentType("application/x-www-form-urlencoded");
+    req.setPostData(
+        "client_id=" + PUBLIC_CLIENT_ID
+            + "&grant_type=authorization_code&redirect_uri="
+            + URLEncoder.encode(PUBLIC_REDIRECT_URI, "UTF-8") + "&code="
+            + PUBLIC_AUTH_CODE, "UTF-8");
+    req.setMethod("GET");
+    req.setServletPath("/oauth2");
+    req.setPathInfo("/access_token");
+    HttpServletResponse resp = mock(HttpServletResponse.class);
+    resp.setStatus(HttpServletResponse.SC_OK);
+    MockServletOutputStream outputStream = new MockServletOutputStream();
+    EasyMock.expect(resp.getOutputStream()).andReturn(outputStream).anyTimes();
+    PrintWriter writer = new PrintWriter(outputStream);
+    EasyMock.expect(resp.getWriter()).andReturn(writer).anyTimes();
+    replay();
+    servlet.service(req, resp);
+    writer.flush();
+
+    JSONObject tokenResponse = new JSONObject(new String(
+        outputStream.getBuffer(), "UTF-8"));
+
+    assertEquals("bearer", tokenResponse.getString("token_type"));
+    assertNotNull(tokenResponse.getString("access_token"));
+    assertTrue(tokenResponse.getLong("expires_in") > 0);
+    verify();
+  }
+
+  /**
+   * Test retrieving an authorization code using a public client
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testGetAuthorizationCode() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest(
+        "http://localhost:8080/oauth2");
+    req.setContentType("application/x-www-form-urlencoded");
+    req.setPostData(
+        "client_id=" + PUBLIC_CLIENT_ID + "&response_type=code&redirect_uri="
+            + URLEncoder.encode(PUBLIC_REDIRECT_URI, "UTF-8"), "UTF-8");
+    req.setMethod("GET");
+    req.setServletPath("/oauth2");
+    req.setPathInfo("/authorize");
+    HttpServletResponse resp = mock(HttpServletResponse.class);
+    Capture<String> redirectURI = new Capture<String>();
+    resp.setHeader(EasyMock.eq("Location"), EasyMock.capture(redirectURI));
+    resp.setStatus(EasyMock.eq(HttpServletResponse.SC_FOUND));
+    MockServletOutputStream outputStream = new MockServletOutputStream();
+    EasyMock.expect(resp.getOutputStream()).andReturn(outputStream).anyTimes();
+    PrintWriter writer = new PrintWriter(outputStream);
+    EasyMock.expect(resp.getWriter()).andReturn(writer).anyTimes();
+    replay();
+    servlet.service(req, resp);
+    writer.flush();
+    String response = new String(outputStream.getBuffer(), "UTF-8");
+    assertTrue(response == null || response.equals(""));
+    verify();
+    assertTrue(redirectURI.getValue()
+        .startsWith(PUBLIC_REDIRECT_URI + "?code="));
+    String code = redirectURI.getValue().substring(
+        redirectURI.getValue().indexOf("=") + 1);
+    UUID id = UUID.fromString(code);
+    assertTrue(id != null);
+  }
+
+  /**
+   * Test retrieving an authorization code using a public client that preserves
+   * state
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testGetAuthorizationCodePreserveState() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest(
+        "http://localhost:8080", "/oauth2", "client_id=" + PUBLIC_CLIENT_ID
+            + "&response_type=code&state=PRESERVEME&redirect_uri="
+            + URLEncoder.encode(PUBLIC_REDIRECT_URI, "UTF-8"));
+    req.setMethod("GET");
+    req.setServletPath("/oauth2");
+    req.setPathInfo("/authorize");
+    HttpServletResponse resp = mock(HttpServletResponse.class);
+    Capture<String> redirectURI = new Capture<String>();
+    resp.setHeader(EasyMock.eq("Location"), EasyMock.capture(redirectURI));
+    resp.setStatus(EasyMock.eq(HttpServletResponse.SC_FOUND));
+    MockServletOutputStream outputStream = new MockServletOutputStream();
+    EasyMock.expect(resp.getOutputStream()).andReturn(outputStream).anyTimes();
+    PrintWriter writer = new PrintWriter(outputStream);
+    EasyMock.expect(resp.getWriter()).andReturn(writer).anyTimes();
+    replay();
+    servlet.service(req, resp);
+    writer.flush();
+    String response = new String(outputStream.getBuffer(), "UTF-8");
+    assertTrue(response == null || response.equals(""));
+    verify();
+    assertTrue(redirectURI.getValue()
+        .startsWith(PUBLIC_REDIRECT_URI));
+    URI uri = new URI(redirectURI.getValue());
+    assertTrue(uri.getQuery().contains("state=PRESERVEME"));
+    assertTrue(uri.getQuery().contains("code="));
+  }
+
+  /**
+   * Test retrieving an authorization code using a confidential client
+   * 
+   * Client authentication is not required for confidential clients accessing
+   * the authorization endpoint
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testGetAuthorizationCodeConfidential() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest(
+        "http://localhost:8080", "/oauth2", "client_id=" + CONF_CLIENT_ID
+            + "&response_type=code&client_secret=" + CONF_CLIENT_SECRET
+            + "redirect_uri=" + URLEncoder.encode(REDIRECT_URI, "UTF-8"));
+    req.setMethod("GET");
+    req.setServletPath("/oauth2");
+    req.setPathInfo("/authorize");
+    HttpServletResponse resp = mock(HttpServletResponse.class);
+    Capture<String> redirectURI = new Capture<String>();
+    resp.setHeader(EasyMock.eq("Location"), EasyMock.capture(redirectURI));
+    resp.setStatus(EasyMock.eq(HttpServletResponse.SC_FOUND));
+    MockServletOutputStream outputStream = new MockServletOutputStream();
+    EasyMock.expect(resp.getOutputStream()).andReturn(outputStream).anyTimes();
+    PrintWriter writer = new PrintWriter(outputStream);
+    EasyMock.expect(resp.getWriter()).andReturn(writer).anyTimes();
+    replay();
+    servlet.service(req, resp);
+    writer.flush();
+    String response = new String(outputStream.getBuffer(), "UTF-8");
+    assertTrue(response == null || response.equals(""));
+    verify();
+    assertTrue(redirectURI.getValue().startsWith(REDIRECT_URI + "?code="));
+    String code = redirectURI.getValue().substring(
+        redirectURI.getValue().indexOf("=") + 1);
+    UUID id = UUID.fromString(code);
+    assertTrue(id != null);
+  }
+
+  /**
+   * Test retrieving an authorization code using a confidential client without
+   * setting redirect URI
+   * 
+   * The redirect URI is registered with this client, so omitting it should
+   * still generate a response using the registered redirect URI.
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testGetAuthorizationCodeNoRedirect() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest(
+        "http://localhost:8080", "/oauth2", "client_id=" + CONF_CLIENT_ID
+            + "&response_type=code");
+    req.setMethod("GET");
+    req.setServletPath("/oauth2");
+    req.setPathInfo("/authorize");
+    HttpServletResponse resp = mock(HttpServletResponse.class);
+    Capture<String> redirectURI = new Capture<String>();
+    resp.setHeader(EasyMock.eq("Location"), EasyMock.capture(redirectURI));
+    Capture<Integer> respCode = new Capture<Integer>();
+    resp.setStatus(EasyMock.capture(respCode));
+    MockServletOutputStream outputStream = new MockServletOutputStream();
+    EasyMock.expect(resp.getOutputStream()).andReturn(outputStream).anyTimes();
+    PrintWriter writer = new PrintWriter(outputStream);
+    EasyMock.expect(resp.getWriter()).andReturn(writer).anyTimes();
+    replay();
+    servlet.service(req, resp);
+    writer.flush();
+    String response = new String(outputStream.getBuffer(), "UTF-8");
+    assertTrue(response == null || response.equals(""));
+    verify();
+    assertEquals((Integer) 302, respCode.getValue());
+    assertTrue(redirectURI.getValue().startsWith(REDIRECT_URI + "?code="));
+    String code = redirectURI.getValue().substring(
+        redirectURI.getValue().indexOf("=") + 1);
+    UUID id = UUID.fromString(code);
+    assertTrue(id != null);
+  }
+
+  /**
+   * Test retrieving an authorization code using a confidential client with a
+   * bad redirect URI
+   * 
+   * The redirect URI is registered with this client, so passing a redirect that
+   * doesn't match the registered value should generate an error per the OAuth
+   * 2.0 spec.
+   * 
+   * See Section 3.1.2.3 under
+   * http://tools.ietf.org/html/draft-ietf-oauth-v2-20#section-3.1.2
+   * 
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testGetAuthorizationCodeBadRedirect() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest(
+        "http://localhost:8080", "/oauth2", "client_id=" + CONF_CLIENT_ID
+            + "&response_type=code&redirect_uri="
+            + URLEncoder.encode("http://example.org/redirect/", "UTF-8"));
+    req.setMethod("GET");
+    req.setServletPath("/oauth2");
+    req.setPathInfo("/authorize");
+    HttpServletResponse resp = mock(HttpServletResponse.class);
+    resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
+    MockServletOutputStream outputStream = new MockServletOutputStream();
+    EasyMock.expect(resp.getOutputStream()).andReturn(outputStream).anyTimes();
+    PrintWriter writer = new PrintWriter(outputStream);
+    EasyMock.expect(resp.getWriter()).andReturn(writer).anyTimes();
+    replay();
+    servlet.service(req, resp);
+    writer.flush();
+    JSONObject tokenResponse = new JSONObject(new String(
+        outputStream.getBuffer(), "UTF-8"));
+    assertEquals("invalid_request", tokenResponse.getString("error"));
+    verify();
+  }
+
+  /**
+   * Test retrieving an auth code and using it to generate an access token
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testConfidentialAuthCodeFlow() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest(
+        "http://localhost:8080", "/oauth2", "client_id=" + CONF_CLIENT_ID
+            + "&client_secret=" + CONF_CLIENT_SECRET
+            + "&response_type=code&redirect_uri="
+            + URLEncoder.encode(REDIRECT_URI, "UTF-8"));
+    req.setMethod("GET");
+    req.setServletPath("/oauth2");
+    req.setPathInfo("/authorize");
+    HttpServletResponse resp = mock(HttpServletResponse.class);
+    Capture<String> redirectURI = new Capture<String>();
+    resp.setHeader(EasyMock.eq("Location"), EasyMock.capture(redirectURI));
+    resp.setStatus(EasyMock.eq(HttpServletResponse.SC_FOUND));
+    MockServletOutputStream outputStream = new MockServletOutputStream();
+    EasyMock.expect(resp.getOutputStream()).andReturn(outputStream).anyTimes();
+    PrintWriter writer = new PrintWriter(outputStream);
+    EasyMock.expect(resp.getWriter()).andReturn(writer).anyTimes();
+    replay();
+    servlet.service(req, resp);
+    writer.flush();
+    String response = new String(outputStream.getBuffer(), "UTF-8");
+    assertTrue(response == null || response.equals(""));
+    verify();
+    assertTrue(redirectURI.getValue().startsWith(REDIRECT_URI + "?code="));
+    String code = redirectURI.getValue().substring(
+        redirectURI.getValue().indexOf("=") + 1);
+    UUID id = UUID.fromString(code);
+    assertTrue(id != null);
+
+    reset();
+
+    req = new FakeHttpServletRequest("http://localhost:8080", "/oauth2",
+        "client_id=" + CONF_CLIENT_ID
+            + "&grant_type=authorization_code&redirect_uri="
+            + URLEncoder.encode(REDIRECT_URI, "UTF-8") + "&code=" + code
+            + "&client_secret=" + CONF_CLIENT_SECRET);
+    req.setMethod("GET");
+    req.setServletPath("/oauth2");
+    req.setPathInfo("/access_token");
+    resp = mock(HttpServletResponse.class);
+    resp.setStatus(HttpServletResponse.SC_OK);
+    outputStream = new MockServletOutputStream();
+    EasyMock.expect(resp.getOutputStream()).andReturn(outputStream).anyTimes();
+    writer = new PrintWriter(outputStream);
+    EasyMock.expect(resp.getWriter()).andReturn(writer).anyTimes();
+    replay();
+    servlet.service(req, resp);
+    writer.flush();
+
+    JSONObject tokenResponse = new JSONObject(new String(
+        outputStream.getBuffer(), "UTF-8"));
+
+    assertEquals("bearer", tokenResponse.getString("token_type"));
+    assertNotNull(tokenResponse.getString("access_token"));
+    assertTrue(tokenResponse.getLong("expires_in") > 0);
+    verify();
+
+  }
+
+  /**
+   * Test using URL parameter to pass client secret to authenticate client
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testGetAccessTokenConfidentialClientParams() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest(
+        "http://localhost:8080", "/oauth2", "client_id=" + CONF_CLIENT_ID
+            + "&grant_type=authorization_code&redirect_uri="
+            + URLEncoder.encode(REDIRECT_URI, "UTF-8") + "&code="
+            + CONF_AUTH_CODE + "&client_secret=" + CONF_CLIENT_SECRET);
+    req.setMethod("GET");
+    req.setServletPath("/oauth2");
+    req.setPathInfo("/access_token");
+    HttpServletResponse resp = mock(HttpServletResponse.class);
+    resp.setStatus(HttpServletResponse.SC_OK);
+    MockServletOutputStream outputStream = new MockServletOutputStream();
+    EasyMock.expect(resp.getOutputStream()).andReturn(outputStream).anyTimes();
+    PrintWriter writer = new PrintWriter(outputStream);
+    EasyMock.expect(resp.getWriter()).andReturn(writer).anyTimes();
+    replay();
+    servlet.service(req, resp);
+    writer.flush();
+
+    JSONObject tokenResponse = new JSONObject(new String(
+        outputStream.getBuffer(), "UTF-8"));
+
+    assertEquals("bearer", tokenResponse.getString("token_type"));
+    assertNotNull(tokenResponse.getString("access_token"));
+    assertTrue(tokenResponse.getLong("expires_in") > 0);
+    verify();
+  }
+
+  /**
+   * Test using basic authentication scheme for client authentication
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testGetAccessTokenConfidentialClientBasicAuth() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest(
+        "http://localhost:8080", "/oauth2", "client_id=" + CONF_CLIENT_ID
+            + "&grant_type=authorization_code&redirect_uri="
+            + URLEncoder.encode(REDIRECT_URI, "UTF-8") + "&code="
+            + CONF_AUTH_CODE);
+    req.setHeader(
+        "Authorization",
+        "Basic "
+            + Base64
+                .encodeBase64String((CONF_CLIENT_ID + ":" + CONF_CLIENT_SECRET)
+                    .getBytes("UTF-8")));
+    req.setMethod("GET");
+    req.setServletPath("/oauth2");
+    req.setPathInfo("/access_token");
+    HttpServletResponse resp = mock(HttpServletResponse.class);
+    resp.setStatus(HttpServletResponse.SC_OK);
+    MockServletOutputStream outputStream = new MockServletOutputStream();
+    EasyMock.expect(resp.getOutputStream()).andReturn(outputStream).anyTimes();
+    PrintWriter writer = new PrintWriter(outputStream);
+    EasyMock.expect(resp.getWriter()).andReturn(writer).anyTimes();
+    replay();
+    servlet.service(req, resp);
+    writer.flush();
+
+    JSONObject tokenResponse = new JSONObject(new String(
+        outputStream.getBuffer(), "UTF-8"));
+
+    assertEquals("bearer", tokenResponse.getString("token_type"));
+    assertNotNull(tokenResponse.getString("access_token"));
+    assertTrue(tokenResponse.getLong("expires_in") > 0);
+    verify();
+  }
+
+  /**
+   * Incorrect client ID used in Basic Authorization header
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testGetAccessTokenConfClientBasicAuthBadID() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest(
+        "http://localhost:8080", "/oauth2", "client_id=" + CONF_CLIENT_ID
+            + "&grant_type=authorization_code&redirect_uri="
+            + URLEncoder.encode(REDIRECT_URI, "UTF-8") + "&code="
+            + CONF_AUTH_CODE);
+    req.setHeader(
+        "Authorization",
+        "Basic "
+            + Base64.encodeBase64String(("BAD_ID:" + CONF_CLIENT_SECRET)
+                .getBytes("UTF-8")));
+    req.setMethod("GET");
+    req.setServletPath("/oauth2");
+    req.setPathInfo("/access_token");
+    HttpServletResponse resp = mock(HttpServletResponse.class);
+    resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
+    MockServletOutputStream outputStream = new MockServletOutputStream();
+    EasyMock.expect(resp.getOutputStream()).andReturn(outputStream).anyTimes();
+    PrintWriter writer = new PrintWriter(outputStream);
+    EasyMock.expect(resp.getWriter()).andReturn(writer).anyTimes();
+    replay();
+    servlet.service(req, resp);
+    writer.flush();
+    String response = new String(outputStream.getBuffer(), "UTF-8");
+    assertTrue(response == null || response.equals(""));
+    verify();
+  }
+
+  /**
+   * Test attempting to get an access token using a bad client secret with a
+   * confidential client.
+   */
+  @Test
+  public void testGetAccessTokenBadConfidentialClientParams() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest(
+        "http://localhost:8080/oauth2");
+    req.setContentType("application/x-www-form-urlencoded");
+    req.setPostData(
+        "client_id=" + CONF_CLIENT_ID
+            + "&grant_type=authorization_code&redirect_uri="
+            + URLEncoder.encode(REDIRECT_URI, "UTF-8") + "&code="
+            + CONF_AUTH_CODE + "&client_secret=BAD_SECRET", "UTF-8");
+    req.setMethod("GET");
+    req.setServletPath("/oauth2");
+    req.setPathInfo("/access_token");
+    HttpServletResponse resp = mock(HttpServletResponse.class);
+    resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+    MockServletOutputStream outputStream = new MockServletOutputStream();
+    EasyMock.expect(resp.getOutputStream()).andReturn(outputStream).anyTimes();
+    PrintWriter writer = new PrintWriter(outputStream);
+    EasyMock.expect(resp.getWriter()).andReturn(writer).anyTimes();
+    replay();
+    servlet.service(req, resp);
+    writer.flush();
+
+    JSONObject tokenResponse = new JSONObject(new String(
+        outputStream.getBuffer(), "UTF-8"));
+
+    assertEquals("unauthorized_client", tokenResponse.getString("error"));
+    verify();
+  }
+
+  /**
+   * Test attempting to get an access token with an unregistered client ID
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testGetAccessTokenBadClient() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest(
+        "http://localhost:8080", "/oauth2",
+        "client_id=BAD_CLIENT&grant_type=authorization_code&redirect_uri="
+            + URLEncoder.encode(REDIRECT_URI, "UTF-8") + "&code="
+            + PUBLIC_AUTH_CODE);
+    req.setMethod("GET");
+    req.setServletPath("/oauth2");
+    req.setPathInfo("/access_token");
+    HttpServletResponse resp = mock(HttpServletResponse.class);
+    resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+    MockServletOutputStream outputStream = new MockServletOutputStream();
+    EasyMock.expect(resp.getOutputStream()).andReturn(outputStream).anyTimes();
+    PrintWriter writer = new PrintWriter(outputStream);
+    EasyMock.expect(resp.getWriter()).andReturn(writer).anyTimes();
+    replay();
+    servlet.service(req, resp);
+    writer.flush();
+
+    JSONObject tokenResponse = new JSONObject(new String(
+        outputStream.getBuffer(), "UTF-8"));
+
+    assertEquals("invalid_client", tokenResponse.getString("error"));
+    verify();
+  }
+
+  /**
+   * Test attempting to get an access token with a bad grant type
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testGetAccessTokenBadGrantType() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest(
+        "http://localhost:8080", "/oauth2", "client_id=" + PUBLIC_CLIENT_ID
+            + "&grant_type=BAD_GRANT&redirect_uri="
+            + URLEncoder.encode(REDIRECT_URI, "UTF-8") + "&code="
+            + PUBLIC_AUTH_CODE);
+    req.setMethod("GET");
+    req.setServletPath("/oauth2");
+    req.setPathInfo("/access_token");
+    HttpServletResponse resp = mock(HttpServletResponse.class);
+    resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+    MockServletOutputStream outputStream = new MockServletOutputStream();
+    EasyMock.expect(resp.getOutputStream()).andReturn(outputStream).anyTimes();
+    PrintWriter writer = new PrintWriter(outputStream);
+    EasyMock.expect(resp.getWriter()).andReturn(writer).anyTimes();
+    replay();
+    servlet.service(req, resp);
+    writer.flush();
+
+    JSONObject tokenResponse = new JSONObject(new String(
+        outputStream.getBuffer(), "UTF-8"));
+
+    assertEquals("unsupported_grant_type", tokenResponse.getString("error"));
+    verify();
+  }
+
+  /**
+   * Test attempting to get an access token with an invalid authorization code
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testGetAccessTokenBadAuthCode() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest(
+        "http://localhost:8080", "/oauth2", "client_id=" + PUBLIC_CLIENT_ID
+            + "&grant_type=authorization_code&redirect_uri="
+            + URLEncoder.encode(REDIRECT_URI, "UTF-8") + "&code=BAD-CODE-OMG");
+    req.setMethod("GET");
+    req.setServletPath("/oauth2");
+    req.setPathInfo("/access_token");
+    HttpServletResponse resp = mock(HttpServletResponse.class);
+    resp.setStatus(HttpServletResponse.SC_BAD_REQUEST);
+    MockServletOutputStream outputStream = new MockServletOutputStream();
+    EasyMock.expect(resp.getOutputStream()).andReturn(outputStream).anyTimes();
+    PrintWriter writer = new PrintWriter(outputStream);
+    EasyMock.expect(resp.getWriter()).andReturn(writer).anyTimes();
+    replay();
+    servlet.service(req, resp);
+    writer.flush();
+
+    JSONObject tokenResponse = new JSONObject(new String(
+        outputStream.getBuffer(), "UTF-8"));
+
+    assertEquals("invalid_grant", tokenResponse.getString("error"));
+    verify();
+  }
+
+  /**
+   * Test attempting to re-use an authorization code to get a new access token.
+   */
+  @Test
+  public void testReuseAuthorizationCode() throws Exception {
+    // get authorization code
+    FakeHttpServletRequest req = new FakeHttpServletRequest("http://localhost:8080","/oauth2", "client_id=" + CONF_CLIENT_ID + "&client_secret="+CONF_CLIENT_SECRET+"&response_type=code&redirect_uri="+URLEncoder.encode(REDIRECT_URI,"UTF-8"));
+    req.setMethod("GET");
+    req.setServletPath("/oauth2");
+    req.setPathInfo("/authorize");
+    HttpServletResponse resp = mock(HttpServletResponse.class);
+    Capture<String> redirectURI = new Capture<String>();
+    resp.setHeader(EasyMock.eq("Location"), EasyMock.capture(redirectURI));
+    resp.setStatus(EasyMock.eq(HttpServletResponse.SC_FOUND));
+    MockServletOutputStream outputStream = new MockServletOutputStream();
+    EasyMock.expect(resp.getOutputStream()).andReturn(outputStream).anyTimes();
+    PrintWriter writer = new PrintWriter(outputStream);
+    EasyMock.expect(resp.getWriter()).andReturn(writer).anyTimes();
+    replay();
+    servlet.service(req, resp);
+    writer.flush();
+    String response = new String(outputStream.getBuffer(),"UTF-8");
+    assertTrue(response == null || response.equals(""));
+    verify();
+    assertTrue(redirectURI.getValue().startsWith(REDIRECT_URI+"?code="));
+    String code = redirectURI.getValue().substring(redirectURI.getValue().indexOf("=")+1);
+    UUID id = UUID.fromString(code);
+    assertTrue(id != null);
+    System.out.println("Retrieved authorization code: " + code);
+    
+    reset();
+    
+    // use authorization code to get access token
+    req = new FakeHttpServletRequest("http://localhost:8080","/oauth2", "client_id=" + CONF_CLIENT_ID + "&grant_type=authorization_code&redirect_uri=" + URLEncoder.encode(REDIRECT_URI,"UTF-8") + "&code=" + code + "&client_secret=" + CONF_CLIENT_SECRET);
+    req.setMethod("GET");
+    req.setServletPath("/oauth2");
+    req.setPathInfo("/access_token");
+    resp = mock(HttpServletResponse.class);
+    resp.setStatus(HttpServletResponse.SC_OK);
+    outputStream = new MockServletOutputStream();
+    EasyMock.expect(resp.getOutputStream()).andReturn(outputStream).anyTimes();
+    writer = new PrintWriter(outputStream);
+    EasyMock.expect(resp.getWriter()).andReturn(writer).anyTimes();
+    replay();
+    servlet.service(req, resp);
+    writer.flush();
+    JSONObject tokenResponse = new JSONObject(new String(outputStream.getBuffer(),"UTF-8"));
+    assertEquals("bearer",tokenResponse.getString("token_type"));
+    assertNotNull(tokenResponse.getString("access_token"));
+    assertTrue(tokenResponse.getLong("expires_in") > 0);
+    verify();
+    String accessToken = tokenResponse.getString("access_token");
+    System.out.println("Retrieved access token: " + accessToken);
+    
+    reset();
+    
+    // ensure access token can get security token for accessing resources
+    OAuth2AuthenticationHandler handler = injector.getInstance(OAuth2AuthenticationHandler.class);
+    req = new FakeHttpServletRequest("http://localhost:8080","/social/rest/activitystreams/john.doe/@self/1/object1", "access_token=" + accessToken);
+    req.setMethod("GET");
+    SecurityToken token = handler.getSecurityTokenFromRequest(req);
+    assertNotNull(token);
+    
+    reset();
+    
+    // attempt to re-use authorization code to get new access token
+    req = new FakeHttpServletRequest("http://localhost:8080","/oauth2", "client_id=" + CONF_CLIENT_ID + "&grant_type=authorization_code&redirect_uri=" + URLEncoder.encode(REDIRECT_URI,"UTF-8") + "&code=" + code + "&client_secret=" + CONF_CLIENT_SECRET);
+    req.setMethod("GET");
+    req.setServletPath("/oauth2");
+    req.setPathInfo("/access_token");
+    resp = mock(HttpServletResponse.class);
+    resp.setStatus(HttpServletResponse.SC_FORBIDDEN);
+    outputStream = new MockServletOutputStream();
+    EasyMock.expect(resp.getOutputStream()).andReturn(outputStream).anyTimes();
+    writer = new PrintWriter(outputStream);
+    EasyMock.expect(resp.getWriter()).andReturn(writer).anyTimes();
+    replay();
+    servlet.service(req, resp);
+    writer.flush();
+    tokenResponse = new JSONObject(new String(outputStream.getBuffer(),"UTF-8"));
+    System.out.println("Rejection response: " + tokenResponse.toString());
+    assertEquals("invalid_grant",tokenResponse.getString("error"));
+    verify();
+    
+    // use (revoked) access token to get a resource
+    req = new FakeHttpServletRequest("http://localhost:8080","/social/rest/activitystreams/john.doe/@self/1/object1", "access_token=" + accessToken);
+    req.setMethod("GET");
+    try {
+      handler.getSecurityTokenFromRequest(req);
+    } catch (InvalidAuthenticationException ist) {
+      return; // test passed
+    }
+    fail("Should have thrown InvalidAuthenticationException");
+  }
+
+}
\ No newline at end of file

Added: shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuth2AuthenticationHandlerTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuth2AuthenticationHandlerTest.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuth2AuthenticationHandlerTest.java (added)
+++ shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuth2AuthenticationHandlerTest.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,91 @@
+/*
+ * 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.shindig.social.core.oauth;
+
+import org.apache.shindig.auth.AuthenticationHandler.InvalidAuthenticationException;
+import org.apache.shindig.common.EasyMockTestCase;
+import org.apache.shindig.common.testing.FakeHttpServletRequest;
+import org.apache.shindig.social.SocialApiTestsGuiceModule;
+import org.apache.shindig.social.core.oauth2.OAuth2AuthenticationHandler;
+import org.junit.Before;
+import org.junit.Test;
+
+import com.google.inject.Guice;
+
+public class OAuth2AuthenticationHandlerTest extends EasyMockTestCase {
+
+  private final String ACCESS_TOKEN = "testClient_accesstoken_1";
+  protected OAuth2AuthenticationHandler handler = null;
+
+  @Before
+  public void setUp() {
+    handler = Guice.createInjector(new SocialApiTestsGuiceModule())
+        .getInstance(OAuth2AuthenticationHandler.class);
+  }
+
+  @Test
+  public void testValidAccessTokenViaURL()
+      throws InvalidAuthenticationException {
+    replay();
+    handler.getSecurityTokenFromRequest(new FakeHttpServletRequest(
+        "http://localhost:8080/oauth2", "/some_protected_uri", "access_token="
+            + ACCESS_TOKEN));
+    // Should not throw exception
+  }
+
+  @Test
+  public void testInvalidAccessTokenViaURL() {
+    replay();
+    try {
+      handler.getSecurityTokenFromRequest(new FakeHttpServletRequest(
+          "http://localhost:8080/oauth2", "/some_protected_uri",
+          "access_token=BADTOKEN"));
+    } catch (InvalidAuthenticationException ex) {
+      return;
+    }
+    fail("Handler allowed invalid token without throwing exception");
+    // Should not throw exception
+  }
+
+  @Test
+  public void testValidAccessTokenViaHeader()
+      throws InvalidAuthenticationException {
+    replay();
+    FakeHttpServletRequest req = new FakeHttpServletRequest(
+        "http://localhost:8080/oauth2", "/some_protected_uri", "");
+    req.setHeader("Authorization", "Bearer " + ACCESS_TOKEN);
+    handler.getSecurityTokenFromRequest(req);
+    // Should not throw exception
+  }
+
+  @Test
+  public void testInvalidAccessTokenViaHeader() {
+    replay();
+    FakeHttpServletRequest req = new FakeHttpServletRequest(
+        "http://localhost:8080/oauth2", "/some_protected_uri", "");
+    req.setHeader("Authorization", "Bearer BADVALUEK");
+    try {
+      handler.getSecurityTokenFromRequest(req);
+    } catch (InvalidAuthenticationException ex) {
+      return;
+    }
+    fail("Handler allowed invalid token without throwing exception");
+    // Should not throw exception
+  }
+
+}
\ No newline at end of file

Added: shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuth2ClientCredentialFlowTest.java
URL: http://svn.apache.org/viewvc/shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuth2ClientCredentialFlowTest.java?rev=1182560&view=auto
==============================================================================
--- shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuth2ClientCredentialFlowTest.java (added)
+++ shindig/trunk/java/social-api/src/test/java/org/apache/shindig/social/core/oauth/OAuth2ClientCredentialFlowTest.java Wed Oct 12 20:00:11 2011
@@ -0,0 +1,180 @@
+/*
+ * 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.shindig.social.core.oauth;
+
+import org.apache.commons.codec.binary.Base64;
+import org.apache.shindig.common.testing.FakeHttpServletRequest;
+import org.apache.shindig.social.core.oauth2.OAuth2Servlet;
+import org.apache.shindig.social.dataservice.integration.AbstractLargeRestfulTests;
+import org.easymock.EasyMock;
+import org.json.JSONObject;
+import org.junit.Before;
+import org.junit.Test;
+
+import javax.servlet.http.HttpServletResponse;
+
+import java.io.PrintWriter;
+
+public class OAuth2ClientCredentialFlowTest extends AbstractLargeRestfulTests {
+
+  protected static final String CLIENT_CRED_CLIENT = "testClientCredentialsClient";
+  protected static final String CLIENT_CRED_SECRET = "clientCredentialsClient_secret";
+
+  protected OAuth2Servlet servlet = null;
+
+  @Before
+  @Override
+  public void abstractLargeRestfulBefore() throws Exception {
+    super.abstractLargeRestfulBefore();
+    servlet = new OAuth2Servlet();
+    injector.injectMembers(servlet);
+  };
+
+  /**
+   * Test using basic authentication scheme for client cred flow
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testClientCredFlowBadHeader() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest(
+        "http://localhost:8080", "/oauth2", "grant_type=client_credentials");
+    req.setHeader("Authorization", "Basic *^%#");
+    req.setMethod("GET");
+    req.setServletPath("/oauth2");
+    req.setPathInfo("/access_token");
+    HttpServletResponse resp = mock(HttpServletResponse.class);
+    resp.setStatus(EasyMock.eq(HttpServletResponse.SC_BAD_REQUEST));
+    MockServletOutputStream outputStream = new MockServletOutputStream();
+    EasyMock.expect(resp.getOutputStream()).andReturn(outputStream).anyTimes();
+    PrintWriter writer = new PrintWriter(outputStream);
+    EasyMock.expect(resp.getWriter()).andReturn(writer).anyTimes();
+    replay();
+    servlet.service(req, resp);
+    writer.flush();
+
+    String response = new String(outputStream.getBuffer(), "UTF-8");
+    JSONObject respObj = new JSONObject(response);
+    assertTrue(respObj.has("error"));
+    verify();
+  }
+
+  /**
+   * Test using basic authentication scheme for client cred flow
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testClientCredFlowBadPass() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest(
+        "http://localhost:8080", "/oauth2", "grant_type=client_credentials");
+    req.setHeader(
+        "Authorization",
+        "Basic "
+            + Base64.encodeBase64String((CLIENT_CRED_CLIENT + ":badsecret")
+                .getBytes("UTF-8")));
+    req.setMethod("GET");
+    req.setServletPath("/oauth2");
+    req.setPathInfo("/access_token");
+    HttpServletResponse resp = mock(HttpServletResponse.class);
+    resp.setStatus(EasyMock.eq(HttpServletResponse.SC_BAD_REQUEST));
+    MockServletOutputStream outputStream = new MockServletOutputStream();
+    EasyMock.expect(resp.getOutputStream()).andReturn(outputStream).anyTimes();
+    PrintWriter writer = new PrintWriter(outputStream);
+    EasyMock.expect(resp.getWriter()).andReturn(writer).anyTimes();
+    replay();
+    servlet.service(req, resp);
+    writer.flush();
+
+    String response = new String(outputStream.getBuffer(), "UTF-8");
+    JSONObject respObj = new JSONObject(response);
+    assertTrue(respObj.has("error"));
+    verify();
+  }
+
+  /**
+   * Test using basic authentication scheme for client cred flow
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testClientCredFlowBasicAuth() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest(
+        "http://localhost:8080", "/oauth2", "grant_type=client_credentials");
+    req.setHeader(
+        "Authorization",
+        "Basic "
+            + Base64
+                .encodeBase64String((CLIENT_CRED_CLIENT + ":" + CLIENT_CRED_SECRET)
+                    .getBytes("UTF-8")));
+    req.setMethod("GET");
+    req.setServletPath("/oauth2");
+    req.setPathInfo("/access_token");
+    HttpServletResponse resp = mock(HttpServletResponse.class);
+    resp.setStatus(HttpServletResponse.SC_OK);
+    MockServletOutputStream outputStream = new MockServletOutputStream();
+    EasyMock.expect(resp.getOutputStream()).andReturn(outputStream).anyTimes();
+    PrintWriter writer = new PrintWriter(outputStream);
+    EasyMock.expect(resp.getWriter()).andReturn(writer).anyTimes();
+    replay();
+    servlet.service(req, resp);
+    writer.flush();
+
+    JSONObject tokenResponse = new JSONObject(new String(
+        outputStream.getBuffer(), "UTF-8"));
+
+    assertEquals("bearer", tokenResponse.getString("token_type"));
+    assertNotNull(tokenResponse.getString("access_token"));
+    assertTrue(tokenResponse.getLong("expires_in") > 0);
+    verify();
+  }
+
+  /**
+   * Test using URL parameter with client cred flow
+   * 
+   * @throws Exception
+   */
+  @Test
+  public void testClientCredFlowParams() throws Exception {
+    FakeHttpServletRequest req = new FakeHttpServletRequest(
+        "http://localhost:8080", "/oauth2", "client_id=" + CLIENT_CRED_CLIENT
+            + "&grant_type=client_credentials&client_secret="
+            + CLIENT_CRED_SECRET);
+    req.setMethod("GET");
+    req.setServletPath("/oauth2");
+    req.setPathInfo("/access_token");
+    HttpServletResponse resp = mock(HttpServletResponse.class);
+    resp.setStatus(HttpServletResponse.SC_OK);
+    MockServletOutputStream outputStream = new MockServletOutputStream();
+    EasyMock.expect(resp.getOutputStream()).andReturn(outputStream).anyTimes();
+    PrintWriter writer = new PrintWriter(outputStream);
+    EasyMock.expect(resp.getWriter()).andReturn(writer).anyTimes();
+    replay();
+    servlet.service(req, resp);
+    writer.flush();
+
+    JSONObject tokenResponse = new JSONObject(new String(
+        outputStream.getBuffer(), "UTF-8"));
+
+    assertEquals("bearer", tokenResponse.getString("token_type"));
+    assertNotNull(tokenResponse.getString("access_token"));
+    assertTrue(tokenResponse.getLong("expires_in") > 0);
+    verify();
+  }
+
+}
\ No newline at end of file