groovyoauthjira-rest-apihttpbuilder

Groovy - Jira OAuth integration using HttpBuilder


I want to get data using JIRA REST api with provided JIRA OAuth authentication service.

Basically I'm able to achieve this task using ScribeJava with Groovy. But I want to decoupled all the process as below :-

So I'm able to achieve above mentioned first three steps using ScribeJava and storing the accessToken into Database for further request for data as below :-

import java.security.KeyFactory
import java.security.PrivateKey
import java.security.spec.PKCS8EncodedKeySpec

import com.github.scribejava.core.builder.api.DefaultApi10a
import com.github.scribejava.core.model.OAuth1RequestToken
import com.github.scribejava.core.services.RSASha1SignatureService
import com.github.scribejava.core.services.SignatureService


class JiraOauthProvider extends DefaultApi10a {

    private String authURL
    private String requestTokenURL
    private String accessTokenURL
    private String consumerPrivateKey

    private JiraOauthProvider(authURL, requestTokenURL, accessTokenURL, consumerPrivateKey) {
        this.authURL = authURL
        this.requestTokenURL = requestTokenURL
        this.accessTokenURL = accessTokenURL
        this.consumerPrivateKey = consumerPrivateKey
    }

    private static JiraOauthProvider instance = null

    public static JiraOauthProvider instance(Map map) {
        if(instance == null) {
            instance = new JiraOauthProvider(map.authURL,
                    map.requestTokenURL,
                    map.accessTokenURL,
                    map.consumerPrivateKey)
        }
        return instance
    }

    @Override
    public String getAccessTokenEndpoint() {
        return accessTokenURL
    }

    @Override
    public String getRequestTokenEndpoint() {
        return requestTokenURL
    }

    @Override
    public String getAuthorizationUrl(OAuth1RequestToken requestToken) {
        return String.format(authURL, requestToken.getToken())
    }

    @Override
    public SignatureService getSignatureService() {
        return new RSASha1SignatureService(getPrivateKey())
    }

    private PrivateKey getPrivateKey() {
        byte[] key = Base64.getDecoder().decode(consumerPrivateKey)
        PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(key)
        KeyFactory kf = KeyFactory.getInstance("RSA")
        return kf.generatePrivate(keySpec)
    }

Now I'm building OAuthService as :-

private static final String CALLBACK_URI = "callback-url"
protected static final String CONSUMER_KEY = "consumer-key"
protected static final String CONSUMER_PRIVATE_KEY = "private-key"

Map oAuthMap = [
                "authURL" :"auth-url=%s",
                "requestTokenURL":"request-token-url",
                "accessTokenURL":"access-token-url",
                "consumerPrivateKey":CONSUMER_PRIVATE_KEY
            ]

//Buid oauth service to get request token, auth url and access token
OAuth10aService service = ServiceBuilder()
                .apiKey(CONSUMER_KEY)
                .apiSecret(CONSUMER_PRIVATE_KEY).callback(CALLBACK_URI)
                .build(JiraOauthProvider.instance(oAuthMap))

OAuth1RequestToken requestToken = service.getRequestToken()
def authURL = service.getAuthorizationUrl(requestToken)

//Now after redirect to this authURL and providing credential I'm getting oauthVerifier code to get accessToken and secretToken

def oauthVerifier = "oauth verifier code"

//Now calling to get accessToken
OAuth1AccessToken oAuth1AccessToken = service.getAccessToken(requestToken, oauthVerifier);
def accessToken = oAuth1AccessToken.getToken()
def secretToken = oAuth1AccessToken.getTokenSecret()
//now I'm storing this `accessToken`and `secretToken` into DB for further future data request.

So after all above stuff I'm able to achieve above mentioned three steps and storing the access token into db for future request only for data.

So to achieve 4th step to getting actual data using HTTPBuilder I'm doing some thing as below :-

def http  = new HTTPBuilder('base-url')

http.auth.oauth CONSUMER_KEY, CONSUMER_PRIVATE_KEY, accessToken, secretToken

http.request(Method.GET, ContentType.JSON) { req ->
            uri.path = 'path'
            response.success = { resp, json ->
                println json
            }
            response.failure = { resp, json -> print json }
        }
    }

But I'm getting response as :-

{oauth_problem=signature_method_rejected}

So, could anyone suggest me how can I get actual data using HTTPBuilder with OAuth authentication using accessToken and secretToken?

Note:- I can get actual data as well using ScribeJava Api with OAuthRequest but requirement is to get actual data using HTTPBuilder

I just want a pointer that how to achieve it.


Solution

  • After lot of search I have got the solution from here. Actually HTTPBuilder internally using Signpost which signing the request using HmacSha Signer while Jira rest api supports RSA-SHA1 Signer to validate the HttpRequest that's why it's giving response as :-

    {oauth_problem=signature_method_rejected}
    

    So, basically I have to do custom RSA-SHA1 Signer to get the signature to http request. To achive this I'm using Google Data (GData) APIs to sign the data using RSA-SHA1 Signer before HttprRequest as below :-

    private static PrivateKey getPrivateKey(String consumerKey) {
        try {
            byte[] key = Base64.getDecoder().decode(consumerKey)
            PKCS8EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(key)
            KeyFactory kf = KeyFactory.getInstance("RSA")
            return kf.generatePrivate(keySpec)
        } catch (Exception e) {
            throw new RuntimeException(e)
        }
    }
    
    import com.google.gdata.client.authn.oauth.OAuthParameters
    import com.google.gdata.client.authn.oauth.OAuthRsaSha1Signer
    import com.google.gdata.client.authn.oauth.OAuthUtil
    import com.google.gdata.client.authn.oauth.RsaSha1PrivateKeyHelper
    
    OAuthRsaSha1Signer rsaSigner = new OAuthRsaSha1Signer()
    rsaSigner.setPrivateKey(getPrivateKey(CONSUMER_PRIVATE_KEY))
    
    OAuthParameters params = new OAuthParameters()
    params.setOAuthConsumerKey(CONSUMER_KEY)
    params.setOAuthNonce(OAuthUtil.getNonce())
    params.setOAuthTimestamp(OAuthUtil.getTimestamp())
    params.setOAuthSignatureMethod("RSA-SHA1")
    params.setOAuthType(OAuthParameters.OAuthType.TWO_LEGGED_OAUTH)
    params.setOAuthToken(accessToken)
    
    String paramString = params.getBaseParameters().sort().collect{it}.join('&')
    
    String baseString = [
            OAuthUtil.encode("GET"),
            OAuthUtil.encode('base-url' + 'path'),
            OAuthUtil.encode(paramString)
        ].join('&')
    
    String signature = rsaSigner.getSignature(baseString, params);
    
    params.addCustomBaseParameter("oauth_signature", signature);
    
    //Now calling using HTTPBuilder with signed data
    def http = new HTTPBuilder('base-url')
    
    http.request(Method.GET, ContentType.JSON) { req ->
            uri.path = 'path'
            uri.query = params.getBaseParameters()
            response.success = { resp, json ->
                println json
            }
            response.failure = { resp, json -> print json }
        }
    }