[SalesForce] Apex REST API call Error – HttpResponse[Status=Unauthorized, StatusCode=401]”|

I build an apex rest API that is returning base64 to client. API works fine with PostMan/soapUI. When i try to invoke same Apex REST API from my another developer Account I am getting HttpResponse[Status=Unauthorized, StatusCode=401]"|. Initially I was getting Return code 302 and in debug log i saw it was redirecting to domain URL and then i changed End point to domain URL so return code 302 does not appear but now keep getting 401. I checked Salesforce documentation but unable to find anything specific to 401 error code.

Any idea if i am doing something wrong in my code ?

    HttpRequest tokenRequest = new HttpRequest();
    tokenRequest.setMethod('POST');
    tokenRequest.setEndpoint('https://test.salesforce.com/services/oauth2/token');
    String CLIENT_ID = ‘xxxxxxxxxxxxxxxx’;
    String CLIENT_SECRET = ‘xxxxxxxxxxxxxxxx’;
    String USERNAME = ‘xxxxxxxxxxxxxxxx’
    String PASSWORD = ‘xxxxxxxxxxxxxxxx’
    
    tokenRequest.setBody('grant_type=password' + '&client_id='+CLIENT_ID + 
                '&client_secret='+CLIENT_SECRET + '&username='+USERNAME + '&password='+PASSWORD);
    
    Http http1 = new Http();
    HTTPResponse responseToken = http1.send(tokenRequest);
    
    string tran = ‘xxxxxxx03zsTNQAY'; // for test purpose only
    string endpoint = 'https://xxxxx--xxxxxxx.my.salesforce.com/services/apexrest/v1/getdetails?transactionid='+tran;
    HttpRequest request = new HttpRequest();
    request.setHeader('Content-Type', 'application/json');
    request.setHeader('Authorization', 'Bearer ' +responseToken.getBody()); 
    request.setEndpoint(endpoint);
    request.setMethod('GET');
    Http http = new Http();
    HTTPresponse res= http.send(request);
/* Added to handle 302 return code 
        while (res.getStatusCode() == 302) {                
            request.setEndpoint(res.getHeader('Location'));
            res = new Http().send(request);
            system.debug('Calling Again !!! ');
        }
   */
        system.debug(' Location header from respose :'+ res.getHeader('Location'));
        system.debug(' Response :'+ res);
        
    } catch(exception e){
        System.debug('ERROR: '+ e.getMessage());  
    }

Best Answer

You are not passing only the access_token value, but the whole response body in the Authorization header of the second callout, here:

request.setHeader('Authorization', 'Bearer ' +responseToken.getBody()); 

responseToken.getBody() returns a JSON string like this:

{
  "access_token": "00DR00000008oBT!AQwAQCPqzc_HBE59c80QmEJD4rQKRRc1GRLvYZEq...",
  "instance_url": "https://MyDomainName.my.salesforce.com",
  "id": "https://login.salesforce.com/id/00DR00000008oBTMAY/005R0000000IUUMIA4",
  "token_type": "Bearer",
  "issued_at": "1513887500425",
  "signature": "3PiFUIioqKkHpHxUiCCDzpvSiM2F6//w2/CslNTuf+o="
}

What you must provide in the Authorization header is only the value of access_token property.

You can use JSON.deserializeUntyped to parse that JSON string into a Map<String, Object> from which getting the access_token.

Map<String, Object> authMap = (Map<String, Object>) JSON.deserializeUntyped(responseToken.getBody());
...
request.setHeader('Authorization', 'Bearer ' + authMap.get('access_token'));

By the way, instead of hardcoding the whole endpoint, you can get the instance domain from that JSON:

String endpoint = authMap.get('instance_url') + '/services/apexrest/v1/getdetails?transactionid='+tran;