Posting User Status to twitter with stored tokens

51 views
Skip to first unread message

Aruna

unread,
Oct 22, 2010, 10:51:56 AM10/22/10
to Signpost users
Hi ,
I am at final step of posting user status to twitter with stored
tokens. sign post library works for me for retrieving request and
access tokens. After that I am storing access tokens in database along
with username. when ever a user comes back I will look up the user
access tokens with username but problem here is when I post status
with these stored tokens I am getting 401 error. How can I make this
work with out authorization? Other thing is that posting status works
fine with normal authorization.

Is there any method to tell twitter that authorization is already
done? Can I send a call back url along with stored tokens?

I have searched forums but did not get right solution.Please help me .

Thanks,
Aruna

Gary Moss

unread,
Oct 22, 2010, 3:08:54 PM10/22/10
to signpos...@googlegroups.com
Hi Aruna,

  Are you signing the request before you submit it?

  Here is some sample code, given that you have restored tokens to vars consumer_access_token and consumer_token_secret.

  private String consumer_token_secret = null;
  private String consumer_access_token = null;
  private OAuthConsumer oauth_consumer;

   // after restoring access tokens
    ...
    this.oauth_consumer  = new DefaultOAuthConsumer( TwitterConsumerKey,  TwitterConsumerSecret );
    this.oauth_consumer.setTokenWithSecret( this.consumer_access_token, this.consumer_token_secret ); 
    ...

    // Use something like the following to make the Twitter API call:

private HttpURLConnection WSCall( String urlstr, Boolean authNeeded, String method )
{
if( authNeeded && (this.oauth_consumer == null || this.oauth_consumer.getToken() == null) )
{
Object [] args = { "Please login first!" };
_win.call( "alert", args );
return null;
}
try
{
System.err.println( "WSCall: url: " + urlstr );
URL url = new URL( urlstr );
HttpURLConnection request = (HttpURLConnection) url.openConnection();
request.setRequestMethod( method );
request.setDoOutput(false);
request.setDoInput(true);
request.setRequestProperty( HTTPConstants.HTTP_CONTENT_TYPE, HTTPConstants.MEDIA_TYPE_X_WWW_FORM );
if( authNeeded )
this.oauth_consumer.sign( request );
request.connect();
return request;
}
catch( Exception ex )
{
       System.err.println( "Can't make WS call: " + urlstr );
       ex.printStackTrace( System.err );
       return null;
}
}

HTH,
-Gary



--
You received this message because you are subscribed to the Google Groups "Signpost users" group.
To post to this group, send email to signpos...@googlegroups.com.
To unsubscribe from this group, send email to signpost-user...@googlegroups.com.
For more options, visit this group at http://groups.google.com/group/signpost-users?hl=en.




--
╔╤╗
╠╬╣         Gary Moss
╠╬╣
╠╬╣ e-mail: gary...@gmail.com
╠╬╣ mobile: 401-580-1512       
╚╧╝

Aruna Pamula

unread,
Oct 25, 2010, 1:15:38 PM10/25/10
to signpos...@googlegroups.com
Hi Gary,
Thanks for your response.  Yes I am signing the request before I submit it.
 
Iam using below PostEnabledOAuthConsumer class for creating new consumer object. I tried using way you suggested but it gives unauthorized error even for normal authorization flow. Problem is with status parameter it has to be signed along with request.
 
Here is my code..
 
private class PostEnabledHttpURLConnectionRequestAdapter extends
HttpURLConnectionRequestAdapter {
        private String body;
        public
PostEnabledHttpURLConnectionRequestAdapter(HttpURLConnection request,
String body) {
            super( request );
            this.body = body;
        }
        public InputStream getMessagePayload() throws IOException {
            return new ByteArrayInputStream(body.getBytes("UTF-8"));
        }
    }

    private class PostEnabledOAuthConsumer extends
