1

I am trying to get access token from box.com. For this I created a custom app that uses OAuth and JWT. I have also created a self signed certificate in salesforce. This self signed certificate is used in getting the assertion which is later used as request body (to box.com). However, I am getting a "400 Bad Request" status message. The code to get access token is shown below:

Map<String, String> extraClaims= new Map<String,String>();
        extraClaims.put('box_sub_type', 'enterprise');
        Auth.Jwt jwt = new Auth.Jwt();
        jwt.setIss(oAuthClientId); //oAuthClientId and clientSecret is given by box.com custom app
        jwt.setAud('https://api.box.com/oauth2/token');
        jwt.setSub(enterpriseID); //enterpriseId of my box.com account
        jwt.setAdditionalClaims(extraClaims);
        jwt.setValidityLength(60);

        Auth.JWS jws = new Auth.JWS(jwt, certificateName); 
        String assertion = jws.getCompactSerialization();
        Http http = new http();
        HttpRequest request = new HttpRequest();
        request.setEndpoint(authenticationUrl);
        request.setMethod('POST');
        request.setHeader('Content-Type', 'application/x-www-form-urlencoded');
        String body = 'grant_type=urn:ietf:params:oauth:grant-type:jwt-bearer'+
                        '&client_id='+oAuthClientId+
                        '&client_secret='+oAuthClientSecret+
                        '&assertion='+assertion;
        request.setBody(body);

        HttpResponse response = http.send(request);
        //response returns 400 Bad Request

In box.com there is an option to add and manage Public Keys. I am not sure on what i should insert here. From salesforce, I just downloaded the self signed certificate(crt file) and copied its content and pasted in box.com Add and Manage public keys section. However, It did not work. The content start as

-----BEGIN CERTIFICATE-----
MIIG+DCCBOCgAwIBA...
.......
-----END CERTIFICATE-----

I also tried to export the certificate as jks file, put a password while exporting. I then used the jks file to PEM file. I then copied the content of PEM file in box.com as well(I am not sure, If i followed the correct steps). However that also didnot work.

So, plz help me on getting the access taken from box.com? What did i miss. Any steps on getting the public key from self signed certificate.

5
  • Did u try this one to get private key: salesforce.stackexchange.com/questions/275775/…
    – Ayub
    Commented Apr 29, 2020 at 8:03
  • @Ayub Yes, I tried and i was able get a public key but that did not work. So, I am thinking the approach i followed were wrong. So, looking for soln.
    – lambad
    Commented Apr 29, 2020 at 8:08
  • But you need to use the private key and sign using this key while sending request from apex. Public key you have alrready uploaded in box.
    – Ayub
    Commented Apr 29, 2020 at 8:21
  • I've prototyped successfully. I'm adding as answer shortly.
    – Ayub
    Commented Apr 29, 2020 at 9:51
  • @lambad did ever work this for you? I am also facing the same issue.
    – Ajay
    Commented Mar 3, 2022 at 18:15

1 Answer 1

1

I did give a try and successfully able to get the access token. During this, I got bunch of things that I fixed as below:

  • App in Box should be admin authorized

  • Signing the request using JWS does not work for some reason. I need to build the payload by myself without using jwt

  • Using Datetime.now for 'exp' does not work - may be because of gmt conversion. So I need to use JWT to generate this.

Here is the code, replace the data accordigly:

public class BoxAuthenticationUsingJWT{
    //BoxAuthenticationUsingJWT.connectWithBox();
    public static void connectWithBox(){
        String clientId = '6651wi15q83qvav53jznsgdswuxxxxxx';
        String clientSecret = '1DTfOrIQF0M2YxSKmKmk9jY5abxxxxxx';
        String endpoint = 'https://api.box.com/oauth2/token';
        String iss = clientId;
        String aud = endpoint;
        String sub = '334200xxx'; //Enterprise Id

        //Long exp = DateTime.now().addhours(-8).addSeconds(30).getTime(); // Datetime.now is returning fiff time than what need JWT token 
        Auth.Jwt jwt = new Auth.Jwt();
        jwt.setValidityLength(60);
        String jwtRequestd = jwt.toJSONString();
        Map<String, Object> m = (Map<String, Object>)JSON.deserializeUntyped(jwtRequestd);
        Long exp = (Long)m.get('exp');
        system.debug(jwtRequestd);

        system.debug(jwt.getValidityLength());
        // Start constructing the header and claims
        String jwtHeader = '{"typ":"JWT","alg":"RS256"}';

        String jwtClaims = '{"iss":"' + iss +'","jti":"'+UserInfo.getUserID()+'","box_sub_type":"enterprise","sub":"' + sub + '","aud":"' + aud + '","exp":' + exp + '}';
        system.debug(jwtClaims);
        String jwtRequest = System.encodingUtil.base64Encode(Blob.valueOf(jwtHeader)).replace('+', '-').replace('/', '_') + '.' + System.encodingUtil.base64Encode(Blob.valueOf(jwtClaims)).replace('+', '-').replace('/', '_');

        String signature = System.encodingUtil.base64Encode(Crypto.signWithCertificate('RSA-SHA256', Blob.valueOf(jwtRequest), 'jwt')).replace('+', '-').replace('/', '_');
        String signedJwtRequest = jwtRequest + '.' + signature;

        // The JWT is fully constructed, now it's time to make the call to get the access token.

        String payload = 'grant_type=' + System.EncodingUtil.urlEncode('urn:ietf:params:oauth:grant-type:jwt-bearer', 'UTF-8');
        payload += '&client_id='+clientId+'&client_secret='+clientSecret;
        payload += '&assertion=' + signedJwtRequest;


        Http httpObj = new Http();
        HttpRequest req = new HttpRequest();
        HttpResponse res;


        req.setEndpoint(endpoint);
        req.setMethod('POST');
        req.setHeader('Content-Type', 'application/x-www-form-urlencoded');
        req.setBody(payload);

        res = httpObj.send(req);

        System.debug(res.getBody());

    }

}

For newbie, I've added this piece with more details and steps here: https://sfdcian.com/box-jwt-authentication-in-salesforce-using-apex/

2
  • Not going to downvote...but the asker's code is better - it correctly uses out of the box functionality rather then reinventing the wheel. Signing the JWT does work out of the box. The asker's issue has to do with correctly configuring Box side.
    – identigral
    Commented Apr 29, 2020 at 20:19
  • 1
    Thanks for not downvoting. But it's not like I just picked this approach by choice - I did spent hours to analyze - in this case something is happening - JWS signed is not working while Crypto.sign is working with BOX. May be I need to do some more research or may be diff thread of discussion!
    – Ayub
    Commented Apr 29, 2020 at 20:47

Not the answer you're looking for? Browse other questions tagged .