Authenticate in Xero from Salesforce - authentication

I'm new to Oauth and I stack on getting oauth_access_token to work with Xero. Web Service authentication doesn't work for me.
Xero returns the following error message "oauth_problem=signature_invalid&oauth_problem_advice=Failed to validate signature".
The generated signature is incorrect, but what is right way to generate it?
Here is APEX code which generates Endpoint. What is wrong?
Http h = new Http();
String consumer_key='XXX';
Long tmp=(System.now().getTime()/1000);
Blob isItCorrect = Crypto.generateMac('HMacSHA1', Blob.valueOf('https://api.xero.com/api.xro/2.0'), Blob.valueOf(consumer_key));
String signature= EncodingUtil.urlEncode(EncodingUtil.base64Encode(isItCorrect), 'UTF-8');
// Try to get access token
HttpRequest req = new HttpRequest();
req.setEndpoint('https://api.xero.com/oauth/RequestToken?oauth_consumer_key='+consumer_key+
'&oauth_signature_method=RSA-SHA1'+
'&oauth_signature='+signature+
'&oauth_timestamp='+tmp+ '&oauth_nonce='+tmp+'&oauth_version=1.0&scope=https%3A%2F%2Fapi.xero.com%2Fapi.xro%2F2.0');
req.setMethod('GET');
// Send the request, and return a response
HttpResponse res = h.send(req);
System.debug('~~~ '+res.getBody());
It generates following Endpoint:
Endpoint=https://api.xero.com/oauth/RequestToken?oauth_consumer_key=ICSP7Y5K2TG7RIIC6Y7R7KLC1AHWYC&oauth_signature_method=RSA-SHA1&oauth_signature=gWP02y2EIatw4xilTvd5Iq3e0%2Fw%3D&oauth_timestamp=1372123781&oauth_nonce=1372123781&oauth_version=1.0&scope=https%3A%2F%2Fapi.xero.com%2Fapi.xro%2F2.0

Just as an aside: I've never worked with salesforce so I'm not sure if there's a better
way to leverage existing oauth work on the platform, it's very rare
now to have to write all the oauth signature stuff yourself and it's
easy to make a mistake but here goes]
I think your signature base string is incorrect.
As far as I can tell you're just performing HMAC-SHA1 over https://api.xero.com/api.xro/2.0
if you read the OAuth Spec here: http://oauth.net/core/1.0/#anchor14 you need to construct the following base string (based on the request above)
GET&https%3A%2F%2Fapi.xero.com%2Foauth%2Frequesttoken&oauth_consumer_key%3DCONSUMER_KEY%26oauth_nonce (etc etc, just append all your query parameters apart from oauth_consumer as url encoded key=value pairs, in alphabetical order)
and then you need to create the hash with the key CONSUMER_KEY&CONSUMER_SECRET (both CONSUMER_KEY and CONSUMER_SECRET should be parameter encoded as per the OAuth Spec)
That should give you a valid signature..
Edit: I found this library which might be of help: https://code.google.com/p/sfdc-oauth-playground/

Related

Problems building signature for Google Authentication

I'm trying to make an authorized Google API call using a JWT.
But when I send the request I get this error:
{
"error": "invalid_grant",
"error_description": "Invalid JWT Signature."
}
I'm following this manual https://developers.google.com/identity/protocols/oauth2/service-account#httprest
The JWT consists of three parts: header, claim set and signature. The header is always the same for authentication and I set every neccessary parameter in the claim set.
Now for the signature it says that you have to encode the header and claim set using Base64url and concatenate them together with a period: {Base64url encoded header}.{Base64url encoded claim set}
Then you sign this using SHA256withRSA and encode the result as well using Base64url again. After that you get the JWT also by concatenating them together: {Base64url encoded header}.{Base64url encoded claim set}.{Base64url encoded signature}
But when I send the request using the JWT I always get the said error. I also tried encoding it first and then signing it but I get the same result.
Am I creating the signature correctly?
If so does anyone have an idea what else the problem could be?
So with the information you provided, your signature is invalid.
You can use https://jwt.io debugger to validate your token and compare the Base64url results.

Authorization Header and Base64 Encoding in VimeoAPI

