aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorSamuel Padgett2012-01-26 15:30:16 (EST)
committerMichael Fiedler2012-01-27 09:26:05 (EST)
commit4dcf25385c224374d4615518878731965b2cb745 (patch)
treefa244d95c18973fa34b8f832a00f91846342ca1d
parent6e83cb30f8c37ba728bd8722953267917c3f6d36 (diff)
downloadorg.eclipse.lyo.server-4dcf25385c224374d4615518878731965b2cb745.zip
org.eclipse.lyo.server-4dcf25385c224374d4615518878731965b2cb745.tar.gz
org.eclipse.lyo.server-4dcf25385c224374d4615518878731965b2cb745.tar.bz2
Bug 369863 - Add OAuth 1.0a support to the OAuth framework
The first version of the framework only supported OAuth 1.0 authentication flows. We should add 1.0a support (and add a warning to the login page if consumers are using 1.0). The OAuth version can be negotiated at runtime depending on whether the consumer passes an oauth_callback parameter when asking for a request token.
-rw-r--r--org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/OAuthConfiguration.java23
-rw-r--r--org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/OAuthRequest.java2
-rw-r--r--org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/consumer/LyoOAuthConsumer.java30
-rw-r--r--org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/token/SimpleTokenStrategy.java151
-rw-r--r--org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/token/TokenStrategy.java71
-rw-r--r--org.eclipse.lyo.server.oauth.webapp/src/main/java/org/eclipse/lyo/server/oauth/webapp/services/OAuthService.java128
-rw-r--r--org.eclipse.lyo.server.oauth.webapp/src/main/webapp/oauth/login.jsp13
-rw-r--r--org.eclipse.lyo.server.oauth.webapp/src/main/webapp/oauth/theme.css4
8 files changed, 357 insertions, 65 deletions
diff --git a/org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/OAuthConfiguration.java b/org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/OAuthConfiguration.java
index 1f59bb0..8ead740 100644
--- a/org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/OAuthConfiguration.java
+++ b/org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/OAuthConfiguration.java
@@ -37,6 +37,7 @@ public class OAuthConfiguration {
private TokenStrategy tokenStrategy;
private ConsumerStore consumerStore = null;
private Authentication authentication = null;
+ private boolean v1_0Allowed = true;
private static final OAuthConfiguration instance = new OAuthConfiguration();
@@ -131,4 +132,26 @@ public class OAuthConfiguration {
public void setAuthentication(Authentication authentication) {
this.authentication = authentication;
}
+
+ /**
+ * Is OAuth version 1.0 allowed, or do we require 1.0a?
+ *
+ * @return true if version 1.0 is allowed
+ * @see <a href="http://oauth.net/advisories/2009-1/">OAuth Security Advisory: 2009.1</a>
+ */
+ public boolean isV1_0Allowed() {
+ return v1_0Allowed;
+ }
+
+ /**
+ * Sets if we allow OAuth 1.0.
+ *
+ * @param allowed
+ * true to allow OAuth version 1.0 requests or false to require
+ * OAuth version 1.0a
+ * @see <a href="http://oauth.net/advisories/2009-1/">OAuth Security Advisory: 2009.1</a>
+ */
+ public void setV1_0Allowed(boolean allowed) {
+ this.v1_0Allowed = allowed;
+ }
}
diff --git a/org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/OAuthRequest.java b/org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/OAuthRequest.java
index 92ae6ec..ad8876d 100644
--- a/org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/OAuthRequest.java
+++ b/org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/OAuthRequest.java
@@ -59,7 +59,7 @@ public class OAuthRequest {
private OAuthAccessor accessor;
public OAuthRequest(HttpServletRequest request)
- throws OAuthProblemException, IOException {
+ throws OAuthException, IOException {
this.httpRequest = request;
this.message = OAuthServlet.getMessage(httpRequest, null);
diff --git a/org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/consumer/LyoOAuthConsumer.java b/org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/consumer/LyoOAuthConsumer.java
index f61a3b0..3e45bf4 100644
--- a/org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/consumer/LyoOAuthConsumer.java
+++ b/org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/consumer/LyoOAuthConsumer.java
@@ -26,9 +26,18 @@ import net.oauth.OAuthServiceProvider;
public class LyoOAuthConsumer extends OAuthConsumer {
private static final long serialVersionUID = 7634987410903334464L;
+ public enum OAuthVersion { OAUTH_1_0, OAUTH_1_0A };
+
private String name;
private boolean trusted = false;
+ /*
+ * Assume 1.0 until we learn otherwise. This can be determined if the
+ * consumer passes an oauth_callback parameter when asking for a request
+ * token.
+ */
+ private OAuthVersion oAuthVersion = OAuthVersion.OAUTH_1_0;
+
public LyoOAuthConsumer(String consumerKey, String consumerSecret) {
super(null, consumerKey, consumerSecret, null);
}
@@ -85,4 +94,25 @@ public class LyoOAuthConsumer extends OAuthConsumer {
public void setTrusted(boolean trusted) {
this.trusted = trusted;
}
+
+ /**
+ * Gets the OAuth version that the consumer supports.
+ *
+ * @return the OAuth version
+ */
+ public OAuthVersion getOAuthVersion() {
+ return oAuthVersion;
+ }
+
+ /**
+ * Sets the OAuth version that the consumer supports. This should be set by
+ * the OAuth service depending on whether the consumer specified an
+ * oauth_callback parameter when asking for a request token.
+ *
+ * @param oAuthVersion
+ * the OAuth version
+ */
+ public void setOAuthVersion(OAuthVersion oAuthVersion) {
+ this.oAuthVersion = oAuthVersion;
+ }
}
diff --git a/org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/token/SimpleTokenStrategy.java b/org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/token/SimpleTokenStrategy.java
index a20d7a5..bfd3a07 100644
--- a/org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/token/SimpleTokenStrategy.java
+++ b/org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/token/SimpleTokenStrategy.java
@@ -32,7 +32,8 @@ import net.oauth.OAuthProblemException;
/**
* A simple strategy for generating and validating tokens. Generates random
* tokens and stores them in memory. Tokens are only good for the life of the
- * process.
+ * process. Least recently used tokens are invalidated when cached limits are
+ * reached.
*
* @author Samuel Padgett <spadgett@us.ibm.com>
*/
@@ -40,27 +41,61 @@ public class SimpleTokenStrategy implements TokenStrategy {
private final static int REQUEST_TOKEN_MAX_ENTIRES = 500;
private final static int ACCESS_TOKEN_MAX_ENTRIES = 5000;
+ /**
+ * Holds information associated with a request token such as the callback
+ * URL and OAuth verification code.
+ *
+ * @author Samuel Padgett <spadgett@us.ibm.com>
+ */
protected class RequestTokenData {
private String consumerKey;
private boolean authorized;
+ private String callback;
+ private String verificationCode;
public RequestTokenData(String consumerKey) {
this.consumerKey = consumerKey;
this.authorized = false;
+ this.callback = null;
+ }
+
+ public RequestTokenData(String consumerKey, String callback) {
+ this.consumerKey = consumerKey;
+ this.authorized = false;
+ this.callback = callback;
}
public String getConsumerKey() {
return consumerKey;
}
+
public void setConsumerKey(String consumerKey) {
this.consumerKey = consumerKey;
}
+
public boolean isAuthorized() {
return authorized;
}
+
public void setAuthorized(boolean authorized) {
this.authorized = authorized;
}
+
+ public String getCallback() {
+ return callback;
+ }
+
+ public void setCallback(String callback) {
+ this.callback = callback;
+ }
+
+ public String getVerificationCode() {
+ return verificationCode;
+ }
+
+ public void setVerificationCode(String verificationCode) {
+ this.verificationCode = verificationCode;
+ }
}
// key is request token string, value is RequestTokenData
@@ -72,10 +107,25 @@ public class SimpleTokenStrategy implements TokenStrategy {
// key is token, value is token secret
private Map<String, String> tokenSecrets;
+ /**
+ * Constructs a SimpleTokenStrategy using the defaults for cache limits on request and access tokens.
+ *
+ * @see SimpleTokenStrategy#SimpleTokenStrategy(int, int)
+ */
public SimpleTokenStrategy() {
this(REQUEST_TOKEN_MAX_ENTIRES, ACCESS_TOKEN_MAX_ENTRIES);
}
+ /**
+ * Constructs a SimpleTokenStrategy with cache limits on the number of
+ * request and access tokens. Least recently used tokens are invalidated
+ * when cache limits are reached.
+ *
+ * @param requestTokenMaxCount
+ * the maximum number of request tokens to track
+ * @param accessTokenMaxCount
+ * the maximum number of access tokens to track
+ */
public SimpleTokenStrategy(int requestTokenMaxCount, int accessTokenMaxCount) {
requestTokens = new LRUCache<String, RequestTokenData>(requestTokenMaxCount);
accessTokens = new LRUCache<String, String>(accessTokenMaxCount);
@@ -84,13 +134,16 @@ public class SimpleTokenStrategy implements TokenStrategy {
}
@Override
- public void generateRequestToken(OAuthRequest oAuthRequest) {
+ public void generateRequestToken(OAuthRequest oAuthRequest)
+ throws IOException {
OAuthAccessor accessor = oAuthRequest.getAccessor();
accessor.requestToken = generateTokenString();
accessor.tokenSecret = generateTokenString();
+ String callback = oAuthRequest.getMessage()
+ .getParameter(OAuth.OAUTH_CALLBACK);
synchronized (requestTokens) {
requestTokens.put(accessor.requestToken, new RequestTokenData(
- accessor.consumer.consumerKey));
+ accessor.consumer.consumerKey, callback));
}
synchronized (tokenSecrets) {
tokenSecrets.put(accessor.requestToken, accessor.tokenSecret);
@@ -100,38 +153,50 @@ public class SimpleTokenStrategy implements TokenStrategy {
@Override
public String validateRequestToken(HttpServletRequest httpRequest,
OAuthMessage message) throws OAuthException, IOException {
- synchronized (requestTokens) {
- RequestTokenData tokenData = requestTokens.get(message.getToken());
- if (tokenData == null) {
- throw new OAuthProblemException(OAuth.Problems.TOKEN_REJECTED);
- }
+ return getRequestTokenData(message.getToken()).getConsumerKey();
+ }
- return tokenData.getConsumerKey();
- }
+ @Override
+ public String getCallback(HttpServletRequest httpRequest,
+ String requestToken) throws OAuthProblemException {
+ return getRequestTokenData(requestToken).getCallback();
}
@Override
public void markRequestTokenAuthorized(HttpServletRequest httpRequest,
String requestToken) throws OAuthProblemException {
- synchronized (requestTokens) {
- RequestTokenData tokenData = requestTokens.get(requestToken);
- if (tokenData == null) {
- throw new OAuthProblemException(OAuth.Problems.TOKEN_REJECTED);
- }
- tokenData.setAuthorized(true);
- }
+ getRequestTokenData(requestToken).setAuthorized(true);
}
@Override
public boolean isRequestTokenAuthorized(HttpServletRequest httpRequest,
- String requestToken) {
- synchronized (requestTokens) {
- RequestTokenData tokenData = requestTokens.get(requestToken);
- if (tokenData == null) {
- return false;
- }
+ String requestToken) throws OAuthProblemException {
+ return getRequestTokenData(requestToken).isAuthorized();
+ }
+
+ @Override
+ public String generateVerificationCode(HttpServletRequest httpRequest,
+ String requestToken) throws OAuthProblemException {
+ String verificationCode = generateTokenString();
+ getRequestTokenData(requestToken).setVerificationCode(verificationCode);
+
+ return verificationCode;
+ }
- return tokenData.isAuthorized();
+ @Override
+ public void validateVerificationCode(OAuthRequest oAuthRequest)
+ throws OAuthException, IOException {
+ String verificationCode = oAuthRequest.getMessage().getParameter(
+ OAuth.OAUTH_VERIFIER);
+ if (verificationCode == null) {
+ throw new OAuthProblemException(
+ OAuth.Problems.OAUTH_PARAMETERS_ABSENT);
+ }
+
+ RequestTokenData tokenData = getRequestTokenData(oAuthRequest);
+ if (!verificationCode.equals(tokenData.getVerificationCode())) {
+ throw new OAuthProblemException(
+ OAuth.Problems.OAUTH_PARAMETERS_REJECTED);
}
}
@@ -153,7 +218,7 @@ public class SimpleTokenStrategy implements TokenStrategy {
// Generate a new access token.
accessor.accessToken = generateTokenString();
- synchronized (requestTokens) {
+ synchronized (accessTokens) {
accessTokens.put(accessor.accessToken,
accessor.consumer.consumerKey);
}
@@ -204,4 +269,40 @@ public class SimpleTokenStrategy implements TokenStrategy {
protected String generateTokenString() {
return UUID.randomUUID().toString();
}
+
+ /**
+ * Gets the request token data from this OAuth request.
+ *
+ * @param oAuthRequest
+ * the OAuth request
+ * @return the request token data
+ * @throws OAuthProblemException
+ * if the request token is invalid
+ * @throws IOException
+ * on reading OAuth parameters
+ */
+ protected RequestTokenData getRequestTokenData(OAuthRequest oAuthRequest)
+ throws OAuthProblemException, IOException {
+ return getRequestTokenData(oAuthRequest.getMessage().getToken());
+ }
+
+ /**
+ * Gets the request token data for this request token.
+ *
+ * @param requestToken
+ * the request token string
+ * @return the request token data
+ * @throws OAuthProblemException
+ * if the request token is invalid
+ */
+ protected RequestTokenData getRequestTokenData(String requestToken)
+ throws OAuthProblemException {
+ synchronized (requestTokens) {
+ RequestTokenData tokenData = requestTokens.get(requestToken);
+ if (tokenData == null) {
+ throw new OAuthProblemException(OAuth.Problems.TOKEN_REJECTED);
+ }
+ return tokenData;
+ }
+ }
}
diff --git a/org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/token/TokenStrategy.java b/org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/token/TokenStrategy.java
index cdd3ec5..3caf571 100644
--- a/org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/token/TokenStrategy.java
+++ b/org.eclipse.lyo.server.oauth.core/src/main/java/org/eclipse/lyo/server/oauth/core/token/TokenStrategy.java
@@ -22,7 +22,6 @@ import javax.servlet.http.HttpServletRequest;
import net.oauth.OAuthAccessor;
import net.oauth.OAuthException;
import net.oauth.OAuthMessage;
-import net.oauth.OAuthProblemException;
import org.eclipse.lyo.server.oauth.core.OAuthRequest;
@@ -40,9 +39,13 @@ public interface TokenStrategy {
*
* @param oAuthRequest
* the OAuth request
+ * @throws IOException
+ * on errors reading from the request message
+ * @throws OAuthException
+ * on OAuth problems
* @see OAuthRequest#getAccessor()
*/
- public void generateRequestToken(OAuthRequest oAuthRequest);
+ public void generateRequestToken(OAuthRequest oAuthRequest) throws OAuthException, IOException;
/**
* Validates that the request token is valid, throwing an exception if not.
@@ -67,6 +70,23 @@ public interface TokenStrategy {
OAuthMessage message) throws OAuthException, IOException;
/**
+ * Gets the OAuth callback associated with this consumer for OAuth 1.0a
+ * authentication flows. Returns null if the consumer did not specify a
+ * callback when asking for a request token.
+ *
+ * @param httpRequest
+ * the HTTP request
+ * @param requestToken
+ * the request token
+ * @return the callback URL
+ *
+ * @throws OAuthException
+ * on OAuth problems
+ */
+ public String getCallback(HttpServletRequest httpRequest,
+ String requestToken) throws OAuthException;
+
+ /**
* Indicates that a user has typed in a valid ID and password, and that the
* request token can now be exchanged for an access token.
*
@@ -74,13 +94,13 @@ public interface TokenStrategy {
* the servlet request
* @param requestToken
* the request token string
- * @throws OAuthProblemException
+ * @throws OAuthException
* if the token is not valid
*
* @see #isRequestTokenAuthorized(HttpServletRequest, String)
*/
public void markRequestTokenAuthorized(HttpServletRequest httpRequest,
- String requestToken) throws OAuthProblemException;
+ String requestToken) throws OAuthException;
/**
* Checks with the request token has been authorized by the end user.
@@ -91,11 +111,44 @@ public interface TokenStrategy {
* the request token
* @return answers if the request token is authorized and can be exchanged
* for an access token
+ * @throws OAuthException
+ * on OAuth problems
*
* @see #markRequestTokenAuthorized(HttpServletRequest, OAuthAccessor)
*/
public boolean isRequestTokenAuthorized(HttpServletRequest httpRequest,
- String requestToken);
+ String requestToken) throws OAuthException;
+
+ /**
+ * Generates an "unguessable" OAuth verification code. The consumer must
+ * supply the verification code when exchanging the request token for an
+ * access token. This is specific to OAuth 1.0a.
+ *
+ * @param httpRequest
+ * the HTTP request
+ * @param requestToken
+ * the request token
+ * @return a verification code
+ * @throws OAuthException
+ * on OAuth problems (e.g., the request token is invalid)
+ */
+ public String generateVerificationCode(HttpServletRequest httpRequest,
+ String requestToken) throws OAuthException;
+
+ /**
+ * Validates that the verification code is recognized and associated with
+ * the request token. This must be called before the request token is
+ * exchanged for the access token in an OAuth 1.0a authentication flow.
+ *
+ * @param oAuthRequest
+ * the OAuth request
+ * @throws OAuthException
+ * on OAuth problems (e.g., the request token is invalid)
+ * @throws IOException
+ * on I/O errors
+ */
+ public void validateVerificationCode(OAuthRequest oAuthRequest)
+ throws OAuthException, IOException;
/**
* Generates an access token and token secret and sets it in the accessor in
@@ -103,14 +156,14 @@ public interface TokenStrategy {
*
* @param oAuthRequest
* the OAuth request
- * @throws OAuthProblemException
+ * @throws OAuthException
* on OAuth problems
* @throws IOException
* on I/O errors
* @see OAuthRequest#getAccessor()
*/
public void generateAccessToken(OAuthRequest oAuthRequest)
- throws OAuthProblemException, IOException;
+ throws OAuthException, IOException;
/**
* Validates that the access token is valid, throwing an exception if not.
@@ -134,9 +187,9 @@ public interface TokenStrategy {
* the token string, either a request token or access token
*
* @return the token secret
- * @throws OAuthProblemException
+ * @throws OAuthException
* on OAuth problems (e.g., the token is invalid)
*/
public String getTokenSecret(HttpServletRequest httpRequest, String token)
- throws OAuthProblemException;
+ throws OAuthException;
}
diff --git a/org.eclipse.lyo.server.oauth.webapp/src/main/java/org/eclipse/lyo/server/oauth/webapp/services/OAuthService.java b/org.eclipse.lyo.server.oauth.webapp/src/main/java/org/eclipse/lyo/server/oauth/webapp/services/OAuthService.java
index 982bc6e..e1f7c59 100644
--- a/org.eclipse.lyo.server.oauth.webapp/src/main/java/org/eclipse/lyo/server/oauth/webapp/services/OAuthService.java
+++ b/org.eclipse.lyo.server.oauth.webapp/src/main/java/org/eclipse/lyo/server/oauth/webapp/services/OAuthService.java
@@ -17,6 +17,7 @@ package org.eclipse.lyo.server.oauth.webapp.services;
import java.io.IOException;
import java.net.URISyntaxException;
+import java.util.List;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
@@ -33,6 +34,7 @@ import javax.ws.rs.core.Response.Status;
import javax.ws.rs.core.UriBuilder;
import net.oauth.OAuth;
+import net.oauth.OAuth.Parameter;
import net.oauth.OAuthAccessor;
import net.oauth.OAuthException;
import net.oauth.OAuthMessage;
@@ -65,6 +67,12 @@ public class OAuthService {
@Context
protected HttpServletResponse httpResponse;
+ @GET
+ @Path("/requestToken")
+ public Response doGetRequestToken() throws IOException, ServletException {
+ return doPostRequestToken();
+ }
+
/**
* Responds with a request token and token secret.
*
@@ -76,21 +84,49 @@ public class OAuthService {
*/
@POST
@Path("/requestToken")
- public Response getRequestToken() throws IOException, ServletException {
+ public Response doPostRequestToken() throws IOException, ServletException {
try {
-
OAuthRequest oAuthRequest = validateRequest();
- OAuthConfiguration.getInstance()
- .getTokenStrategy().generateRequestToken(oAuthRequest);
- OAuthAccessor accessor = oAuthRequest.getAccessor();
- return respondWithToken(accessor.requestToken, accessor.tokenSecret);
+ // Generate the token.
+ OAuthConfiguration.getInstance().getTokenStrategy()
+ .generateRequestToken(oAuthRequest);
+ // Check for OAuth 1.0a authentication.
+ boolean callbackConfirmed = confirmCallback(oAuthRequest);
+
+ // Respond to the consumer.
+ OAuthAccessor accessor = oAuthRequest.getAccessor();
+ return respondWithToken(accessor.requestToken,
+ accessor.tokenSecret, callbackConfirmed);
} catch (OAuthException e) {
return respondWithOAuthProblem(e);
}
}
+ protected boolean confirmCallback(OAuthRequest oAuthRequest)
+ throws OAuthException {
+ boolean callbackConfirmed = OAuthConfiguration
+ .getInstance()
+ .getTokenStrategy()
+ .getCallback(httpRequest,
+ oAuthRequest.getAccessor().requestToken) != null;
+ if (callbackConfirmed) {
+ oAuthRequest.getConsumer().setOAuthVersion(
+ LyoOAuthConsumer.OAuthVersion.OAUTH_1_0A);
+ } else {
+ if (!OAuthConfiguration.getInstance().isV1_0Allowed()) {
+ throw new OAuthProblemException(
+ OAuth.Problems.OAUTH_PARAMETERS_ABSENT);
+ }
+
+ oAuthRequest.getConsumer().setOAuthVersion(
+ LyoOAuthConsumer.OAuthVersion.OAUTH_1_0);
+ }
+
+ return callbackConfirmed;
+ }
+
/*
* TODO: Give providers a way to show their own branded login page.
*/
@@ -127,6 +163,10 @@ public class OAuthService {
httpRequest.setAttribute("consumerName", consumer.getName());
httpRequest.setAttribute("callback",
getCallbackURL(message, consumer));
+ boolean callbackConfirmed =
+ consumer.getOAuthVersion() == LyoOAuthConsumer.OAuthVersion.OAUTH_1_0A;
+ httpRequest.setAttribute("callbackConfirmed", new Boolean(
+ callbackConfirmed));
Authentication auth = config.getAuthentication();
if (auth == null) {
@@ -150,20 +190,40 @@ public class OAuthService {
}
private String getCallbackURL(OAuthMessage message,
- LyoOAuthConsumer consumer) throws IOException {
- // Find the callback URL.
- String callback = message.getParameter(OAuth.OAUTH_CALLBACK);
- if (callback == null) {
- callback = consumer.callbackURL;
+ LyoOAuthConsumer consumer) throws IOException, OAuthException {
+ String callback = null;
+ switch (consumer.getOAuthVersion()) {
+ case OAUTH_1_0:
+ if (!OAuthConfiguration.getInstance().isV1_0Allowed()) {
+ throw new OAuthProblemException(OAuth.Problems.VERSION_REJECTED);
+ }
+
+ // If this is OAuth 1.0, the callback should be a request parameter.
+ callback = message.getParameter(OAuth.OAUTH_CALLBACK);
+ break;
+
+ case OAUTH_1_0A:
+ // If this is OAuth 1.0a, the callback was passed when the consumer
+ // asked for a request token.
+ String requestToken = message.getToken();
+ callback = OAuthConfiguration.getInstance().getTokenStrategy()
+ .getCallback(httpRequest, requestToken);
}
if (callback == null) {
return null;
}
- return UriBuilder.fromUri(callback)
- .queryParam(OAuth.OAUTH_TOKEN, message.getToken()).build()
- .toString();
+ UriBuilder uriBuilder = UriBuilder.fromUri(callback)
+ .queryParam(OAuth.OAUTH_TOKEN, message.getToken());
+ if (consumer.getOAuthVersion() == LyoOAuthConsumer.OAuthVersion.OAUTH_1_0A) {
+ String verificationCode = OAuthConfiguration.getInstance()
+ .getTokenStrategy()
+ .generateVerificationCode(httpRequest, message.getToken());
+ uriBuilder.queryParam(OAuth.OAUTH_VERIFIER, verificationCode);
+ }
+
+ return uriBuilder.build().toString();
}
/**
@@ -194,7 +254,7 @@ public class OAuthService {
try {
OAuthConfiguration.getInstance().getTokenStrategy()
.markRequestTokenAuthorized(httpRequest, requestToken);
- } catch (OAuthProblemException e) {
+ } catch (OAuthException e) {
return Response.status(Status.CONFLICT)
.entity("Request token invalid.")
.type(MediaType.TEXT_PLAIN).build();
@@ -203,6 +263,12 @@ public class OAuthService {
return Response.noContent().build();
}
+ @GET
+ @Path("/accessToken")
+ public Response doGetAccessToken() throws IOException, ServletException {
+ return doPostAccessToken();
+ }
+
/**
* Responds with an access token and token secret for valid OAuth requests.
* The request must be signed and the request token valid.
@@ -215,15 +281,22 @@ public class OAuthService {
*/
@POST
@Path("/accessToken")
- public Response getAccessToken() throws IOException, ServletException {
+ public Response doPostAccessToken() throws IOException, ServletException {
try {
-
- // Validate the request is signed and check that the request token is valid.
+ // Validate the request is signed and check that the request token
+ // is valid.
OAuthRequest oAuthRequest = validateRequest();
- TokenStrategy strategy = OAuthConfiguration.getInstance()
- .getTokenStrategy();
+ OAuthConfiguration config = OAuthConfiguration.getInstance();
+ TokenStrategy strategy = config.getTokenStrategy();
strategy.validateRequestToken(httpRequest,
oAuthRequest.getMessage());
+
+ // The verification code MUST be passed in the request if this is
+ // OAuth 1.0a.
+ if (!config.isV1_0Allowed()
+ || oAuthRequest.getConsumer().getOAuthVersion() == LyoOAuthConsumer.OAuthVersion.OAUTH_1_0A) {
+ strategy.validateVerificationCode(oAuthRequest);
+ }
// Generate a new access token for this accessor.
strategy.generateAccessToken(oAuthRequest);
@@ -231,7 +304,6 @@ public class OAuthService {
// Send the new token and secret back to the consumer.
OAuthAccessor accessor = oAuthRequest.getAccessor();
return respondWithToken(accessor.accessToken, accessor.tokenSecret);
-
} catch (OAuthException e) {
return respondWithOAuthProblem(e);
}
@@ -264,9 +336,19 @@ public class OAuthService {
protected Response respondWithToken(String token, String tokenSecret)
throws IOException {
- String responseBody = OAuth.formEncode(OAuth.newList(OAuth.OAUTH_TOKEN,
- token, OAuth.OAUTH_TOKEN_SECRET, tokenSecret));
+ return respondWithToken(token, tokenSecret, false);
+ }
+ protected Response respondWithToken(String token, String tokenSecret,
+ boolean callbackConfirmed) throws IOException {
+ List<Parameter> oAuthParameters = OAuth.newList(OAuth.OAUTH_TOKEN,
+ token, OAuth.OAUTH_TOKEN_SECRET, tokenSecret);
+ if (callbackConfirmed) {
+ oAuthParameters.add(new Parameter(OAuth.OAUTH_CALLBACK_CONFIRMED,
+ "true"));
+ }
+
+ String responseBody = OAuth.formEncode(oAuthParameters);
return Response.ok(responseBody)
.type(MediaType.APPLICATION_FORM_URLENCODED).build();
}
diff --git a/org.eclipse.lyo.server.oauth.webapp/src/main/webapp/oauth/login.jsp b/org.eclipse.lyo.server.oauth.webapp/src/main/webapp/oauth/login.jsp
index 00524ae..91fdc4e 100644
--- a/org.eclipse.lyo.server.oauth.webapp/src/main/webapp/oauth/login.jsp
+++ b/org.eclipse.lyo.server.oauth.webapp/src/main/webapp/oauth/login.jsp
@@ -24,13 +24,20 @@
<%-- Creative Commons Image: http://openclipart.org/detail/211/shiny-key-by-tiothy --%>
<img src="<%=request.getContextPath()%>/oauth/key.png" style="float: right;">
<h1>Connect to <c:out value="${applicationName}">Your Application</c:out></h1>
-
+
<form id="loginForm">
- <div class="message"><b><c:out value="${consumerName}">Another application</c:out></b>
+ <div><b><c:out value="${consumerName}">Another application</c:out></b>
is requesting access to your <b><c:out value="${applicationName}">application</c:out></b> data. Enter your
username and password to continue or click cancel to exit.</div>
-
+
+ <c:if test="${not callbackConfirmed}">
+ <p>
+ Only continue if you initiated this request directly from
+ <c:out value="${consumerName}">the consumer's website</c:out>.
+ </p>
+ </c:if>
+
<div id="error" class="error" style="display: hidden;"></div>
<input type="hidden" name="requestToken" value="<c:out value="${requestToken}"/>">
<input type="hidden" id="callback" value="<c:out value="${callback}"/>">
diff --git a/org.eclipse.lyo.server.oauth.webapp/src/main/webapp/oauth/theme.css b/org.eclipse.lyo.server.oauth.webapp/src/main/webapp/oauth/theme.css
index 77bf42a..aa2c16e 100644
--- a/org.eclipse.lyo.server.oauth.webapp/src/main/webapp/oauth/theme.css
+++ b/org.eclipse.lyo.server.oauth.webapp/src/main/webapp/oauth/theme.css
@@ -8,10 +8,6 @@ label {
text-align: right;
}
-.message {
- font-size: 120t;
-}
-
.textField {
width: 400px;
margin-bottom: 1em;