DefaultOAuthConsumer {
        public PostEnabledOAuthConsumer( String consumerKey, String
consumerSecret ) {
            super( consumerKey, consumerSecret );
        }
        protected HttpRequest wrap(Object request, String body) {
            if (!(request instanceof HttpURLConnection)) {
                throw new IllegalArgumentException(
                        "The default consumer expects requests of type
java.net.HttpURLConnection");
            }
            return new
PostEnabledHttpURLConnectionRequestAdapter((HttpURLConnection)
request, body);
        }
        public HttpRequest sign(Object request, String body) throws
OAuthMessageSignerException,
            OAuthExpectationFailedException,
OAuthCommunicationException {
            return sign(wrap(request, body));
        }
    }


 

//Here is the twitter API call
 
HttpURLConnection connection = (HttpURLConnection)new URL("http://api.twitter.com/1/statuses/update.xml").openConnection();
 
PostEnabledOAuthConsumer pcon =
new PostEnabledOAuthConsumer(consumerKey,consumerSecret);

 pcon.setTokenWithSecret(storedToken,storedSecret);

connection.setRequestMethod("POST");

connection.setRequestProperty(

"Content-Type", "application/x-www-form-urlencoded; charset=UTF-8");

connection.setDoInput(

true);

connection.setDoOutput(

true);

StringBuilder content =

new StringBuilder();

content.append(

"status=").append(java.net.URLEncoder.encode(status, "UTF-8"));

//signing the request along with body
pcon.sign(connection, content.toString());

outputStream = connection.getOutputStream();

outputStream.write(content.toString().getBytes(

"UTF-8"));

connection.connect();

I am receiving below error :
Server returned HTTP response code: 401 for URL: http://api.twitter.com/1/statuses/update.xml
 
Do I need to set any parameter that user has already authenticated the application? please suggest is this the way we can use stored tokens or is there any other way to proceed.
 
Thanks,
Aruna

Gary Moss

unread,
Oct 25, 2010, 5:26:41 PM10/25/10
to signpos...@googlegroups.com
Aruna,

  I put the status in the URL, so my example does not need to send a body with the message.  Of course there are lots of ways to do things.  I thought maybe if you tried it my way and got it working, you would see what was different.  This is how I build the URL string:

// status being posted is in 'msg' below...
String url = http://api.twitter.com/1/statuses/update.json?" + "status=" + URLEncoder.encode( msg, "UTF-8" );

  Maybe your are not saving/restoring the tokens correctly, I save mine to an XML file under the user's App folder.  No, you don't need to set any parameter to state that the user has authenticated, just sign the request.  Below is some code for the save/restore.  Note that I am requiring the user to type the PIN one time, not using the callback URL, when attempting to use the callback URL I was getting 401 errors, and never figured out why, just figured I would move on since the user only needs to authenticate once if you save the tokens indefinitely.  This was back in the spring and I never revisited it.  So, I do not restore the user tokens in the same session as when they first authenticate with the PIN. They type the PIN in to my browser app one time, then on later sessions the tokens are retrieved automatically.  I hope the undefined variables are self-explanatory.  Have you tried to print out details of the error?  Twitter supposedly places detailed error messages in the response body.

  Hopefully someone more knowledgeable about authentication or signpost can chime in, I struggle with this stuff too.  :)
