Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Enabling OAuth1 Support on a Jersey Jax-rs web service

I'd like to enable OAuth1 Provider support on my restful web service. Jersey supports this as described here Jersey OAuth1 Provider support. I've been trying to register it as so:

public ApplicationConfig(){
    super();
    addRestResourceClasses(getMyResourceClasses());                 
    register(new OAuth1ServerFeature(new DefaultOAuth1Provider(),"/oauth/access_token","/oauth/request_token"));
}

But, when I register the OAuth1ServerFeature, I get a 404 when trying to access my resources. Can't seem to find any examples/tutorials implementing jersey oauth support anywhere!

Is there a simple component I can plug into my jax-rs service to enable oauth support?

like image 934
Andrew Avatar asked Feb 15 '14 14:02

Andrew


People also ask

What is the difference between OAuth1 and oauth2?

Once the token was generated, OAuth 1.0 required that the client send two security tokens on every API call, and use both to generate the signature. OAuth 2.0 has only one security token, and no signature is required.

Is oauth2 compatible with OAuth1?

OAuth 2.0 is not backwards compatible with OAuth 1.0 or 1.1, and should be thought of as a completely new protocol. OAuth 1.0 was largely based on two existing proprietary protocols: Flickr's authorization API and Google's AuthSub.

How does OAuth1 0a work?

OAuth1 can be used for authorization of various applications or manual user access. It works by providing an application with an access token (representing a user's permission for the client to access their data) to use for request authentication.


1 Answers

I realise this thread is somewhat old - but having just got it work myself, I felt a reply was in order! Given time, I may even create a blog post with a fuller example. Be warned - this is not a short answer!

There is an absolute lack of examples on information on using the OAuth1 server (aka Provider) feature in Jersey - I can't remember a tech topic that revealed so little useful Google information. I almost passed on looking for another solution since it led me to think perhaps it didn't work. But, with some perseverance, I can say that not only is it usable, but it seems to work rather well. Plus of course, if you're already using Jersey for your REST API - you don't need any extra libs.

I am not an OAuth1 expert - and I'd strongly recommend some background reading for those attempting this. I am also assuming here you have Jersey working, understand things like ContainerRequestFilters, and also have some internal means to authorize users.

My examples also use the excellent JAX-RS OSGi connector - the only real difference is that where we use an OSGi bundle context to register the OAuth1 feature via an OSGI service, regular Jersey users will need to configure via their normal Application / Server config model.

Initialisation

You must create your OAuth1 feature - and give it a provider:

DefaultOAuth1Provider oap = new DefaultOAuth1Provider();
Feature oaFeature = new OAuth1ServerFeature(oap, "oauth1/request_token", "oauth1/access_token");       

Don't forget to register oaFeature into Jersey!

The DefaultOAuth1Provider is entirely memory based - which was fine for us to start with. Many will want to persist access tokens for use across server restarts, which will require an extended subclass (or clean implementation)

Add in your Consumers Keys and Secrets

It took me a while to realise Consumers were not users but clients i.e. applications. The Jersey implementation will not work if you don't register keys and secrets for each consumer (aka client app) that wishes to connect

oap.registerConsumer("some-owner-id", 
                      "abcdef" ,
                      "123456",
                      new MultivaluedHashMap<String,String> ());

You obviously would never hard-code these, and further would use some form of secure store for the secret (param 3).

If you do not add these you will not get any further.

OAuth protocol step 1 - get a request token

At this stage you are ready client side to get a request token - and here there is a perfectly good example on GitHub.

ConsumerCredentials consumerCredentials = new ConsumerCredentials("abcdef","123456");

//TODO - user proper client builder with real location + any ssl context
OAuth1AuthorizationFlow authFlow = OAuth1ClientSupport.builder(consumerCredentials)
            .authorizationFlow(
                    "http://myhost:8080/myapi/oauth1/request_token",
                    "http://myhost:8080/myapi/oauth1/access_token",
                    "http://myhost:8080/myapi/oauth1/authorize")
            .build();
String authorizationUri = authFlow.start();
System.out.println("Auth URI: " + authorizationUri); 

Obviously you would change URLs to point to your server and - crucially - the client needs to use the same Conumer Key and Secret you registered in the server.

You will get back a response with an oauth_token string in it e.g.

http://myhost:8080/myapi/oauth/authorize?oauth_token=a1ec37598da
b47f6b9d770b1b23a5f99

OAuth protocol step 2 - authorize the user

As you will read in any article, actual user Authorization is outside of the scope of OAuth1 - at this stage you must invoke your servers auth process whatever that is.

However!!!! What is not outside the OAuth1 scope is what your server needs to do if the user authorizes successfully. You must tell your DefaultOAuth1Provider about the successful auth:

// Dummy code - make out like we're auth'd
Set<String> dummyRoles = new HashSet<> (Arrays.asList( new String[] { "my-role-1", "my-role-2" }));
DefaultOAuth1Provider.Token tok1 = getRequestToken("a1ec37598da
b47f6b9d770b1b23a5f99");
String verifier = authorizeToken(tok1, new Principal()
            {
                public String getName()
                {
                    return "my-user";
                }
            }, 
            dummyRoles);
System.out.println("***** verifier: " + verifier);

Note the request token string is that from step 1. Obviously a real implementation would pass a real Principal and set of roles for the authorized user.

Also, of course, printing out the verifier is not much use - you need to get that back to your client in some way, either via an independent channel or possibly as a header in the auth response - which maybe would need to be encrypted for added protection.

OAuth protocol step 3 - swap the request token for an access token

Once the client receives or has the verifier entered manually, it can finalize the process and swap the request token for an access token e.g.

String verifier = System.console().readLine("%s", "Verifier: ");
final AccessToken accessToken = authFlow.finish(verifier);        
System.out.println("Access token: " + accessToken.getToken());      

Again, not a realistic example - but it shows the process.

If your OAuth1Provider saves access tokens to some persistent store on the server, you can re-use any access token returned here on a future session without going through all the previous steps.

That's it - you then just need to make sure every request the client creates from this point on in the process makes use of that access token.

like image 113
Rob Walker Avatar answered Oct 13 '22 01:10

Rob Walker