google-apigoogle-oauth

Google API Invalid JWT Signature in SFCC


I'm trying to get an access token for the google api and have the following error: {"error":"invalid_grant","error_description":"Invalid JWT Signature."}

Things that I had to do to get it signed:

I'm coding it for the SFCC (demandware) platform. The code looks like that:

var Signature = require("dw/crypto/Signature");  
var Encoding = require('dw/crypto/Encoding'); 
var Bytes = require('dw/util/Bytes');

function execute( args : PipelineDictionary ) : Number
{
    var service = GoogleAPIHttpFormService.getService();
    var jwt = createJWT();
    service.addHeader("Content-Type", "application/x-www-form-urlencoded");
    service.setRequestMethod("POST");
    service.addParam('grant_type', 'urn:ietf:params:oauth:grant-type:jwt-bearer');
    service.addParam('assertion', jwt);
    
    var result = service.call();
    

    return PIPELET_NEXT;
}

function createJWT() {
    var json = {
          "private_key": "...private key code...",
      "client_email": "*****@*******.iam.gserviceaccount.com"
    };
    
    var date = new Date();
    var iat = (date.getTime() / 1000);
    var exp = iat + 3600; //the current time + 1 hour in seconds
    
    var jwtHeader = {"alg":"RS256","typ":"JWT"};
    var jwtClaimSet = {
            "iss": json.client_email,
        "scope": "https://www.googleapis.com/auth/analytics.readonly",
        "aud": "https://oauth2.googleapis.com/token",
        "exp": exp,
        "iat": iat
    };
    
    var signature = new Signature();
    //Encode the Header as Base64  
    let headerBase64 = Encoding.toBase64(new Bytes(JSON.stringify(jwtHeader)));  

    //Encode the Payload as Base64  
    let jwtClaimSetBase64 = Encoding.toBase64(new Bytes(JSON.stringify(jwtClaimSet)));  

    //Create the content of the JWT Signature  
    var signatureStr = headerBase64 + "." + jwtClaimSetBase64;  

    var token = signature.sign(signatureStr, json.private_key, 'SHA256withRSA');
 
    var signatureUrlEncoded = token.replace(/\+/g, '-').replace(/\//g, '_').replace(/\=+$/m, ''); 
    
        let jwt = headerBase64 + "." + jwtClaimSetBase64 + "." + signatureUrlEncoded;  

    return jwt;  
}

I will be thankful for any ideas how to fix it.

I tried whitout the replace of the symbols in the token and if I use this part: token.replace(/\//g, '_').replace(/\=+$/m, '') I get this error: { "error": "invalid_grant", "error_description": "java.security.SignatureException: Invalid signature for token: ... }


Solution

  • I found the issue, 2 changes had to be made:

    1. I added this line var keyStr = json.private_key.replace("-----BEGIN PRIVATE KEY-----", "").replace(/\n/gi, '').replace("-----END PRIVATE KEY-----", "");

    2. Then I replaced the signature concatenation line with this one:

      var signatureStr = Encoding.toBase64(new Bytes(headerBase64 + "." + jwtClaimSetBase64));

    And now it works