-Gary

    public String GetRequestToken()
    {
        try
        {
            log.debug("Fetching request token from Twitter...");

            // not using a callback URL, user must type PIN to authenticate the first time
            String authUrl = this.oauth_provider.retrieveRequestToken( this.oauth_consumer, OAuth.OUT_OF_BAND );
            log.trace("Request token: " + this.oauth_consumer.getToken());
            log.trace("Token secret: " + this.oauth_consumer.getTokenSecret());
            SaveConsumerTokens( "request_token", this.oauth_consumer.getToken(),
                                "token_secret", this.oauth_consumer.getTokenSecret() );
            return authUrl;
        }
        catch( OAuthMessageSignerException ex )
        {
            Config.HandleException( this, ex );
        }
        catch( OAuthNotAuthorizedException ex )
        {
            Config.HandleException( this, ex );
        }
        catch( OAuthExpectationFailedException ex )
        {
            Config.HandleException( this, ex );
        }
        catch( OAuthCommunicationException ex )
        {
            Config.HandleException( this, ex );
        }
        return null;
    }
   
    public void SaveConsumerTokens( String ... tokens )
    {
        try
        {
            String folderName = config.APP_DIR + File.separator + this._screen_name;
            File folder = new File( folderName );
            if( ! folder.exists() )
                folder.mkdir();
            String fileName = folder.getAbsolutePath() + File.separator + "oauth_tokens.xml";
            File file = new File( fileName );
            if( ! file.exists() && ! file.createNewFile() )
                throw new Exception( "Can't create file for saving consumer tokens: " + file.getAbsolutePath() );
           
            FileWriter fw = new FileWriter( file );
            StringWriter strwriter = new StringWriter();
            XMLOutputFactory factory = XMLOutputFactory.newInstance();
            XMLStreamWriter writer = factory.createXMLStreamWriter( strwriter );
            writer.writeStartDocument( "utf-8", "1.0" );
            writer.writeStartElement( "oauth_consumer" );
            for( int i = 0; i < tokens.length; i += 2 )
            {
                writer.writeStartElement( tokens[i] );
                writer.writeCharacters( tokens[i+1] );
                writer.writeEndElement();
            }
            writer.writeEndElement();
            writer.writeEndDocument();
            writer.close();
            fw.write( strwriter.toString() );
            fw.close();
            log.debug( "Consumer tokens saved." );
        }
        catch( Exception ex )
        {
            Config.HandleException( this, ex );
        }

    }


     public void RestoreConsumerTokens()
    {
        try
        {
            log.debug( "Restoring consumer tokens..." );
            
            String folderName = config.APP_DIR + File.separator + this._screen_name;
            File folder = new File( folderName );
            if( ! folder.exists() )
                return; // nothing there
            String fileName = folder.getAbsolutePath() + File.separator + "oauth_tokens.xml";
            File file = new File( fileName );
            if( ! file.exists() )
                return; // nothing there
            
            XMLInputFactory factory = XMLInputFactory.newInstance();
            XMLStreamReader reader = factory.createXMLStreamReader( new FileReader( file ) );
            log.debug( "Reader initialized." );
            while( reader.hasNext() )
            {
                switch( reader.getEventType() )
                {
                case XMLStreamReader.START_ELEMENT:
                    log.trace( "Got START_ELEMENT" );
                    if( reader.getName().toString().equals( "access_token" ) )
                    {
                        reader.next();
                        this.consumer_access_token = reader.getText();
                        log.trace( "Got access_token: " + this.consumer_access_token );                        
                    }
                    else
                    if( reader.getName().toString().equals( "token_secret" ) )
                    {
                        reader.next();
                        this.consumer_token_secret = reader.getText();
                        log.trace( "Got token_secret: " + this.consumer_token_secret );
                    }
                    else
                    if( reader.getName().toString().equals( "request_token" ) )
                    {
                        reader.next();
                        this.consumer_request_token = reader.getText();
                        log.trace( "Got request_token: " + this.consumer_request_token );
                    }
                    break;
                case XMLStreamReader.END_ELEMENT:
                    log.trace( "Got END_ELEMENT: " + reader.getName() );
                    break;
                default:
                    log.trace( "Got event: " + reader.getEventType() );
                    break;
                }
                reader.next();
            }
            log.trace( "Reader done." );
            reader.close();
            if( this.consumer_access_token != null )
            {
                this.oauth_consumer.setTokenWithSecret( this.consumer_access_token, this.consumer_token_secret );
                log.debug( "Consumer access tokens restored." );
            }
        }
        catch( Exception ex )
        {
            Config.HandleException( this, ex );
            return;
        }
    }
   
  


On Mon, Oct 25, 2010 at 1:15 PM, Aruna Pamula <aruna...@gmail.com> wrote:
Hi Gary,
Thanks for your response.  Yes I am signing the request before I submit it.
 
Iam using below PostEnabledOAuthConsumer class for creating new consumer object. I tried using way you suggested but it gives unauthorized error even for normal authorization flow. Problem is with status parameter it has to be signed along with request.
 

 

Aruna Pamula

unread,
Oct 26, 2010, 3:29:31 PM10/26/10
to signpos...@googlegroups.com
Gary,
  I tried printing stack trace as shown below:
 
