Saurabh Gaur Saurabh Gaur - 2 months ago 7
Groovy Question

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 :-


  • Request to get request token

  • Request to get authorized URL

  • Request to access token

  • Request to get actual data using
    HTTPBuilder



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.

Answer

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 }
    }
}
Comments