I am new to the world of APIs and Auth, and would certainly appreciate any help. I am attempting to authorize and receive a token through the Vimeo API (OAuth2). My question is how to properly set the value of the Authorization Header. (Table below from: https://developer.vimeo.com/api/authentication)
Header
Set value to
Authorization
basic base64_encode(x:y), where x is the client identifier and y is the client secret
In this table is base64_encode plain text that I need to write, or does this denote a function I need to use in my language that converts x:y into Base64? M Language, in my case.
Also, are my clientID and clientSecret ready to put into the header "as-is" or do they themselves need to be converted into Base64 before being used as auth for the token endpoint?
The gist of my confusion is how exactly I should write the authorization header, because I keep getting the error "[invalid_client] A valid client ID must be provided along with any request made to Vimeo's API" when trying to POST to the token endpoint.
Thank you for any help!
The idea that you should use some sort of function to encode your clientID and secret is correct.
If you are using javascript the code might looks something like this
const clientId = 'client_id';
const clientSecret = 'client_secret';
// btoa() is a javascript built-in that base64 encodes a string
const authorizationValue = 'Basic ' + btoa( clientId + ':' + clientSecret );
You can read more about btoa() on Mozilla's documentation website.
https://developer.mozilla.org/en-US/docs/Web/API/btoa

How does a Servant client handle received cookies?

I want to use a Servant client to first call a login endpoint to obtain a session cookie and then make a request against an endpoint that requires cookie authentication.
The API is (simlified)
import qualified Servant as SV
import qualified Servant.Auth.Server as AS
import qualified Servant.Client as SC
-- Authentication and X-CSRF cookies
type CookieHeader = ( SV.Headers '[SV.Header "Set-Cookie" AS.SetCookie
, SV.Header "Set-Cookie" AS.SetCookie]
SV.NoContent )
type LoginEndpoint = "login" :> SV.ReqBody '[SV.JSON] Login :> SV.Verb 'SV.POST 204 '[SV.JSON] CookieHeader
type ProtectedEndpoint = "protected" :> SV.Get '[SV.JSON]
-- The overall API
type Api = LoginEndpoint :<|> (AS.Auth '[AS.Cookie, AS.JWT] User :> ProtectedEndpoint)
apiProxy :: Proxy Api
apiProxy = Proxy
I define the client as follows:
loginClient :: Api.Login -> SC.ClientM Api.CookieHeader
protectedClient :: AC.Token -> SC.ClientM Text :<|> SC.ClientM SV.NoContent
loginClient :<|> protectedClient = SC.client Api.apiProxy
How does the client handle the authentication cookie? I can think of two ways. When executing a request in the ClientM monad, like
do
result <- SC.runClientM (loginClient (Login "user" "password")) clientEnv
[..]
where Login is the login request body and clientEnv of type Servant.Client.ClientEnv, the cookie could part of the result, it could be updated in the cookieJar TVar inside clientEnv, or both. I would assume the TVar to be updated, so that a subsequent request with the same clientEnv would send the received cookies along. However, my attempt to read the TVar and inspect its contents using Network.HTTP.Client.destroyCookieJar revealed an empty array. Is this intended? I couldn't find anything in the documentation.
Thus, to make an authenticated call, I would need to extract the cookie from the header in the result (how?), update the TVar, create a new clientEnv that references this TVar, and make the authenticated call using this new environment. Is this indeed the suggested procedure? I'm asking because I suppose the use case is so standard that there should be a more streamlined solution. Is there? Am I missing something?
After some experimentation, I figured out that the Servant client indeed does maintain cookies in the cookieJar that is part of the clientEnv. To be more precise, clientEnv contains the field cookieJar, which is of type Maybe (TVar CookieJar). It is the TVar the client updates according to the Set-Cookie instructions of subsequent requests. It is up to the developer to create and initialize that TVar before making the first request; otherwise, the Servant client will discard cookies between requests.
In addition, it is possible to retrieve cookies in the same way as the request body. To this end, the cookies to be retrieved must be defined as part of the API type, like in the example of my original question:
type LoginEndpoint = "login" :> SV.ReqBody '[SV.JSON] Login :> SV.Verb 'SV.POST 204 '[SV.JSON] CookieHeader
Disassembling the returned was a little tricky at first, because I needed to figure out the final type that results from Servant's type-level machinery. Ultimately, I did the following:
SV.Headers resp h <- tryRequest clientEnv (loginClient (Api.Login "user" "pwd"))
let headers = SV.getHeaders h
where tryRequest is a helper to execute runClientM and extract the Right part. The pattern match resp contains the return value (here NoContent), while h is is an HList of the different headers. It can be converted into a regular list of Network.HTTP.Types.Header using Servant's getHeaders function.
It would then be possible to change or generate new headers and submit them with a new request by adding a new header to the cookieJar TVar (see the cookie-manipulating functions in Network.HTTP.Client).

Oauth HMac Signature Generator for HERE map

I want to use HERE map api's in my project.
As per their document, I need to use generate the signature first using Oauth 1.0 HMAC-256.
I am following below AAA sdk by HERE map :
https://github.com/heremaps/here-aaa-java-sdk/blob/master/here-oauth-client/src/main/java/com/here/account/auth/SignatureCalculator.java
SignatureCalculator signatureCalculator = new SignatureCalculator(CONSUMER_KEY,CONSUMER_SECRET);
String signature = signatureCalculator.calculateSignature("GET","https://account.api.here.com/oauth2/token",
(System.currentTimeMillis()/1000L),RandomStringUtils.randomAlphanumeric(12),
SignatureMethod.HMACSHA256,null,null);
But I am not able to generate the token using this signature. I got below error :
{"errorId":"ERROR-25b51579-57d0-4873-b82e-466519b8b6a4","httpStatus":401,"errorCode":401202,"message":"Invalid Client Authorization header, expecting signed request format.","error":"invalid_request","error_description":"errorCode: '401202'. Invalid Client Authorization header, expecting signed request format."}
When i try to do same through postman then everything works perfect.
There is difference between both the signature (generated though postman which works / generated though java code)
pw7d5IVYFsYH0zNPNZJU7x3T6LUtDj8zzYw2hsUUlM= . -- Not Working. Generated using AAA here library.
tBWyomfDmgihh2kC%2B14qYurOoPF%2FYkpg7QyGpwgZ77A%3D" -- Working Signature , generated through postman
I think I am doing wrong signature generation.
You have to done signature encoding by URLEncoding in UTF-8 format.
URLEncoder.encode("Signature_Generated_from_HMAC-SHA256","UTF-8")

How to Emulate a POST Request from PostMan - For a series of Integration Tests in a WebAPI Development

I am using PostMan as part of the testing regime for a WebAPI service development that I am working on. In order for external parties to gain access to our WebAPI service they first need to obtain an access token.
The POST request returns some JSON containing the required access token:
{
"access_token": "anencryptedaccesstoken",
"scope": "am_application_scope default",
"token_type": "Bearer",
"expires_in": 3218
}
I am putting together a series of integration tests which need to emulate the POST calls from POSTMAN. I am currently using System.Net.WebClient to achieve this. I am not sure what I need to do in order to achieve my goal. Here is a function that I am using to try and obtain the access token:
Public Shared Function GetAccessToken(ByVal endpoint As String, wc As WebClient) As String
Dim result As String = ""
Dim data As Byte() = Nothing
'Header information
wc.Headers.Add("Authorization", "Basic <alongencryptedstring>")
wc.Headers.Add("Content_Type", "application/x-www-form-urlencoded")
result = wc.UploadString(endpoint, "POST", "")
Return result
End Function
The 'Body' tab in PostMan contains the following entries:
grant_type - client_credentials
Content_Type - application%2Fx-www-form-urlencoded
In this instance as far as I am aware there is no 'data' element to the PostMan request hence the empty string in my use of UploadString. The function returns the following error:
"The remote server returned an error: (415) Unsupported Media Type."
I am new to web app development so please bear with me, I am finding it difficult to phrase what I think are meaningful question in the context of this post, I hope however that I have ssupplied sufficient information to convey the gist of my problem and for someone to be able to respond appropriately.
Kind Regards
Paul.