
This article contains incorrect information about the 2Legged mode of the OAuth protocol. Thanks to all the people that commented on this article, I wrote a new article, where the 2Legged mode is (I hope) better explained and where a better client for OAuth is implemented. This article is left here for historical references and in order to keep the valuable comments that were added to it.
The OAuth protocol has two models that can be implemented. First, the 3legged model involves all the steps described by the RFC and it is generally used when the authentication of the user is involved in the process (this case is the most common). A very good example of implementing this form of the protocol is composed of the two applications (Sparklr and Tonr) that are delivered with the OAuth plugin for Spring Security source.
The second model is the 2legged model, where the second step of the process (the one where the user is presented with a login page from the server, and he/she needs to log in and then authorize the access to the protected resource) is skipped, on the reason that the client has an additional level of trust, and therefore it is sufficient the exchange and authorization of tokens (this is clearly incorrect; the 2Legged mode means that instead of having 3 parties involved, one has only 2: the provider and the consumer; read this for a better explanation), without the need for the user to authenticate itself.
In this article I will present how one can implement the server and the client for leveraging this flavor of the OAuth protocol.
Tools
For the implementation of the server I will use Spring and Spring Security (to enforce the application security) along with the OAuth plugin for Spring Security. For the test client I will use the same OAuth plugin (which also provides the service consumer support) and the Apache Commons HttpClient (to make the authorization of the token).
If you use Maven as build system, here are the dependencies that you will most likely need:
<dependencies>
<dependency>
<groupId>commons-httpclient</groupId>
<artifactId>commons-httpclient</artifactId>
<version>3.1</version>
</dependency>
<dependency>
<groupId>log4j</groupId>
<artifactId>log4j</artifactId>
<version>1.2.14</version>
</dependency>
<dependency>
<groupId>org.codehaus.spring-security-oauth</groupId>
<artifactId>spring-security-oauth</artifactId>
<version>3.15</version>
</dependency>
<dependency>
<groupId>commons-io</groupId>
<artifactId>commons-io</artifactId>
<version>1.4</version>
</dependency>
</dependencies>
Server
The first player in our game is the server. For this we will define a protected resource and the URLs for the OAuth security protocol (soauth is the name of the web application):
- /soauth/photos/photo1.jpg : the protected resource. The clients will have to follow the OAuth protocol and get their (digital
) hands on an access token, in order to get access to this resource - /soauth/oauth/request_token: the URL from where the clients can get the resource token
- /soauth/oauth/authorize: the URL where the clients can authorize existing request tokens
- /soauth/oauth/access_token: the URL from where the clients can get an access token
Let’s kick in by defining first a dummy consumer details service. This component is responsible for providing the details of the customers that are allowed to connect to the server and access the protected resources. A consumer is identified by the consumer_key and the consumer_secret. These two items are used during the OAuth process to encrypt and sign the requests, therefore allowing the server to check that the consumer that connects and requests access to the protected resources is indeed which one it says it is.
public class DummyConsumerDetailsServiceImpl implements ConsumerDetailsService, UserDetailsService {
private static final Log log = LogFactory.getLog( DummyConsumerDetailsServiceImpl.class );
private Map<String, ConsumerDetails> consumers;
public DummyConsumerDetailsServiceImpl() {
consumers = new HashMap<String, ConsumerDetails>();
consumers.put( "consumer-k1", createConsumerDetails( "consumer-k1", "consumer-n1", "consumer-secret1" ) );
consumers.put( "consumer-k2", createConsumerDetails( "consumer-k2", "consumer-n2", "consumer-secret2" ) );
}
private ConsumerDetails createConsumerDetails( String consumerKey, String consumerName, String consumerSecret ) {
SharedConsumerSecret secret = new SharedConsumerSecret( consumerSecret );
BaseConsumerDetails bcd = new BaseConsumerDetails();
bcd.setConsumerKey( consumerKey );
bcd.setConsumerName( consumerName );
bcd.setSignatureSecret( secret );
bcd.setAuthorities( new GrantedAuthority[] { new GrantedAuthorityImpl( "ROLE_OAUTH_USER" ) } );
// set this to false to enable the 2legged OAuth model
// see http://spring-security-oauth.codehaus.org/twolegged.html
bcd.setRequiredToObtainAuthenticatedToken( false );
return bcd;
}
@Override
public ConsumerDetails loadConsumerByConsumerKey( String key ) throws OAuthException {
log.info( "Request received to find consumer for consumerKey=[" + key + "]" );
ConsumerDetails consumer = consumers.get( key );
if ( consumer == null ) {
log.info( "Result: No consumer found for [" + key + "]" );
throw new OAuthException( "No consumer found for key " + key );
}
if ( log.isDebugEnabled() ) {
log.debug( "Result: Found consumer [" + consumer.getConsumerName() + "]" );
}
return consumer;
}
@Override
public UserDetails loadUserByUsername( String username ) throws UsernameNotFoundException, DataAccessException {
throw new UnsupportedOperationException();
}
}
Next component is a simple OAuth authentication handler that will help us create user authentication based on consumer authentication (more about this in the next sections).
public class ConsumerBasedAuthenticationHandler implements OAuthAuthenticationHandler {
@Override
public Authentication createAuthentication( HttpServletRequest request, ConsumerAuthentication authentication, OAuthAccessProviderToken authToken ) {
// return the consumer authentication to replace the user authentication
return authentication;
}
}
Right. Now we’re done with implementing the Java stuff. Let’s move on to defining and configuring the web application and the Spring beans.
First the web application descriptor:
<web-app>
<display-name>Spring Security OAuth Test</display-name>
<context-param>
<param-name>log4jConfigLocation</param-name>
<param-value>file:log4j.properties</param-value>
</context-param>
<context-param>
<param-name>log4jRefreshInterval</param-name>
<param-value>60000</param-value>
</context-param>
<context-param>
<param-name>log4jExposeWebAppRoot</param-name>
<param-value>false</param-value>
</context-param>
<filter>
<filter-name>springSecurityFilterChain</filter-name>
<filter-class>org.springframework.web.filter.DelegatingFilterProxy</filter-class>
</filter>
<filter-mapping>
<filter-name>springSecurityFilterChain</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
</web-app>
The code should be straightforward. We make all the requests go through the Spring Security filter chain so that they are inspected and so that the protected resources are placed behind the OAuth shield.
Finally, the last piece of configuration is the Spring application context:
<?xml version="1.0" encoding="UTF-8"?>
<beans:beans xmlns="http://www.springframework.org/schema/security"
xmlns:beans="http://www.springframework.org/schema/beans"
xmlns:oauth="http://spring-security-oauth.codehaus.org/3.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans-3.0.xsd
http://www.springframework.org/schema/security http://www.springframework.org/schema/security/spring-security-2.0.4.xsd
http://spring-security-oauth.codehaus.org/3.0 http://spring-security-oauth.codehaus.org/schema/spring-security-oauth-3.0.xsd">
<beans:bean name="dummyConsumerDetailsService" class="ro.bmocanu.test.spring.security.oauth.DummyConsumerDetailsServiceImpl"/>
<beans:bean name="consumerBasedAuthenticationHandler" class="ro.bmocanu.test.spring.security.oauth.ConsumerBasedAuthenticationHandler" />
<beans:bean name="oauthProcessingFilterEntryPoint" class="org.springframework.security.oauth.provider.OAuthProcessingFilterEntryPoint" />
<http auto-config='true' entry-point-ref="oauthProcessingFilterEntryPoint">
<intercept-url pattern="/photos/**" access="ROLE_OAUTH_USER"/>
<intercept-url pattern="/oauth/**" access="ROLE_OAUTH_USER" />
<intercept-url pattern="/**" access="IS_AUTHENTICATED_ANONYMOUSLY" />
</http>
<authentication-provider user-service-ref="dummyConsumerDetailsService"/>
<oauth:provider consumer-details-service-ref="dummyConsumerDetailsService"
auth-handler-ref="consumerBasedAuthenticationHandler"
token-services-ref="tokenServices"
request-token-url="/oauth/request_token"
authenticate-token-url="/oauth/authorize"
authentication-failed-url="/authorization_failed.jsp"
access-granted-url="/authorization_ok.jsp"
access-token-url="/oauth/access_token"
require10a="false"/>
<oauth:token-services id="tokenServices"/>
</beans:beans>
This last file is where the actual magic happens. The OAuth plugin for Spring Security defines the http://spring-security-oauth.codehaus.org/3.0 namespace, that aids in configuring the provider for the OAuth service. Above that we map the two classes of URLs of our application (the /photos/* and the /oauth/* under the ROLE_OAUTH_USER permission, so that only the authorized users can access these resources).
In the whole configuration of the OAuth plugin there is one tricky part (ok, there might be more than one, but after you read the documentation of the OAuth protocol and play around a little bit with the sample applications for the 3legged model, you get accustomed with this configuration): as the 2legged configuration page clearly describes it, you have to define your own authentication handler (in our example consumerBasedAuthenticationHandler) that allows you to convert the consumer authentication (which is handled by the plugin during the request/authorize/access token exchange) in an user authentication, which is required by Spring Security before allowing any client to access the protected resources.
This is exactly what the ConsumerBasedAuthenticationHandler presented above does. It receives the customer authentication object and simply returns it as the new user authentication.
Client
The second player in our drama is the test client. This is a very simple application that tries to get access to the protected resource: the photo located at /soauth/photos/photo1.jpg. In order to get there, it must access the server and play by the OAuth rules.
For implementing the client we can use the same library that we used for configuring the OAuth on the server side: the OAuth plugin for the Spring Security. This library contains both the server-side support and the client-side support.
public class TestClient {
private static final Log log = LogFactory.getLog( TestClient.class );
private static final String SERVER_URL = "http://localhost:9090/soauth";
private static final String SERVER_URL_OAUTH_REQUEST = SERVER_URL + "/oauth/request_token";
private static final String SERVER_URL_OAUTH_AUTHZ = SERVER_URL + "/oauth/authorize";
private static final String SERVER_URL_OAUTH_ACCESS = SERVER_URL + "/oauth/access_token";
private static final String SERVER_URL_RESOURCE = SERVER_URL + "/photos/photo1.jpg";
private static final String CONSUMER_KEY = "consumer-k1";
private static final String CONSUMER_SECRET = "consumer-secret1";
private static final String SIGNATURE_METHOD = "HMAC-SHA1";
private static final String RESOURCE_ID = "photo1";
private static OAuthConsumerSupport consumerSupport;
// -------------------------------------------------------------------------------------------------
private static OAuthConsumerToken requestToken;
private static String requestTokenVerifier;
private static OAuthConsumerToken accessToken;
// -------------------------------------------------------------------------------------------------
public static void main( String[] args ) throws HttpException, IOException {
CoreOAuthConsumerSupport localConsumerSupport = new CoreOAuthConsumerSupport();
localConsumerSupport.setStreamHandlerFactory( new DefaultOAuthURLStreamHandlerFactory() );
localConsumerSupport.setProtectedResourceDetailsService( new ProtectedResourceDetailsService() {
@Override
public ProtectedResourceDetails loadProtectedResourceDetailsById( String id ) throws IllegalArgumentException {
SignatureSecret secret = new SharedConsumerSecret( CONSUMER_SECRET );
BaseProtectedResourceDetails result = new BaseProtectedResourceDetails();
result.setConsumerKey( CONSUMER_KEY );
result.setSharedSecret( secret );
result.setSignatureMethod( SIGNATURE_METHOD );
result.setUse10a( false );
result.setRequestTokenURL( SERVER_URL_OAUTH_REQUEST );
result.setAccessTokenURL( SERVER_URL_OAUTH_ACCESS );
result.setUserAuthorizationURL( SERVER_URL_OAUTH_AUTHZ );
return result;
}
} );
consumerSupport = localConsumerSupport;
getRequestToken();
authorizeRequestToken();
getAccessToken();
getProtectedResource();
}
public static void getRequestToken() {
log.info( "OAUTH: Request token: getting..." );
requestToken = consumerSupport.getUnauthorizedRequestToken( RESOURCE_ID, null );
log.info( "OAUTH: Request token: " + requestToken.getValue() );
log.info( "OAUTH: Request token secret: " + requestToken.getSecret() );
}
public static void authorizeRequestToken() throws HttpException, IOException {
log.info( "OAUTH: Authorizing token" );
int resultCode = 0;
HttpClient httpClient = new HttpClient();
PostMethod authorizeMethod = new PostMethod( SERVER_URL_OAUTH_AUTHZ );
authorizeMethod.addParameter( "requestToken", requestToken.getValue() );
authorizeMethod.addParameter( "authorize", "Authorize" );
resultCode = httpClient.executeMethod( authorizeMethod );
String body = authorizeMethod.getResponseBodyAsString();
String redirectURL = authorizeMethod.getResponseHeader( "Location" ).getValue();
if ( redirectURL != null && redirectURL.indexOf( "oauth_verifier" ) > -1 ) {
requestTokenVerifier = redirectURL.substring( redirectURL.indexOf( "oauth_verifier" ) + 15 );
}
log.info( "OAUTH: Authorization URL code : " + resultCode );
log.info( "OAUTH: Authorization URL body : " + body );
log.info( "OAUTH: Authorization URL redirect : " + redirectURL );
log.info( "OAUTH: Authorization URL verifier : " + requestTokenVerifier );
}
public static void getAccessToken() {
log.info( "OAUTH: Access token: getting..." );
accessToken = consumerSupport.getAccessToken( requestToken, requestTokenVerifier );
log.info( "OAUTH: Access token: " + accessToken.getValue() );
log.info( "OAUTH: Access token secret: " + accessToken.getSecret() );
}
public static void getProtectedResource() throws OAuthRequestFailedException, IOException {
log.info( "OAUTH: Getting protected resource" );
InputStream is = consumerSupport.readProtectedResource( new URL( SERVER_URL_RESOURCE ), accessToken, "GET" );
ByteArrayOutputStream baos = new ByteArrayOutputStream();
IOUtils.copy( is, baos );
is.close();
baos.close();
log.info( "OAUTH: Resource : " + new String( Arrays.copyOf( baos.toByteArray(), 30 ) ) );
}
}
From the code above, probably the trickiest part is the authorization one. The problem that I found is that the request token still needs to be authorized, although not with a page that needs to be validated by the user or with a login that the user (that is, the resource owner) must perform (that is because this is still the normal mode of OAuth; for the 2Legged mode only one request needs to be made, using the consumer key and the consumer secret; read this for a better explanation). The authorization request still needs to be made, and for the test client I used the HttpClient in order to call the URL and pass in the request token to authorize. Once this step is performed, we get back from the server the verifier string that we can use in our last request for which we get the long desired item: the access_token.
After we got our hands on this access token, we can freely access the protected resource, given that we sign each request to the protected resource with this access token.
Conclusion
This is one simple example of implementing the 2legged model for the OAuth protocol. With these components, one can protect resources and allow access to these resources only for the clients that 1) know and respect the protocol and 2) are in possession of the correct consumer key and consumer secret, which they have to use to sign their requests and perform the handshake with the server.
Please feel free to provide feedback and comment on the items discussed above.








