I'm new to Twitters API and Twitter's twitter4j library. I've recently registered an app to be able to use Twitter's API. Twitter has granted me consumer API keys (API key & API secret key), as well as an access token & access token secret.
The problem is, I've been trying to use twitter4j to authenticate into twitter (using the aforementioned keys), but when trying to access any of the APIs resources, I get an error saying I'm not allowed access due to a rate limit. But how can I possibly have reached a rate limit when I've never been able to query the api? :,(
This is what I'm attempting (with sensitive bits replaced by dummy values):
#SpringBootApplication
public class App
{
private static final String CONSUMER_KEY = "FakeConsumerKey";
private static final String CONSUMER_SECRET = "FakeConsumerSecret";
public static void main( String[] args )
{
SpringApplication.run(App.class, args);
System.out.println("Making an authentication request to"
+ " retrieve the bearer token...");
OAuth2Token token;
token = getOAuth2Token();
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setApplicationOnlyAuthEnabled(true);
cb.setOAuthConsumerKey(CONSUMER_KEY);
cb.setOAuthConsumerSecret(CONSUMER_SECRET);
cb.setOAuth2TokenType(token.getTokenType());
cb.setOAuth2AccessToken(token.getAccessToken());
Twitter twitter = new TwitterFactory(cb.build()).getInstance();
try {
System.out.println("My screen name: " + twitter.getScreenName());
} catch (IllegalStateException e) {
// TODO Auto-generated catch block
e.printStackTrace();
} catch (TwitterException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
}
public static OAuth2Token getOAuth2Token()
{
OAuth2Token token = null;
ConfigurationBuilder cb;
cb = new ConfigurationBuilder();
cb.setApplicationOnlyAuthEnabled(true);
cb.setOAuthConsumerKey(CONSUMER_KEY);
cb.setOAuthConsumerSecret(CONSUMER_SECRET);
try
{
token = new TwitterFactory(cb.build())
.getInstance().getOAuth2Token();
System.out.println("token: " + token.getAccessToken());
}
catch (Exception e)
{
System.out.println("Can't get OAuth2 token");
e.printStackTrace();
System.exit(0);
}
return token;
}
}
This is the error returned:
403:The request is understood, but it has been refused. An accompanying error message will explain why. This code is used when requests are being denied due to update limits (https://support.twitter.com/articles/15364-about-twitter-limits-update-api-dm-and-following).
message - Your credentials do not allow access to this resource
code - 220
Relevant discussions can be found on the Internet at:
http://www.google.co.jp/search?q=9a9caf7a or
http://www.google.co.jp/search?q=bf94ba05
TwitterException{exceptionCode=[9a9caf7a-bf94ba05], statusCode=403, message=Your credentials do not allow access to this resource, code=220, retryAfter=-1, rateLimitStatus=null, version=4.0.6}
at twitter4j.HttpClientImpl.handleRequest(HttpClientImpl.java:164)
at twitter4j.HttpClientBase.request(HttpClientBase.java:57)
at twitter4j.HttpClientBase.get(HttpClientBase.java:75)
at twitter4j.TwitterBaseImpl.fillInIDAndScreenName(TwitterBaseImpl.java:133)
at twitter4j.TwitterBaseImpl.fillInIDAndScreenName(TwitterBaseImpl.java:128)
at twitter4j.TwitterBaseImpl.getScreenName(TwitterBaseImpl.java:108)
at com.vismark.social.twitter.TwitterAccountService.App.main(App.java:41)
Where did I go wrong?
Definition of getScreenName
"Returns authenticating user's screen name.
This method may internally call verifyCredentials() on the first invocation if
- this instance is authenticated by Basic and email address is supplied instead of screen name, or - this instance is authenticated by OAuth."
User-based authentication has to use OAuth 1.0a not OAuth 2. You need to get access tokens, follow this :
https://developer.twitter.com/en/docs/basics/authentication/overview/using-oauth
When you get your access tokens, just update your ConfigurationBuilder like this :
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setApplicationOnlyAuthEnabled(false);
cb.setOAuthConsumerKey(CONSUMER_KEY)
.setOAuthConsumerSecret(CONSUMER_SECRET)
.setOAuthAccessToken(ACCESS_TOKEN)
.setOAuthAccessTokenSecret(ACCESS_TOKEN_SECRET);
Related
For use on a mobile app, I'm authenticating for a Firebase realtime db. The code for reading the private key and generating an access token works fine on the Android emulator, but generates an error on an actual phone.
Here's the code:
private String getToken () {
try {
InputStream serviceAccount = getApplicationContext().getAssets().open("asset.json");
GoogleCredentials googleCred = GoogleCredentials
.fromStream (serviceAccount)
.createScoped (Arrays.asList(
"https://www.googleapis.com/auth/firebase.database",
"https://www.googleapis.com/auth/userinfo.email"
));
StrictMode.ThreadPolicy policy = new StrictMode.ThreadPolicy.Builder().permitAll().build();
StrictMode.setThreadPolicy(policy);
try {
return googleCred.refreshAccessToken().getTokenValue();
}
catch (Exception e) {
Log.d ("getToken", "Auth 2 Error: " + e);
return "";
}
}
catch (Exception e) {
Log.d ("getToken", "Auth 1 Error: " + e);
return "";
}
}
When this code is run on the phone, the error generated is:
Auth 2 Error:
I5.m: Error getting access token for service account: 400 Bad Request
POST https://oauth2.googleapis.com/token
{
"error": "invalid_grant",
"error_description": "Invalid grant: account not found"
}
iss: xxxredactedxxx#appspot.gserviceaccount.com
The private key asset is being read correctly on both emulator and phone, so I suspect the problem is not in the code, but something in my Firebase and/or Google Cloud setup. What "account" is needed when the app is run on a phone but not in the emulator? And where would I set up that account?
Edit: In the Google Cloud Service Accounts page, I've added the iss account (in the error message) to "Principals with access to this Service Account" table:
Principal: xxxredactedxxx#appspot.gserviceaccount.com
Name: App Engine default service account
Role: Editor
Service Account Token Creator
(To be clear, I'm not using logins or other userid/password-style authentication; the app should connect internally to its db in Firebase in a read-only mode, based on the private key).
I'm trying to use the Azure Workload Identity MSAL Java Sample, and I'm trying to figure out if the built-in token cache that comes with MSAL4J is actually usable with Azure Workload Identity (Client Assertions), as my understanding is that every time you request a new token, you need to read the AZURE_FEDERATED_TOKEN_FILE again (See // 1). I've looked through the MSAL4J code and to me it looks like you'd need to throw away the ConfidentialClientApplication (see // 2) and create a brand new one to load in a new federated token file, because the clientAssertion ends up baked into the client. So then I'd need to do my own checks to figure out if I need if I need to recreate the client, basically defeating the purpose of the built-in client.
Are my assumptions correct? Or is there some way to hook into the token refresh process and reload the clientAssertion?
Maybe MSAL4J needs integrated token cache support for Azure Workload Identity that handles the reloading of the client assertion on renewal?
Here is the sample code included for context.
public class CustomTokenCredential implements TokenCredential {
public Mono<AccessToken> getToken(TokenRequestContext request) {
Map<String, String> env = System.getenv();
String clientAssertion;
try {
clientAssertion = new String(Files.readAllBytes(Paths.get(env.get("AZURE_FEDERATED_TOKEN_FILE"))),
StandardCharsets.UTF_8); // 1
} catch (IOException e) {
throw new RuntimeException(e);
}
IClientCredential credential = ClientCredentialFactory.createFromClientAssertion(clientAssertion);
String authority = env.get("AZURE_AUTHORITY_HOST") + env.get("AZURE_TENANT_ID");
try {
ConfidentialClientApplication app = ConfidentialClientApplication
.builder(env.get("AZURE_CLIENT_ID"), credential).authority(authority).build(); // 2
Set<String> scopes = new HashSet<>();
for (String scope : request.getScopes())
scopes.add(scope);
ClientCredentialParameters parameters = ClientCredentialParameters.builder(scopes).build();
IAuthenticationResult result = app.acquireToken(parameters).join();
return Mono.just(
new AccessToken(result.accessToken(), result.expiresOnDate().toInstant().atOffset(ZoneOffset.UTC)));
} catch (Exception e) {
System.out.printf("Error creating client application: %s", e.getMessage());
System.exit(1);
}
return Mono.empty();
}
}
Iam trying to authenticate a Java app with Cognito.
I have used for python the warrant library that worked very good. But i want to do the same in java now.
My Python function i used for authentication with the warrant library
def SRPauthentication(organizationAdmin,
password,
pool_id,
client_id,
client):
aws = AWSSRP(username=organizationAdmin,
password=password,
pool_id=pool_id,
client_id=client_id,
client=client)
tokens = aws.authenticate_user()
authorization_token= tokens['AuthenticationResult']['IdToken']
return authorization_token
with this i could easily acces some secured APIs.
Now i want to do the same with Java but i have problems.
This is my solution so far is this method:
public static void GetCreds()
{
AWSCognitoIdentityProvider identityProvider = AWSCognitoIdentityProviderClientBuilder.defaultClient();
AdminInitiateAuthRequest adminInitiateAuthRequest = new AdminInitiateAuthRequest().
withAuthFlow(AuthFlowType.USER_SRP_AUTH).
withClientId("234234234234").withUserPoolId("eu-central-1_sdfsdfdsf")
.addAuthParametersEntry("USERNAME", "UserK").
addAuthParametersEntry("PASSWORD","#######);
adminInitiateAuthRequest.getAuthFlow();
AdminInitiateAuthResult adminInitiateAuth = identityProvider.adminInitiateAuth(adminInitiateAuthRequest);
System.out.println(adminInitiateAuth.getAuthenticationResult().getIdToken());
}
When i run this i get an Exception:
Exception in thread "main" `com.amazonaws.services.cognitoidp.model.AWSCognitoIdentityProviderException: User: arn:aws:iam::XXXXXXXXXXXXXXXXX:user/khan is not authorized to perform: cognito-idp:AdminInitiateAuth on resource: arn:aws:cognito-idp:eu-central-1:XXXXXXXX:userpool/eu-central-1_XXXXXXX with an explicit deny (Service: AWSCognitoIdentityProvider; Status Code: 400; Error Code: AccessDeniedException; Request ID: 21be0b8e-adec-11e8-ad45-234234234)`
It says iam not authorized to perform this kind of instruction. So i guess iam doing something generally wrong. Because its working with my python code and in Java it recognizes my username from the credentials. The Cognito call should actually be independent from my aws credentials/useraccount right?
How to authenticate with Cognito using Java to get an Token to access secured aws services?
EDIT:
AWSCognitoIdentityProvider identityProvider = AWSCognitoIdentityProviderClientBuilder.standard()
.build();
InitiateAuthRequest adminInitiateAuthRequest = new InitiateAuthRequest()
.withAuthFlow(AuthFlowType.USER_SRP_AUTH)
.withClientId("XXXXXXXXXXXXXXXXX")
.addAuthParametersEntry("USERNAME", "user").
addAuthParametersEntry("PASSWORD","za$Lwn")
.addAuthParametersEntry("SRP_A",new AuthenticationHelper("eu-central-1XXXXXXXXX").getA().toString(16));
adminInitiateAuthRequest.getAuthFlow();
InitiateAuthResult adminInitiateAuth = identityProvider.initiateAuth(adminInitiateAuthRequest);
System.out.println(adminInitiateAuth);
I changed the AdminInitateAuthRequest to InitateAuthRequest. After that i had the Error missing SRP_A parameter that i somehow fixed with a similiar question here
And now i recive this :
{ChallengeName: PASSWORD_VERIFIER,ChallengeParameters: {SALT=877734234324234ed68300f39bc5b, SECRET_BLOCK=lrkwejrlewrjlewkjrewlrkjwerlewkjrewlrkjewrlkewjrlewkrjZ+Q==, USER_ID_FOR_SRP=user, USERNAME=user, SRP_B=43ecc1lwkerjwelrkjewlrjewrlkewjrpoipweoriwe9r873jr34h9r834hr3455f7d079d71e5012f1623ed54dd10b832792dafa3438cca3f59c0f462cbaee255d5b7c2werwerwerkjweorkjwerwerewrf5020e4f8b5452f3b89caef4a797456743602b80b5259261f90e52374adc06b456521a9026cce9c1cbe8b9ffd6040e8c1589d35546861422110ac7e38c1c93389b802a03e3e2e4a50e75d088275195f836f66e25f1a431dd56bb2},}
I have shorten the result with all the keys, but what to do next ?
Finally i could solve it with this code class.
There are multiple challenges involved in SRP authentication. The InitiateAuthRequest is one first request that is necessary.
This similiar question helped me :
stackoverflow
stackoverfow
String PerformSRPAuthentication(String username, String password) {
String authresult = null;
InitiateAuthRequest initiateAuthRequest = initiateUserSrpAuthRequest(username);
try {
AnonymousAWSCredentials awsCreds = new AnonymousAWSCredentials();
AWSCognitoIdentityProvider cognitoIdentityProvider = AWSCognitoIdentityProviderClientBuilder
.standard()
.withCredentials(new AWSStaticCredentialsProvider(awsCreds))
.withRegion(Regions.fromName(this.region))
.build();
InitiateAuthResult initiateAuthResult = cognitoIdentityProvider.initiateAuth(initiateAuthRequest);
if (ChallengeNameType.PASSWORD_VERIFIER.toString().equals(initiateAuthResult.getChallengeName())) {
RespondToAuthChallengeRequest challengeRequest = userSrpAuthRequest(initiateAuthResult, password);
RespondToAuthChallengeResult result = cognitoIdentityProvider.respondToAuthChallenge(challengeRequest);
//System.out.println(result);
System.out.println(CognitoJWTParser.getPayload(result.getAuthenticationResult().getIdToken()));
authresult = result.getAuthenticationResult().getIdToken();
}
} catch (final Exception ex) {
System.out.println("Exception" + ex);
}
return authresult;
}
private InitiateAuthRequest initiateUserSrpAuthRequest(String username) {
InitiateAuthRequest initiateAuthRequest = new InitiateAuthRequest();
initiateAuthRequest.setAuthFlow(AuthFlowType.USER_SRP_AUTH);
initiateAuthRequest.setClientId(this.clientId);
//Only to be used if the pool contains the secret key.
//initiateAuthRequest.addAuthParametersEntry("SECRET_HASH", this.calculateSecretHash(this.clientId,this.secretKey,username));
initiateAuthRequest.addAuthParametersEntry("USERNAME", username);
initiateAuthRequest.addAuthParametersEntry("SRP_A", this.getA().toString(16));
return initiateAuthRequest;
}
I have a java program for fetching my tweets and below is the code
public class TwitterTest {
public static void main(String[] args) {
Twitter twitter = new TwitterFactory().getInstance();
// My Applications Consumer and Auth Access Token
//twitter.setOAuthConsumer("67MIcbC1X6mbpaEqxa7YTd1hDPIdLb5bLKf4TxIRLAsX63DgFQ", "7HHjSHJ6Rjxx4ASC2465AlWBG");
twitter.setOAuthAccessToken(new AccessToken("s1y1iAhG5nsXTrE9OVAsqMtqiLIP4QKT8CmTRVgV9LC3O", "110445397-Cf3l9NAK4iD8VAERXp0ZMKnAfWx9KywuJs3OSdkF"));
try {
ResponseList<Status> a = twitter.getUserTimeline(new Paging(1,5));
for(Status b: a) {
System.out.println(b.getText());
}
}catch(Exception e ){
}
}
}
Below is the error which i get
Relevant discussions can be found on the Internet at:
http://www.google.co.jp/search?q=b64d2231 or
http://www.google.co.jp/search?q=309f0452
TwitterException{exceptionCode=[b64d2231-309f0452], statusCode=403, message=SSL is required, code=92, retryAfter=-1, rateLimitStatus=RateLimitStatusJSONImpl{remaining=178, limit=180, resetTimeInSeconds=1430935706, secondsUntilReset=775}, version=3.0.3}
What is the reason for the error?
You need to set SSL enabled. Look at this topic for further information: "SSL is required" exception while requesting OAuthRequest Token using Twitter4J library
It is clearly mentioned that the number of Requests allotted via application-only auth for search/tweets is 450/15 minutes. I am using twitter4j version 4.0.1 but, I am getting only 180 requests/15 minutes via application-only auth.
I tried to get the limit using the code below and I got the limit as 450. But, am getting the rate limit exceeded error after 180 requests.
twitter.getRateLimitStatus().get("/search/tweets").getLimit();
Where did I go wrong?
Update
public static OAuth2Token getOAuth2Token(String key, String sec) {
OAuth2Token token = null;
ConfigurationBuilder cb;
cb = new ConfigurationBuilder();
cb.setApplicationOnlyAuthEnabled(true);
cb.setOAuthConsumerKey(key).setOAuthConsumerSecret(sec);
try
{
token = new TwitterFactory(cb.build()).getInstance().getOAuth2Token();
}
catch (Exception e)
{
System.out.println("Could not get OAuth2 token");
e.printStackTrace();
System.exit(0);
}
return token;
}
public TwitterManager() throws TwitterException {
OAuth2Token token;
token = getOAuth2Token("XXXX","XXXX");
ConfigurationBuilder cb = new ConfigurationBuilder();
cb.setApplicationOnlyAuthEnabled(true);
cb.setOAuthConsumerKey("XXXX");
cb.setOAuthConsumerSecret("XXXX");
cb.setOAuth2TokenType(token.getTokenType());
cb.setOAuth2AccessToken(token.getAccessToken());
twitter = new TwitterFactory(cb.build()).getInstance();
}
This is how I used application-only auth.
Are you able to share any more of your code?
From the results, I suspect you are not using application-only, but maybe have used another oauth flow?