java.io.IOException: Server returned HTTP response code: 401 for URL: http://api.twitter.com/1/statuses/update.xml
2010-10-26 14:56:19,254 ERROR [STDERR]  at sun.net.www.protocol.http.HttpURLConnection.getInputStream(HttpURLConnection.java:1241)
 
Iam receving above error at code connection.getInputStream();   as I have set dooutput to true. I tries removing that but the result was same 401.
 
I tried the way you suggested but response was null and stacktrace is :
 
2010-10-26 15:13:13,135 INFO  [STDOUT] Can't make WS call: http://api.twitter.com/1/statuses/update.xml?status=Check+out+NEWS
java.lang.NullPointerException
java.io.Reader.<init>(Reader.java:61)
java.io.InputStreamReader.<init>(InputStreamReader.java:55)
oauth.signpost.OAuth.decodeForm(OAuth.java:157)
oauth.signpost.AbstractOAuthConsumer.collectBodyParameters(AbstractOAuthConsumer.java:236)
oauth.signpost.AbstractOAuthConsumer.sign(AbstractOAuthConsumer.java:96)
oauth.signpost.AbstractOAuthConsumer.sign(AbstractOAuthConsumer.java:119)
com.lsn.share.TwitterClient.postStateToTwitter(TwitterClient.java:341)
 
It is showing null pointer exception in code when I do signing.

this

.postConsumer.sign( request );
Before I do signing printed tokens from consumer object. They are not null.
 
Other thing for me, call back works fine and able to post the message once user authorizes and comes back to application.
 
I am saving tokens into Database. I tried so many ways but can't figure it out how to implement the same with stored tokens.
Do I need to create a new provider object along with new consumer object?
 
 
Thanks,
Aruna

Gary Moss

unread,
Oct 26, 2010, 5:06:57 PM10/26/10
to signpos...@googlegroups.com
Aruna,

   Did you call setDoInput( true ) on the request? If not, calling HttpURLConnection.getInputStream() will fail.  Also when using my method, make sure to setDoOutput( false ) because you are not sending a message body with the request. When you get the 401, if you can get the response back with getInputStream(), it should tell you why you got the 401.

  The response body should have the Twitter error message in it that explains why you got a 401.  If getInputStream() fails when setDoInput(true) is set, then it sounds like the connection is failing outright.  In that case, I would get tcpmon from Apache  (http://ws.apache.org/commons/tcpmon/), or something that will intercept your request show you what your request looks like to the server.  It will also show you what the response looks like.  If you haven't done this, it is very useful.  I haven't tried it, but twurl is recommended by Twitter (http://thechangelog.com/post/536535280/twurl-oauth-enabled-curl-for-the-twitter-api) to aid in development with OAuth.

  I create a new provider and consumer for every user session, so I call Init_OAuth() below before calling my RestoreConsumerTokens() method:

    private static final String TwitterOAuthEndpoint = "https://api.twitter.com/oauth/";
    private OAuthConsumer oauth_consumer = null;
    private OAuthProvider oauth_provider = null;

    public void Init_OAuth()
    {
        log.debug( "Init_OAuth()" );

        this.oauth_consumer = new DefaultOAuthConsumer(    TwitterConsumerKey,
                                                        TwitterConsumerSecret );

        this.oauth_provider = new DefaultOAuthProvider( TwitterOAuthEndpoint + "request_token",
                                                        TwitterOAuthEndpoint + "access_token",
                                                        TwitterOAuthEndpoint + "authorize" );
        log.debug( "Init_OAuth() done" );
    }

  There is also a Twitter Google group which has many postings from people with OAuth problems: http://groups.google.com/group/twitter-development-talk/topics?hl=en

-Gary

Aruna Pamula

unread,
Oct 28, 2010, 2:39:00 PM10/28/10
to signpos...@googlegroups.com
Gary,
 Thanks for your reply. I figured it out. when I save my tokens into database I am storing with a space. Thats the reason its giving 401 error i.e unauthorized error. Now it works well along with redirect and also with stored tokens.

-Aruna

Gary Moss

unread,
Oct 29, 2010, 10:36:06 AM10/29/10
to signpos...@googlegroups.com
Yeah the dreaded invisible space in the database bug, hate when that happens. :)

_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/_/
Reply all
Reply to author
Forward
0 new messages