rust - Why am I getting an invalid request error using twitter api - twitter

So over at the twitter api documentation, it says to generate a Bearer token you run this command
curl --user "$API_KEY:$API_SECRET_KEY" \
--data 'grant_type=client_credentials' \
'https://api.twitter.com/oauth2/token'
I run it and the result I get is the HTML for https://api.twitter.com/ouath2/token
Then I wrote some rust code,
extern crate reqwest;
use reqwest::{ Client };
// Twitter api endpoint - https://api.twitter.com/
#[tokio::main]
async fn main() -> Result<(), reqwest::Error>{
let http_client =Client::new();
// We need to get a bearer_token before we can access anything with the twitter api
let bearer_token = http_client.post("https://api.twitter.com/oauth2/token")
.basic_auth(API_KEY, Some(API_KEY_SECRET)
.body("grant_type=client_credentials")
.send().await?
.text().await?;
println!("{}", bearer_token);
Ok(())
}
After executing the rust code above, this is what is printed,
{"errors":[{"code":170,"message":"Missing required parameter: grant_type","label":"forbidden_missing_parameter"}]}
However from the documentation the expected result is this:
{"token_type":"bearer","access_token":"AAAAAAAAAAAAAAAAAAAAAMLheAAAAAAA0%2BuSeid%2BULvsea4JtiGRiSDSJSI%3DEUifiRBkKG5E2XzMDjRfl76ZC9Ub0wnz4XsNiRVBChTYbJcE3F"}
Does anyone know why?
Here's my cargo.toml file
[package]
name = "release"
version = "0.1.0"
edition = "2018"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
reqwest = { version = "0.11", features = ["json"] }
tokio = { version = "1", features = ["full"] }
By the way, I applied for twitter developer access a few hours back, but I'm not sure on how to check if I do have access. I mean, I can make a project, and an app and access the dashboard.

https://api.twitter.com/2/oauth2/token is the wrong URI, it should be https://api.twitter.com/oauth2/token

Are there any brackets missing?
.basic_auth(API_KEY, Some(API_KEY_SECRET)
.basic_auth(API_KEY, Some(API_KEY_SECRET))

Related

How to send a speech to text request using google_speech1 in Rust?

I am trying to use google_speech1 for Rust, but the documentation provides incomplete examples, which makes it very hard for me, being both new at Rust and at using Google Speech Api, to figure out how to do send a speech to text request.
More specifically, I would like to be able to send a local audio file, indicate the source language and retrieve the transcription.
Here is the closest I could find in the official documentation(https://docs.rs/google-speech1/1.0.8+20181005/google_speech1/struct.SpeechRecognizeCall.html):
use speech1::RecognizeRequest;
// As the method needs a request, you would usually fill it with the desired information
// into the respective structure. Some of the parts shown here might not be applicable !
// Values shown here are possibly random and not representative !
let mut req = RecognizeRequest::default();
// You can configure optional parameters by calling the respective setters at will, and
// execute the final call using `doit()`.
// Values shown here are possibly random and not representative !
let result = hub.speech().recognize(req)
.doit();
UPDATE
Taking a step back, even simple examples provided on the website don't seem to run properly. Here is some sample very basic code:
pub mod speech_api_demo {
extern crate google_speech1 as speech1;
extern crate hyper;
extern crate hyper_rustls;
extern crate yup_oauth2 as oauth2;
use oauth2::{ApplicationSecret, Authenticator, DefaultAuthenticatorDelegate, MemoryStorage};
use speech1::Speech;
use speech1::{Error, Result};
use std::fs::File;
use std::io::Read;
#[derive(Deserialize, Serialize, Default)]
pub struct ConsoleApplicationSecret {
pub web: Option<ApplicationSecret>,
pub installed: Option<ApplicationSecret>,
}
pub fn speech_sample_demo() {
/*
Custom code to generate application secret
*/
let mut file =
File::open("C:\\Users\\YOURNAME\\.google-service-cli\\speech1-secret.json").unwrap();
let mut data = String::new();
file.read_to_string(&mut data).unwrap();
use serde_json as json;
let my_console_secret = json::from_str::<ConsoleApplicationSecret>(&data);
assert!(my_console_secret.is_ok());
let unwrappedConsoleSecret = my_console_secret.unwrap();
assert!(unwrappedConsoleSecret.installed.is_some() && unwrappedConsoleSecret.web.is_none());
let secret: ApplicationSecret = unwrappedConsoleSecret.installed.unwrap();
/*
Custom code to generate application secret - END
*/
// Instantiate the authenticator. It will choose a suitable authentication flow for you,
// unless you replace `None` with the desired Flow.
// Provide your own `AuthenticatorDelegate` to adjust the way it operates and get feedback about
// what's going on. You probably want to bring in your own `TokenStorage` to persist tokens and
// retrieve them from storage.
let auth = Authenticator::new(
&secret,
DefaultAuthenticatorDelegate,
hyper::Client::with_connector(hyper::net::HttpsConnector::new(
hyper_rustls::TlsClient::new(),
)),
<MemoryStorage as Default>::default(),
None,
);
let mut hub = Speech::new(
hyper::Client::with_connector(hyper::net::HttpsConnector::new(
hyper_rustls::TlsClient::new(),
)),
auth,
);
let result = hub.operations().get("name").doit();
match result {
Err(e) => match e {
// The Error enum provides details about what exactly happened.
// You can also just use its `Debug`, `Display` or `Error` traits
Error::HttpError(_)
| Error::MissingAPIKey
| Error::MissingToken(_)
| Error::Cancelled
| Error::UploadSizeLimitExceeded(_, _)
| Error::Failure(_)
| Error::BadRequest(_)
| Error::FieldClash(_)
| Error::JsonDecodeError(_, _) => (println!("{}", e)),
},
Ok(res) => println!("Success: {:?}", res),
}
}
}
Running this code (calling speech_sample_demo) gives the following error:
Token retrieval failed with error: Invalid Scope: 'no description
provided'
I also tried some very ugly code to force the scope into the request, but it did not make any difference. I am having a hard time understanding what this error means. Am I missing something in my request or is it something else getting in the way at the other end? Or maybe that api code library is just broken?
Please also note that client id and client secret provided by default don't work anymore, when I was using those it would say that account is deleted.
I then set up an OAuth 2.0 client and generated the json file which I copied over to default location and then started getting the error above. Maybe it is just me not setting Google Api account properly, but in any case would be great if someone else could try it out to see if I am the only one having those issues.
Once I get over running such a simple request, I have some more code ready to be tested that sends over an audio file, but for now it fails very early on in the process.
The error you get originates from here and means that the OAuth scope you used when generating your credentials file doesn't allow you to access the Google speech API. So the problem is not in your Rust code, but instead in the script you used to generate your OAuth access tokens.
Basically, this means that when you generated your OAuth json file, you requested access to the Google API in a general way, but you didn't say which specific APIs you meant to use. According to this document, you need to request access to the https://www.googleapis.com/auth/cloud-platform scope.
You are missing the flow param to Authenticator. This is how you get the access token. You create an Enum using FlowType.
example:
use oauth2::{ApplicationSecret, Authenticator, DefaultAuthenticatorDelegate, MemoryStorage,FlowType};
let Flo = FlowType::InstalledInteractive;
let auth = Authenticator::new(
&secret,
DefaultAuthenticatorDelegate,
hyper::Client::with_connector(hyper::net::HttpsConnector::new(
hyper_rustls::TlsClient::new(),
)),
<MemoryStorage as Default>::default(),
None,)
See here: https://docs.rs/yup-oauth2/1.0.3/yup_oauth2/enum.FlowType.html
Not exactly easy to figure out.
I made this work via service accounts by doing this
let https = hyper_rustls::HttpsConnectorBuilder::new()
.with_native_roots()
.https_only()
.enable_http1()
.build();
let service_account_key: oauth2::ServiceAccountKey = oauth2::read_service_account_key(
&"PATH_TO_SERVICE_ACCOUNT.json".to_string(),
)
.await
.unwrap();
let auth = oauth2::ServiceAccountAuthenticator::builder(service_account_key)
.build()
.await
.unwrap();
let hub = Speech::new(hyper::Client::builder().build(https), auth);

Module file was created for incompatible target x86_64-apple-macosx10.10

I was trying to run the sample code from Apple's website, Adding-Content-to-Apple-Music, which shows how to interact with Apple Music Content.
Then it turned out to be a problem of dealing with JWT(JSON Web Token), and the current available libraries of JWT in Objective-C and Swift are at jwt.io.
But only vapor / jwt can sign with ES256, which Apple Music requires. I cloned the code from GitHub, "swift build" the library, and imported it into the project. However, I get this error on the line import JWT in my code:
Module file was created for incompatible target x86_64-apple-macosx10.10
What does this mean? Can I only use the library in macOS?
update: the jwt module works in vapor, and I think I can get the JWT successfully.
But when I do this in terminal :
just like the apple document says:
"curl -v -H 'Authorization: Bearer [developer token]' "https
://api.music.apple.com/v1/catalog/us/songs/203709340""
I got a http status code 500.....
Is it my fault or Apple's Server's??
Help me please..
I try to use anther library in Python which name is jwcrypto.(pip install jwcrypto to get it)
Python code:
from jwcrypto import jwt,jwk
import time
currentTime = time.time()
iat = int(currentTime)
exp = iat + 10000000
p8Data = open('AuthKey_myP8file.p8')
privateKey = p8Data.read()
key = jwk.JWK(generate='EC', curve='P-256')
key.import_from_pem(data=privateKey)
Token = jwt.JWT(header={'alg':'ES256', 'kid':'myKid'},claims={'iss':'myIss','iat':iat,'exp': exp })
Token.make_signed_token(key)
t = Token.serialize()// t is the Developer Token!!
Finally I got http status code 200, and the response! The picture is here:https://i.stack.imgur.com/67rHy.jpg

Access Google spreadsheet API without auth token

I have created Google Spreadsheet, and given edit access to all (can edit even without login).
Here is the link. I would like to update this sheet with Google Spreadsheet API. But I am getting error. My requirement is update the sheet thru API even without access credential.
It is possible to write to spreadsheet without OAuth or API Keys. You need to use Service Account Keys.
Here is what I did for my Node.js environment.
Get a service account key from https://console.cloud.google.com/apis/credentials (You can here also restrict what this keys is allowed todo)
When creating, make sure you click the Furnish a new private key
Select JSON when it asks you how to download the key.
The service account key you have just generated includes a client_email.
Go to you google spreadsheet and allow this client_email to have write access on this document
Use the following code to authenticate
let jwtClient = new google.auth.JWT(client_email, null, private_key, [
"https://www.googleapis.com/auth/spreadsheets",
]);
//authenticate request
jwtClient.authorize(function(err, tokens) {
// at this point the authentication is done you can now use `jwtClient`
// to read or write to the spreadsheet
});
client_email and private_key are part of the service account key
A more detailed description can be found here. http://isd-soft.com/tech_blog/accessing-google-apis-using-service-account-node-js/ Also, all credit goes to this page.
You need to be authorized to make such requests
Every request your application sends to the Google Sheets API needs to
identify your application to Google. There are two ways to identify
your application: using an OAuth 2.0 token (which also authorizes the
request) and/or using the application's API key. Here's how to
determine which of those options to use:
If the request requires authorization (such as a request for an
individual's private data), then the application must provide an OAuth
2.0 token with the request. The application may also provide the API key, but it doesn't have to. If the request doesn't require
authorization (such as a request for public data), then the
application must provide either the API key or an OAuth 2.0 token, or
both—whatever option is most convenient for you.
That's it. There's no bypassing authorization.
Finally digged deep enough and found the answer. Any kind of writing, even to publicly editable sheets requires an OAuth flow:
https://issuetracker.google.com/issues/36755576#comment3
Jürgen Brandstetter's answer above is completely right. Using Postman, I have been successful without using an OAuth token (I needed my personal API key and a service account) - I have written to a new sheet (in fact I did a batchUpdate operation with two steps, first create a new sheet and then pasteData on it). I followed the instructions here to create a service account, downloaded the credentials JSON and used it to create and sign a JWT string that is later used as Bearer.
Here is the Java code to obtain the JWT string:
private static String getSignedJWT() throws IOException {
InputStream in = YourClass.class.getResourceAsStream("/downloaded-service-account-creds.json");
if (in == null) {
throw new FileNotFoundException("Resource not found");
}
ServiceAccountCredentials serviceAccountCredentials = ServiceAccountCredentials.fromStream(in);
GoogleCredentials googleCredentials = serviceAccountCredentials
.createScoped(Collections.singletonList(SheetsScopes.SPREADSHEETS));
PrivateKey privateKey = serviceAccountCredentials.getPrivateKey();
String privateKeyId = serviceAccountCredentials.getPrivateKeyId();
long now = System.currentTimeMillis();
Algorithm algorithm = Algorithm.RSA256(null, (RSAPrivateKey)privateKey);
String signedJwt = JWT.create()
.withKeyId(privateKeyId)
.withIssuer(serviceAccountCredentials.getClientEmail())
.withSubject(serviceAccountCredentials.getClientEmail())
.withAudience("https://sheets.googleapis.com/")
.withIssuedAt(new Date(now))
.withExpiresAt(new Date(now + 3600 * 1000L))
.sign(algorithm);
return signedJwt;
}
Dependencies needed: com.auth0:java-jwt and com.google.auth:google-auth-library-oauth2-http.
Here's the curl that uses the JWT string generated above:
curl --location --request POST 'https://sheets.googleapis.com/v4/spreadsheets/YOUR_SHEET_ID:batchUpdate?key=ANY_PERSONAL_API_KEY' \
--header 'Accept: application/json' \
--header 'Content-Type: application/json' \
--header 'Authorization: Bearer YOUR_JWT_STRING' \
--data-raw '{
"requests": [
{
"addSheet": {
"properties": {
"title": "newPred",
"sheetId": 0
}
}
},
{
"pasteData": {
"coordinate": {
"columnIndex": 0,
"rowIndex": 0,
"sheetId": 0
},
"delimiter": "\t",
"data": "col1\tcol2\nPeter\t25",
"type": "PASTE_NORMAL"
}
}
]
}'
Not exactly what asked but here is a solution with google-api-nodejs-client worked for me:
const { google } = require('googleapis');
const sheets = google.sheets('v4');
const spreadsheetId = '.........';
(async () => {
const auth = new google.auth.GoogleAuth({
keyFile: './my_service_account_privatekey.json',
scopes: ['https://www.googleapis.com/auth/spreadsheets'],
});
// get
try {
const getResult = await sheets.spreadsheets.values.get({
auth,
spreadsheetId,
range: 'Sheet1!B2:C4'
})
console.log('Got values:', getResult.data.values)
} catch (error) {
console.error('Get error:', error)
}
})()
docs: https://github.com/googleapis/google-api-nodejs-client#using-the-keyfile-property

Using REST APIs

I am new to gerrit. I am using gerrit V. 2.6 . I want to use gerrit REST APIs in my python script. But not able to figure out how to use it. I tried below code but getting errors.
curl --digest --user user:password http://server/a/changes/path/to/project~branch~change_id/rebase
getting error :
401 Authorization Required
Authorization Required
This server could not verify that you
are authorized to access the document
requested. Either you supplied the wrong
credentials (e.g., bad password), or your
browser doesn't understand how to supply
the credentials required.
Am I missing something.??
Are you using the correct username:password combination? This isn't your network password - it is the HTTP password that gerrit generates. You can find it by going to Settings->HTTP Password. If the password box is blank, click the button to have Gerrit generate a new password.
You may try using pygerrit. https://pypi.python.org/pypi/pygerrit/0.2.1
I think it has some APIs to easily access gerrit.
As #Ramraj mentioned, you can try using pygerrit or pygerrit2.
And I provide some examples that how I use gerrit REST APIs in my python script.
Here is the code.
auth = HTTPBasicAuth(username, password)
rest = GerritRestAPI(url='http://review.xxxxxx.com:8080', auth=auth)
Query changes by change number.
info = rest.get("/changes/?q=change:{}".format(change_number))
change_id = info[0]['change_id']
subject = info[0]['subject']
Query changes by commit id.
info = rest.get("/changes/?q=commit:{}".format(commit_id))
change_id = info[0]['change_id']
subject = info[0]['subject']
Revert a change.
headers = {'content-type': 'application/json'}
query = "/changes/" + str(change_number) + "/revert"
my_data = {"message": "{}".format("Revert "+str(subject))}
rest.post(query, data=json.dumps(my_data), timeout=30, headers=headers)
Review a change
headers = {'content-disposition': 'attachment', 'content-type': 'application/json'}
query = "/changes/" + str(change_number) + "/revisions/current/review"
my_data = { "labels": {"Code-Review": "+2", "Verified": "+1"} }
rest.post(query, data=json.dumps(my_data), timeout=30, headers=headers)

Why does this twitter oauth API token request fail

[Note: all oauth tokens/secrets below were created randomly; they are
NOT my actual tokens/secrets]
curl -o /tmp/test.txt 'https://api.twitter.com/oauth/request_token?
oauth_timestamp=1345141469&
consumer_key=UEIUyoBjBRomdvrVcUTn&oauth_access_token_secret=YePiEkSDFdYAOgscijMCazcSfBflykjsEyaaVbuJeO&oauth_access_token=47849378%2drZlzmwutYqGypbLsQUoZUsGdDkVVRkjkOkSfikNZC&oauth_nonce=1345141469&
consumer_secret=rUOeZMYraAapKmXqYpxNLTOuGNmAQbGFqUEpPRlW&
oauth_version=1%2e0&
oauth_signature_method=HMAC%2dSHA1&oauth_signature=H0KLLecZNAAz%2bXoyrPRiUs37X3Zz%2bAcabMa5M4oDLkM'
[I added newlines for clarity; actual command is one single line]
Assuming all the other data is valid, why does the command above yield
"Failed to validate oauth signature and token" (even when I use my
real data)?
In particular, is my signature
"H0KLLecZNAAz%2bXoyrPRiUs37X3Zz%2bAcabMa5M4oDLkM" invalid, or am I
doing something more fundamentally wrong.
The program I used to generate this:
#!/bin/perl
use Digest::SHA;
%twitter_auth_hash = (
"oauth_access_token" => "47849378-rZlzmwutYqGypbLsQUoZUsGdDkVVRkjkOkSfikNZC",
"oauth_access_token_secret" => "YePiEkSDFdYAOgscijMCazcSfBflykjsEyaaVbuJeO",
"consumer_key" => "UEIUyoBjBRomdvrVcUTn",
"consumer_secret" => "rUOeZMYraAapKmXqYpxNLTOuGNmAQbGFqUEpPRlW"
);
# if uncommented, pull my actual data
# require "bc-private.pl";
$twitter_auth_hash{"oauth_signature_method"} = "HMAC-SHA1";
$twitter_auth_hash{"oauth_version"} = "1.0";
$twitter_auth_hash{"oauth_timestamp"} = time();
$twitter_auth_hash{"oauth_nonce"} = time();
for $i (keys %twitter_auth_hash) {
push(#str,"$i=".urlencode($twitter_auth_hash{$i}));
}
$str = join("&",#str);
# thing to sign
$url = "GET $str";
# signing it
$sig = urlencode(Digest::SHA::hmac_sha256_base64($url, "rUOeZMYraAapKmXqYpxNLTOuGNmAQbGFqUEpPRlW&YePiEkSDFdYAOgscijMCazcSfBflykjsEyaaVbuJeO"));
# full URL incl sig
$furl = "https://api.twitter.com/oauth/request_token?$str&oauth_signature=$sig";
# system("curl -o /tmp/testing.txt '$furl'");
print "FURL: $furl\n";
print "STR: $str\n";
print "SIG: $sig\n";
sub urlencode {
my($str) = #_;
$str=~s/([^a-zA-Z0-9])/"%".unpack("H2",$1)/iseg;
$str=~s/ /\+/isg;
return $str;
}
Note: I realize there are many other possible reasons this is failing,
but current question is: am I sending the parameters correctly and am
I computing the signature correctly.
Twitter asks that you do a POST for the request token.

Resources