xpat
October 24, 2010 at 2:47 am
Hi,
Thanks for publishing this, its one of the few sources demonstrating spring security oauth.
I am confused by the part were you were required to obtain authorization for the consumer after sending the consumer key and shared secret. Furthermore, why need an access key if you are intending to demonstrate 2-legged model? According to my understanding the whole point of 2-legged is that you skip the steps for getting a request token and then authorizing it (no need to get authorization from the user and no need for a temprorary access key). See for example yahoo’s documentation about it: http://developer.yahoo.com/oauth/guide/oauth-private_public_data.html
From the spring oauth documentation I see a piece that I did not see in your example which could explain the issue you had with requiring token authorization:
“To implement 2-legged OAuth using OAuth for Spring Security, all that is needed is for the provider to indicate that a specific consumer has an extra level of trust. To do this, make sure your implementation of ConsumerDetailsService returns instances of ConsumerDetails that implement ExtraTrustConsumerDetails. Then, for each consumer that doesn’t need to obtain a user-authorized token, make sure ExtraTrustConsumerDetails.isRequiredToObtainAuthenticatedToken() returns false.”
So, I guess what I am not understanding is why did you have to go through the whole process of doing a fake 3-legged flow to implement 2-legged.
In theory the 2-legged flow should simply be (assuming the consumer has a registered consumer-key and secret with the provider):
- Send oauth request for protected resouce with key and shared secret (specifying oauth signature method)
- provider returns request after verifying consumer credentials
Sorry to question the approach, but I am just wondering if I am wrong and perhaps you can clarify my misunderstanding… thank you!
Bogdan Mocanu
October 25, 2010 at 4:43 pm
Hi xpat,
if I understood correctly the OAuth protocol, the 2-legged model means that you still have the first and last step of the process (that is, step1=get the request token, step3=get the access token) but you don’t have the step2, which is the authentication + authorization required from the (human)user part.
The thing is that employing the OAuth protocol between two machines makes the Step2 unappropriate, since it is not acceptable to ask a machine to open a website, log in and accept the sharing of resources. Therefore, on the server where the resource is located you authorize the request token (sent in Step1) without asking for user intervention (since that user is a machine, in our case).
The flow that you are mentioning is neither the 3-legged model, nor the 2-legged model. It is just the shortcut of the entire process, and can take place in either of the models. Here is what it happens:
- System1 (S1) makes a request-token call to System2 (S2) (which has the protected resource). S2 returns the request token.
- the authentication and authorization of S1 takes place (by involvement of S1, or directly by S2 (this is the 2-legged model))
- S1 makes an access-token call to S2. S2 returns the access token
Now S1 is in possession of the access token. It should be able to access the resource any time, with ONE call, any number of repeated times, directly by signing the request with this latter token, the access token. This assumes that S1 will safely store the access token on its side.
Of course, to protect the resources even further, HTTPS cand be used, as well as some timeouts for the generated access tokens. However, this is not part of the OAuth protocol, and varies from case to case.
xpat
October 25, 2010 at 11:30 pm
Hi Bogdan,
From my research the 2-legged flow, does not require an access token to be generated and used before getting protected resources. The implementation of 2-legged assumes you will be using your consumer key and secret to access the protected resources (hence the default behavior of spring security oauth of setting your consumer authentication in the context). In case you didn’t check out the flow yahoo documented in the link I posted earlier, See this additional example explanation of 2-legged here: http://nagiworld.net/2008/11/oauth-ify-this-2-legged-oauth-service-for-yql
and example URL query would be like this:
http://localhost:8080/myapp/myservice?oauth_version=1.0&oauth_nonce=a0b24b3b5d88f97fa83c0e4e08e6f6d4&oauth_timestamp=1288038049&oauth_consumer_key=consumer-key&oauth_signature_method=HMAC-SHA1&oauth_signature=DADSFAfczdDFdfaBRKSAOsssWERHHaaa%3D
And that would return the protected resource, no need to request an access token.
By definition, an Access token is a value required to gain access to a protected resource on behalf of the user. In the 2-legged scenario, the consumer and the user (resource owner) are the same, so why create a temporary access token to get the same level of protection over the protected resource that the consumer-key/secret gives you?
If you follow that assumption you wouldn’t have had to create a custom auth handler, the 2-legged configuration page in the spring security oauth documentation doesn’t say you have to define your own authentication handler, it says: “In many instances, providers may want to manage the authentication that is set up in the security context. By default for 2-legged OAuth, only the consumer’s authentication will be set up in the context. However, if a user authentication is needed in the context, provide an alternate implementation of org.springframework.security.oauth.provider.OAuthAuthenticationHandler that loads the user authentication, and provide a reference to the alternate implementation using the “auth-handler-ref” attribute of the “provider” configuration element.”
I just finished implementing this with SS3 and SSOauth3.19SS3 and did not have to implement the authorization handler. Your example was super useful though, but I believe there is an incorrect assumption that you have to use access tokens in 2-legged in your implementation. If you test your implementation with any of the oauth clients out there, and try to do a 2-legged flow with their examples… it will fail, because you are asking the client to create an access token (for nor reason or benefit since the same level of access you have by generating an access token is attained by just having the consumer key and secret).
As a comparison in hueniverse there is a reference to 2-legged compared with basic authentication, the difference is instead of a user/password for an application yo have a consumer/secret. There is no extra security you are adding by doing this, in the end if I have the consumer key and secret i have the ability to generate those access tokens, so why modifying the flow to add that extra step? Unless you want to do some other custom security.
Amit
September 20, 2011 at 9:42 am
how to implement Zero -legged Oauth ??
xpat
October 26, 2010 at 12:47 am
Also refer to this reply from Ryan Heaton on the spring security oauth forum:
http://markmail.org/message/6sbqwy75s6bbvtib#query:spring%20security%20oauth%20consumer%202-legged+page:1+mid:4bhhskax4fvzdsoa+state:results
Bogdan Mocanu
October 27, 2010 at 9:34 pm
Thanks a lot for the information and the trouble to write such a detailed explanation.
Yes, I believe you are right. Reading some quick Google results (didn’t had the time yet to research this matter more deeply) (this one http://sites.google.com/site/oauthgoog/2leggedoauth/2opensocialrestapi and this one http://oauth.googlecode.com/svn/spec/ext/consumer_request/1.0/drafts/2/spec.html#anchor3) led me to think that the 2-legged model is indeed like you describe.
I am planning on making another example and see exactly how this is working. The example from this article is, then, the 3-legged model, with just an automatic replacement for the step 2, where the user needs to authorize the token.
I hope I will have time soon to revise these example, and correct the mistake. Thanks again!
Muhammad Yousaf
November 13, 2011 at 2:54 am
I cannot tell how much informative this blog has been for me to get the clear understanding of the difference b/w 2 legged and the 3 legged.
@Bogdan have you posed the 2 legged implementation somewhere?
xpat
November 5, 2010 at 12:22 am
no problem Bodgan, glad I could help.
thanks to your example I was able to sort out the 2-legged approach though, your blog on this is pretty much the only search result with an actual sample so far…
cheers!
Tom Barber
December 1, 2010 at 2:42 am
You sir are a legend, finding a proper test client was impossible, I’d virtually given up hope, then 15 minutes later I could validate my server.
Bogdan Mocanu
December 1, 2010 at 10:31 am
Thanks Tom, I’m glad I could help.
Steve Kingsland
March 10, 2011 at 10:06 pm
Bogdan,
Thanks for your test client! It worked great, problem was Spring Security’s “Sparklr” needed a couple of bug fixes before it would work for me. I realized what the problem was after reading smithap’s post in this thread, and then posted my solution there as well:
http://forum.springframework.org/showthread.php?p=350320
That contains 2 different working 2-legged OAuth clients. (I’ve got both working for me.)
demonzoo
August 9, 2011 at 5:22 am
Thank you very much for your blog. I love the sample! Thank you!
Ramesh
October 25, 2011 at 6:57 pm
Hello Bogdan,
I guess it is more than a year you posted this and just want to let you know that still it is probably the best resource for understanding 2 legged OAuth with Spring Security.
After reading xpat’s comment, I got confused thinking it wasn’t the right implementation but then at the end, I found this is how you do oauth with spring.
Thank you Sir,
regards
Ramesh
Ramesh
October 25, 2011 at 6:59 pm
I think this should make it two legged oauth without need to write DummyConsumerDetailsServiceImpl !
Muhammad Yousaf
November 13, 2011 at 2:57 am
I cannot tell how much informative this blog is to tell the difference b/w 2 legged and 3 legged approach.
Bogdan please lemme know if you have posted the 2 legged implementation somewhere
Xpat you are awesome. If you have the example too please post that.
Thanks and appreciate this wonderful blog.
Muhammad Yousaf
November 14, 2011 at 10:01 pm
Following are the changes I have made to make it OAuth 2 legged implementation
package com.sequent.resources;
import java.util.HashMap;
import java.util.Map;
import org.springframework.dao.DataAccessException;
import org.springframework.security.GrantedAuthority;
import org.springframework.security.GrantedAuthorityImpl;
import org.springframework.security.oauth.common.OAuthException;
import org.springframework.security.oauth.common.signature.SharedConsumerSecret;
import org.springframework.security.oauth.provider.BaseConsumerDetails;
import org.springframework.security.oauth.provider.ConsumerDetails;
import org.springframework.security.oauth.provider.ConsumerDetailsService;
import org.springframework.security.userdetails.UserDetails;
import org.springframework.security.userdetails.UserDetailsService;
import org.springframework.security.userdetails.UsernameNotFoundException;
public class ConsumerDetailsServiceImpl implements ConsumerDetailsService, UserDetailsService {
private Map consumers;
public ConsumerDetailsServiceImpl() {
consumers = new HashMap();
consumers.put( “consumer-k1″, createConsumerDetails( “consumer-k1″, “consumer-n1″, “consumer-secret1″ ) );
consumers.put( “consumer-k2″, createConsumerDetails( “consumer-k2″, “consumer-n2″, “consumer-secret2″ ) );
}
private ConsumerDetails createConsumerDetails( String consumerKey, String consumerName, String consumerSecret ) {
SharedConsumerSecret secret = new SharedConsumerSecret( consumerSecret );
BaseConsumerDetails bcd = new BaseConsumerDetails();
bcd.setConsumerKey( consumerKey );
bcd.setConsumerName( consumerName );
bcd.setSignatureSecret( secret );
bcd.setAuthorities( new GrantedAuthority[] { new GrantedAuthorityImpl( “ROLE_OAUTH_USER” ) } );
// set this to false to enable the 2legged OAuth model
// see http://spring-security-oauth.codehaus.org/twolegged.html
bcd.setRequiredToObtainAuthenticatedToken( false );
return bcd;
}
@Override
public ConsumerDetails loadConsumerByConsumerKey( String key ) throws OAuthException {
System.out.println( “Request received to find consumer for consumerKey=[" + key + "]” );
ConsumerDetails consumer = consumers.get( key );
if ( consumer == null ) {
System.out.println( “Result: No consumer found for [" + key + "]” );
throw new OAuthException( “No consumer found for key ” + key );
}
System.out.println( “Result: Found consumer [" + consumer.getConsumerName() + "]” );
return consumer;
}
@Override
public UserDetails loadUserByUsername( String username ) throws UsernameNotFoundException, DataAccessException {
throw new UnsupportedOperationException();
}
}
————————————————————————
web.xml
———————————————————————–
springSecurityFilterChain
org.springframework.web.filter.DelegatingFilterProxy
springSecurityFilterChain
/*
org.springframework.web.context.ContextLoaderListener
—————————————————————————
applicationContext.xml
—————————————————————————
————————————————————————-
Client Test
————————————————————————–
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.net.URL;
import java.util.Arrays;
import org.apache.commons.httpclient.HttpClient;
import org.apache.commons.httpclient.HttpException;
import org.apache.commons.httpclient.methods.PostMethod;
import org.apache.commons.io.IOUtils;
import org.springframework.security.oauth.common.signature.SharedConsumerSecret;
import org.springframework.security.oauth.common.signature.SignatureSecret;
import org.springframework.security.oauth.consumer.BaseProtectedResourceDetails;
import org.springframework.security.oauth.consumer.CoreOAuthConsumerSupport;
import org.springframework.security.oauth.consumer.OAuthConsumerSupport;
import org.springframework.security.oauth.consumer.OAuthRequestFailedException;
import org.springframework.security.oauth.consumer.ProtectedResourceDetails;
import org.springframework.security.oauth.consumer.ProtectedResourceDetailsService;
import org.springframework.security.oauth.consumer.net.DefaultOAuthURLStreamHandlerFactory;
import org.springframework.security.oauth.consumer.token.OAuthConsumerToken;
public class ApiConsumer {
private static final String SERVER_URL = “http://localhost:8080/sequent-api-public”;
private static final String SERVER_URL_RESOURCE = SERVER_URL + “/sqms/v1/walletmetadata/getTermsOfUse”;
private static final String CONSUMER_KEY = “consumer-k1″;
private static final String CONSUMER_SECRET = “consumer-secret1″;
private static final String SIGNATURE_METHOD = “HMAC-SHA1″;
private static final String RESOURCE_ID = “PublicApi”;
private static OAuthConsumerSupport consumerSupport;
public static void main( String[] args ) throws HttpException, IOException {
CoreOAuthConsumerSupport localConsumerSupport = new CoreOAuthConsumerSupport();
localConsumerSupport.setStreamHandlerFactory( new DefaultOAuthURLStreamHandlerFactory() );
localConsumerSupport.setProtectedResourceDetailsService( new ProtectedResourceDetailsService() {
@Override
public ProtectedResourceDetails loadProtectedResourceDetailsById( String id ) throws IllegalArgumentException {
SignatureSecret secret = new SharedConsumerSecret( CONSUMER_SECRET );
BaseProtectedResourceDetails result = new BaseProtectedResourceDetails();
result.setConsumerKey( CONSUMER_KEY );
result.setSharedSecret( secret );
result.setSignatureMethod( SIGNATURE_METHOD );
result.setUse10a( false );
return result;
}
} );
consumerSupport = localConsumerSupport;
getProtectedResource();
}
public static void getProtectedResource() throws OAuthRequestFailedException, IOException {
System.out.println( “OAUTH: Getting protected resource” );
OAuthConsumerToken accessToken = new OAuthConsumerToken();
accessToken.setResourceId(RESOURCE_ID);
InputStream is = consumerSupport.readProtectedResource( new URL( SERVER_URL_RESOURCE ), accessToken, “GET” );
ByteArrayOutputStream baos = new ByteArrayOutputStream();
IOUtils.copy( is, baos );
is.close();
baos.close();
System.out.println( “OAUTH: Resource : ” + new String( Arrays.copyOf( baos.toByteArray(), 30 ) ) );
}
}
Client for OAuth with Spring Security « Bogdan Mocanu | Coding
November 20, 2011 at 2:53 pm
[...] one year ago I wrote an article in which I described how can one implement a client and a server that uses the OAuth security [...]
Bogdan
November 20, 2011 at 3:12 pm
Thank you all for the comments on this article. Because this article contains an incorrect explanation of the OAuth 2Legged mode, I wrote a newer and (I hope) better article on this subject, available here: http://bmocanu.ro/coding/409/client-for-oauth-with-spring-security/.
Thusitha
January 1, 2012 at 11:42 pm
Thanks for your implementation of two legged approach.
ConsumerBasedAuthenticationHandler class was the most important bit to me..Thanks for that.I think Oauth2.0 will be released soon.I think it will offload some complexity with Oauth 1.0..
Thanks very much