I am stuck with the oauth1 migration to oauth2. I don't want to ask my users to grant contact access again, so I prefer to do migration myself but I am getting hard time figuring out why it doesn't work.
I'm getting this error from Google server:
DEBUG - << " "error" : "invalid_request",[\n]"
DEBUG - << " "error_description" : "Invalid authorization header."[\n]"
here is my code, I did almost the same thing when consuming google api, but for migration it is not working.
GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
oauthParameters.setOAuthConsumerKey(getConsumerKey());
oauthParameters.setOAuthConsumerSecret(getConsumerSecret());
oauthParameters.setOAuthToken(token);
ClassPathResource cpr = new ClassPathResource("mykey.pk8");
File file = cpr.getFile();
PrivateKey privKey = getPrivateKey(file);
OAuthRsaSha1Signer signer = new OAuthRsaSha1Signer(privKey);
GoogleOAuthHelper oauthHelper = new GoogleOAuthHelper(signer);
String requestUrl = "https://www.googleapis.com/oauth2/v3/token";
String header = oauthHelper.getAuthorizationHeader(requestUrl, "POST", oauthParameters);
String payload = "grant_type=urn:ietf:params:oauth:grant-type:migration:oauth1&client_id="+com.app.framework.utils.OAuthHelper.OAUTH2_CLIENT_ID+"&client_secret="+com.app.framework.utils.OAuthHelper.OAUTH2_CLIENT_SECRET;
HttpClient httpClient = new DefaultHttpClient();
HttpPost httpPost = new HttpPost(requestUrl);
httpPost.addHeader("Authorization", header);
httpPost.addHeader("Content-Type", "application/x-www-form-urlencoded");
httpPost.setEntity(new ByteArrayEntity(payload.getBytes()));
String response = httpClient.execute(httpPost, new BasicResponseHandler());
After some emails exchange with #Miguel, he successfully points me to the solution.
The issue:
The OAuthHelper that GoogleOAuthHelper extends uses TwoLeggedOAuthHelper to build the base_string. The TwoLeggedOAuthHelper was not injecting 3 required parameters: client_id, client_secret and the grant_type in the base_string.
The solution:
I had to create my own classes: copy/paste code from OAuthHelper to MyOAuthHelper and from TwoLeggedOAuthHelper to MyTwoLeggedOAuthHelper. You need some declarations from GoogleOAuthHelper to resolve compilation errors.
MyOAuthHelper will call MyTwoLeggedOAuthHelper instead of TwoLeggedOAuthHelper.
Now in MyTwoLeggedOAuthHelper, around line 79, locate the
String baseString = OAuthUtil.getSignatureBaseString(baseUrl,
httpMethod,…
and add the following:
String clientId = "client_id%3DXXX123456789XXX.apps.googleusercontent.com%26";
String clientSecret = "client_secret%3DXXXX_XXXX_XX_XX%26";
String grantType = "grant_type%3Durn%253Aietf%253Aparams%253Aoauth%253Agrant-type%253Amigration%253Aoauth1%26";
baseString = StringUtils.replace(baseString, "token&", "token&" + clientId + clientSecret + grantType);
Some notes:
client_id and client_secret must be the one your backend used to get the OAUTH1 access token. Be careful with that especially if you have multiple "Client ID for web application" defined in your Google console.
Notice the crazy grant_type encoded twice.
The Google classes used are located in maven: com/google/gdata/core/1.47.1/core-1.47.1.jar
Kudos to #Miguel
Your request is failing signature verification. Please check out the responses to this related question for detailed instructions on how to construct the base string for your request and sign it.
Hope that helps!
Related
I have just setup a new project and added the following scopes for "Web application".
email, and send mail
I have also enabled GMailAPI from library
After this I have created credentials. Then edit > redirect_uri
I am not sure what this uri should be but I have tried almost everything here
Gmail error message state "If you are a developer of this app see error details"
the url mentioned here : http:\x.x.x.x:1234\authorize\
with and without ending slash
P.S: when I type above uri in my browser, I get to a break point in my application
my home page url
http:\localhost\default.aspx
my calling page uri
http:\localhost\member\create.aspx
None of these work and I still get redirect_uri_mismatch Access Blocked error
My code is still running on my local machine and not available in google cloud.
vb.net code
Dim credential As UserCredential = Await GoogleWebAuthorizationBroker.AuthorizeAsync(New ClientSecrets With {
.ClientId = "xxx",
.ClientSecret = "xxx"
},
{"https://www.googleapis.com/auth/gmail.send"},
"user",
CancellationToken.None)
Update
I got to know that AuthorizeAsync is for installed applications and not for web apps, here is my updated code...which is not sending me back a token.
Public Function DoOauthAndSendEmail(subject As String, body As String, recipients As String) As Task
Dim fromEmail As String = ConfigurationSettings.AppSettings("ContactEmail")
Dim MailMessage As MailMessage = New MailMessage(fromEmail, recipients, subject, body)
'Specify whether the body Is HTML
MailMessage.IsBodyHtml = True
'Convert to MimeMessage
Dim Message As MimeMessage = MimeMessage.CreateFromMailMessage(MailMessage)
Dim rawMessage As String = Message.ToString()
Dim flow As IAuthorizationCodeFlow = New GoogleAuthorizationCodeFlow(New GoogleAuthorizationCodeFlow.Initializer With {
.ClientSecrets = New ClientSecrets With {
.ClientId = "CLIENT_ID",
.ClientSecret = "CLIENT_SECRET"
},
.Scopes = {GmailService.Scope.GmailSend}
})
Dim token As Responses.TokenResponse = New Responses.TokenResponse()
If flow IsNot Nothing And token IsNot Nothing Then
Dim credential As UserCredential = New UserCredential(flow, "user", token)
Dim success As Boolean = credential.RefreshTokenAsync(CancellationToken.None).Result
Dim gmail As GmailService = New GmailService(New Google.Apis.Services.BaseClientService.Initializer() With {
.ApplicationName = "APP_NAME",
.HttpClientInitializer = credential
})
gmail.Users.Messages.Send(New Message With {
.Raw = Base64UrlEncode(rawMessage)
}, "me").Execute()
End If
End Function
You are looking in the wrong place for the redirect uri's it is found under credentials then edit your web app client
Google OAuth2: How the fix redirect_uri_mismatch error. Part 2 server sided web applications
update Installed app
The code you are using GoogleWebAuthorizationBroker.AuthorizeAsync is used for authorizing an installed application. In this instance you need to make sure that you have created an installed application credentials on google cloud console.
How to create installed application credetilas.
You should not be seeing a redirect uri error if you have created the correct credentials type for you to match the code you are using.
Using Postman I'm successfully able to query and create tailored audiences using the Twitter API, using Postman's OAuth 1.0 Authorization. However when trying to do the same with RestSharp I get an Unauthorized error.
"UNAUTHORIZED_ACCESS" - "This request is not properly authenticated".
My GET request authenticates fine, but the POST request fails.
_twitterRestClient = new RestClient("https://ads-api.twitter.com/1")
{
Authenticator = OAuth1Authenticator.ForProtectedResource(ConsumerKey, ConsumerSecret, AccessToken, AccessSecret)
};
var restRequest1 = new RestRequest(string.Format("/accounts/{0}/tailored_audiences", TwitterAccountId), Method.GET);
//this works and gives me a list of my tailored audiences
var response1 = _twitterRestClient.Execute(restRequest1);
var restRequest2 = new RestRequest(string.Format("/accounts/{0}/tailored_audiences?name=SampleAudience2&list_type=EMAIL", TwitterAccountId), Method.POST);
// this results in an "Unauthorized" status code , and the message {\"code\":\"UNAUTHORIZED_ACCESS\",\"message\":\"This request is not properly authenticated\"}
var response2 = _twitterRestClient.Execute(restRequest2);
Turns out this is due to a quirk in RestSharp OAuth1 implementation. I think its related to this issue - https://www.bountysource.com/issues/30416961-oauth1-not-specifing-parameter-type . Part of creating an OAuth1 signature involves gathering all the parameters in the request and other details and then hashing it all. It looks like when the HTTP Method is a POST, then RestSharp is not expecting parameters in the querystring (which makes sense), its expecting them in the post body. Anyhow if you add parameters explicitly then they are picked up and the OAuth1 signing works. (Turns out the twitter API works if these params are in the post body, so I didn't need to explicitly add them to the query string). Updated code that now works:
_twitterRestClient = new RestClient("https://ads-api.twitter.com/1")
{
Authenticator = OAuth1Authenticator.ForProtectedResource(ConsumerKey, ConsumerSecret, AccessToken, AccessSecret)
};
var restRequest1 = new RestRequest(string.Format("/accounts/{0}/tailored_audiences", TwitterAccountId), Method.GET);
var response1 = _twitterRestClient.Execute(restRequest1);
var restRequest2 = new RestRequest(string.Format("/accounts/{0}/tailored_audiences", TwitterAccountId), Method.POST);
restRequest2.AddParameter("name", "SampleAudience2");
restRequest2.AddParameter("list_type", "EMAIL");
var response2 = _twitterRestClient.Execute(restRequest2);
I am writing some code to try to get a token to use from Google in OAuth2. This is for a service account, so the instructions are here:
https://developers.google.com/identity/protocols/OAuth2ServiceAccount
I keep getting this error when I post the JWT to Google:
{ "error": "invalid_grant", "error_description": "Invalid JWT Signature." }
Here is the code:
try{
var nowInSeconds : Number = (Date.now() / 1000);
nowInSeconds = Math.round(nowInSeconds);
var fiftyNineMinutesFromNowInSeconds : Number = nowInSeconds + (59 * 60);
var claimSet : Object = {};
claimSet.iss = "{{RemovedForPrivacy}}";
claimSet.scope = "https://www.googleapis.com/auth/plus.business.manage";
claimSet.aud = "https://www.googleapis.com/oauth2/v4/token";
claimSet.iat = nowInSeconds;
claimSet.exp = fiftyNineMinutesFromNowInSeconds;
var header : Object = {};
header.alg = "RS256";
header.typ = "JWT";
/* Stringify These */
var claimSetString = JSON.stringify(claimSet);
var headerString = JSON.stringify(header);
/* Base64 Encode These */
var claimSetBaseSixtyFour = StringUtils.encodeBase64(claimSetString);
var headerBaseSixtyFour = StringUtils.encodeBase64(headerString);
var privateKey = "{{RemovedForPrivacy}}";
/* Create the signature */
var signature : Signature = Signature();
signature = signature.sign(headerBaseSixtyFour + "." + claimSetBaseSixtyFour, privateKey , "SHA256withRSA");
/* Concatenate the whole JWT */
var JWT = headerBaseSixtyFour + "." + claimSetBaseSixtyFour + "." + signature;
/* Set Grant Type */
var grantType = "urn:ietf:params:oauth:grant-type:jwt-bearer"
/* Create and encode the body of the token post request */
var assertions : String = "grant_type=" + dw.crypto.Encoding.toURI(grantType) + "&assertion=" + dw.crypto.Encoding.toURI(JWT);
/* Connect to Google And Ask for Token */
/* TODO Upload Certs? */
var httpClient : HTTPClient = new HTTPClient();
httpClient.setRequestHeader("content-type", "application/x-www-form-urlencoded; charset=utf-8");
httpClient.timeout = 30000;
httpClient.open('POST', "https://www.googleapis.com/oauth2/v4/token");
httpClient.send(assertions);
if (httpClient.statusCode == 200) {
//nothing
} else {
pdict.errorMessage = httpClient.errorText;
}
}
catch(e){
Logger.error("The error with the OAuth Token Generator is --> " + e);
}
Does anyone know why the JWT is failing?
Thanks so much!
Brad
The problem might be related to the fact that your StringUtils.encodeBase64() method is likely to perform a standard base64 encoding.
According to the JWT spec, however, it's not the standard base64 encoding that needs to be used, but the the URL- and filename-safe Base64 encoding, with the = padding characters omitted.
If you don't have a utility method handy for base64URL encoding, you can verify by
replacing all + with -;
replacing all / with _;
removing all =
in your base64-encoded strings.
Also, is your signature also base64-encoded? It needs to be, following the same rules as described above.
I had the same problem before and this is what was wrong:
wrong application name (project ID)
wrong service account ID (email)
The another reason for this error could be "Your service account is not activated", With gsutil installed from the Cloud SDK, you should authenticate with service account credentials.
1- Use an existing service account or create a new one, and download the associated private key.
2- Use gcloud auth activate-service-account to authenticate with the service account:
gcloud auth activate-service-account --key-file [KEY_FILE]
Where [KEY_FILE] is the name of the file that contains your service account credentials.
Link for more detail: Activate service account
This could also happen if a developer mistakenly copies, edits, and uses a service account key file for a purpose other than the one for which the file was originally intended. For example:
Developer A creates SA 1
Developer A uses gcloud iam service-accounts keys create ... to create the secret file for SA 1, encrypts it, and checks it in to source control
Developer B creates SA 2
Developer B (mistakenly) decrypts and copies the secret file from step 2, modifies some of its fields with data from SA 2, then attempts to use it in an application
The resolution in this scenario obviously is for Developer B to get rid of the copied/edited file and create a new secret file with gcloud like Developer A did in step 2.
I had this same error occur when using a service account. I couldn't figure out what was wrong so I came back to it the next day and it worked. So maybe Google Cloud takes some time to propagate every once in a while.
i am getting a 401 response from Asana with my request.
var url = "https://app.asana.com/api/1.0/users/me";
byte[] encodedByte = System.Text.ASCIIEncoding.ASCII.GetBytes(APIKey);
APIKey = Convert.ToBase64String(encodedByte);
WebRequest wrGETURL;
wrGETURL = WebRequest.Create(url);
wrGETURL.Headers.Add("Authorization: Basic " + APIKey);
string result;
using (StreamReader reader = new StreamReader(wrGETURL.GetResponse().GetResponseStream()))
{
result = reader.ReadToEnd();
}
return result;
The way HTTP basic auth works, you encode the username and password together as base64, separated by a colon. In the Asana API the key is the username and there is no password.
From the docs at https://asana.com/developers/documentation/getting-started/authentication#sts=API%20Keys :
Note: Most utilities and libraries that allow you to specify a username and password will handle proper encoding of the header for you. However, if you need to set the Authorization header manually, the header value is constructed by adding a colon (:) to the API key, then base64-encoding that string. You can read more on basic authentication if you need further details.
So, you should probably do:
byte[] encodedByte = System.Text.ASCIIEncoding.ASCII.GetBytes(APIKey + ":")
My Consumer Code is as follows,
GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
oauthParameters.setOAuthConsumerKey(CONSUMER_KEY);
oauthParameters.setOAuthConsumerSecret(CONSUMER_SECRET);
oauthParameters.setOAuthType(OAuthType.TWO_LEGGED_OAUTH);
OAuthHmacSha1Signer signer = new OAuthHmacSha1Signer();
GoogleService service = new GoogleService("oauthclient", "sampleapp");
service.setOAuthCredentials(oauthParameters, signer);
String param = "Hellow World";
String baseString = APP_SERVER + "services/OAuthTest/greet"+"?xoauth_requestor_id="+USER_NAME+"&name="+ param;
URL feedUrl = new URL(baseString);
request = service.createFeedRequest(feedUrl);
request.execute();
convertStreamToString(request.getResponseStream());
And my OAuth Server Side code is as follows,
GoogleOAuthParameters oauthParameters = new GoogleOAuthParameters();
oauthParameters.setOAuthConsumerKey(consumerKey);
oauthParameters.setOAuthConsumerSecret(secretKey);
oauthParameters.setOAuthNonce(nonce);
oauthParameters.setOAuthTimestamp(timestamp);
oauthParameters.setOAuthSignatureMethod(signatureMethod);
validateTimestampAndNonce(otimestamp, nonce);
OAuthHmacSha1Signer signer = new OAuthHmacSha1Signer();
String baseString = OAuthUtil.getSignatureBaseString(baseUrl,httpMethod, baseParameters);
String signature = signer.getSignature(baseString, oauthParameters);
return signature.equals(oauthParams.getOauthSignature())
Above signature validation fails, I have a no clue on what is wrong. Please help.
I found the reason for this. OAuth 1.0a spec section 9.1.2 defines that Signature Base String includes the request absolute URL. So if this is different at the server side than at the consumer side, then the signature verification fails. So in my case, at the consumer end I was using
127.0.0.1
for the host name of the Request URL and at the server end I was using
localhost
as the host name which is wrong. After changing the consumer to use localhost as the host name I could get the signature verification successful.