Search code examples
javaoauth-2.0github-apidropwizardscribe

Dropwizard, Scribejava, Oauth 2.0 and Github


I'm attempting to authenticate with GitHub via oauth using the scribejava library in a Dropwizard (I guess jetty) application. I'm not overly sure of what I'm doing as I'm new to all this but I've hacked together the following resource class from a number of different blogs and examples.

@Path("/github")
@Produces(MediaType.APPLICATION_JSON)
public class GithubResource {

private static final Logger LOGGER = LoggerFactory.getLogger(GithubResource.class);

public static final String API_KEY = "APIKEY";

public static final String API_SECRET = "SECRETKEY";

private static final String PROTECTED_RESOURCE_URL = "https://api.github.com/users";

private static final Token EMPTY_TOKEN = null;

@GET
@Produces(MediaType.TEXT_PLAIN)
public Response redirectToAuthorization() {

    OAuthService service = createService()
        .callback("https://localhost:8443/github/continue")
        .build();

    String authURL = service.getAuthorizationUrl(EMPTY_TOKEN);

    return Response.seeOther(URI.create(authURL)).build();
}

@GET
@Path("continue")
@Produces(MediaType.TEXT_PLAIN)
public Response redirectToApp(@QueryParam("client_id") String oauthToken, @QueryParam("code") String oauthVerifier) {

    OAuthService service = createService().build();

    Verifier verifier = new Verifier(oauthVerifier);

    Token requestToken = service.getAccessToken(EMPTY_TOKEN, verifier);
    Token accessToken = service.getAccessToken(requestToken, verifier);

    OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL, service);
    service.signRequest(accessToken, request);

    com.github.scribejava.core.model.Response response = request.send();

    return Response.ok(response.getBody()).build();
}


private ServiceBuilder createService() {
    return new ServiceBuilder()
        .provider(GitHubApi.class)
        .apiKey(API_KEY)
        .apiSecret(API_SECRET);
}
}

It's taken me a few days to get to this point but I can now successfully pull out the code from the response from github (I think) but then the app crashes with the following error log

ERROR [2015-11-23 03:12:37,417] io.dropwizard.jersey.errors.LoggingExceptionMapper: Error handling a request: fdf48e68be65c626
! com.github.scribejava.core.exceptions.OAuthException: Response body is incorrect. Can't extract a token from this: 'error=redirect_uri_mismatch&error_description=The+redirect_uri+MUST+match+the+registered+callback+URL+for+this+application.&error_uri=https%3A%2F%2Fdeveloper.github.com%2Fv3%2Foauth%2F%23redirect-uri-mismatch2'
! at com.github.scribejava.core.extractors.TokenExtractor20Impl.extract(TokenExtractor20Impl.java:32) ~[scribejava-core-2.0.jar:na]
! at com.github.scribejava.core.oauth.OAuth20ServiceImpl.getAccessToken(OAuth20ServiceImpl.java:37) ~[scribejava-core-2.0.jar:na]
! at org.squandered.generator.resource.GithubResource.redirectToApp(GithubResource.java:58) ~[classes/:na]
! at sun.reflect.NativeMethodAccessorImpl.invoke0(Native Method) ~[na:1.7.0_51]
! at sun.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:57) ~[na:1.7.0_51]
! at sun.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43) ~[na:1.7.0_51]
! at java.lang.reflect.Method.invoke(Method.java:606) ~[na:1.7.0_51]

I see the "Response body is incorrect. Can't extract a token from this" but I'm really not sure how I can correctly do this...

Does anyone see any obvious flaws with my code, or have a working example of using these libraries with oauth 2.0? Google seems to return results for oauth 1.0 which is a little different...

Appreciate any help in the right direction

Cheers


Solution

  • Was actually a very simple bug, I wasn't pulling the correct param from the response URL in the callback method. Complete working example:

    import com.github.scribejava.apis.GitHubApi;
    import com.github.scribejava.core.builder.ServiceBuilder;
    import com.github.scribejava.core.model.OAuthRequest;
    import com.github.scribejava.core.model.Token;
    import com.github.scribejava.core.model.Verb;
    import com.github.scribejava.core.model.Verifier;
    import com.github.scribejava.core.oauth.OAuthService;
    import org.slf4j.Logger;
    import org.slf4j.LoggerFactory;
    
    import javax.ws.rs.GET;
    import javax.ws.rs.Path;
    import javax.ws.rs.Produces;
    import javax.ws.rs.QueryParam;
    import javax.ws.rs.core.MediaType;
    import javax.ws.rs.core.Response;
    import java.net.URI;
    import java.util.Random;
    
    @Path("/github")
    public class GithubResource {
    
        private static final Logger LOGGER = LoggerFactory.getLogger(GithubResource.class);
        private static final String PROTECTED_RESOURCE_URL = "https://api.github.com/user";
        private static final String API_KEY = "your key";
        private static final String API_SECRET = "your secret";
        private static final String CALL_BACK_URL = "https://localhost:8443/github/callback/";
        private static final Token EMPTY_TOKEN = null;
    
        @GET
        public Response getToken() {
            OAuthService service = createService().build();
    
            final String authorizationUrl = service.getAuthorizationUrl(EMPTY_TOKEN);
    
            return Response.seeOther(URI.create(authorizationUrl)).build();
        }
    
        @GET
        @Path("callback")
        @Produces(MediaType.TEXT_PLAIN)
        public Response callback(@QueryParam("code") String oauthToken) {
    
            OAuthService service = createService().build();
    
            final Verifier verifier = new Verifier(oauthToken);
    
            final Token accessToken = service.getAccessToken(EMPTY_TOKEN, verifier);
            final OAuthRequest request = new OAuthRequest(Verb.GET, PROTECTED_RESOURCE_URL, service);
            service.signRequest(accessToken, request);
    
            final com.github.scribejava.core.model.Response response = request.send();
    
            LOGGER.info("Response code: " + response.getCode());
            LOGGER.info("Response body: " + response.getBody());
    
            return Response.ok(response.getBody()).build();
        }
    
        private ServiceBuilder createService() {
            return new ServiceBuilder()
                .provider(GitHubApi.class)
                .apiKey(API_KEY)
                .apiSecret(API_SECRET)
                .scope("repo")
                .state("secret" + new Random().nextInt(999_999))
                .callback(CALL_BACK_URL);
        }
    }