Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Impossible to authenticate on forked webservers #18

Closed
akarelas opened this issue May 19, 2014 · 14 comments
Closed

Impossible to authenticate on forked webservers #18

akarelas opened this issue May 19, 2014 · 14 comments

Comments

@akarelas
Copy link

I'm using the Mojolicious framework, with its Hypnotoad server.

When there's only one server process, authentication goes smoothly. When there are 10 server processes running, authentication fails. See below the code I wrote for the two twitter pages (initial page, and callback page):

sub _nt {
        state $nt = Net::Twitter::Lite::WithAPIv1_1->new(
                ssl => 1,
                %{ $pconf->{'twitter'} },
        );
}

sub twitter {
        my $self = shift;

        my $auth_url = _nt->get_authentication_url(callback => $self->url_for('twitter_callback')->to_abs->to_string);

        return $self->redirect_to($auth_url);
}

sub twitter2 {
        my $self = shift;

        my $request_token = $self->param('oauth_token');
        my $verifier = $self->param('oauth_verifier');

        _nt->request_access_token(verifier => $verifier);

        return $self->render(text => 'happy');
}

Both with 1 process and with 10 processes running, I am correctly redirected to twitter's auth url, where I click "Yes" and get redirected to my twitter2 (callback) page.

But whereas with 1 process I get the twitter2 page saying "happy", when there are 10 processes the twitter2 page execution dies, with this error:

Missing required parameter 'token' at /home/pm/perl5/lib/perl5/Net/Twitter/Lite.pm line 181.

This looks like it happens because Net::Twitter::Lite requires the visitor to revisit the same webserver process when he returns from Twitter.com's authentication page.

If that is the case, then it seems like a bug of Net::Twitter::Lite, because there is no way to direct the user to exactly the same process.

Am I doing something wrong here?

@akarelas
Copy link
Author

Just for the record, here's the full stacktrace of the error:

Trace begun at /usr/share/perl/5.18/Carp.pm line 100
Carp::croak('Missing required parameter \'token\'') called at /home/pm/perl5/lib/perl5/Net/OAuth/Message.pm line 88
Net::OAuth::Message::check('Net::OAuth::V1_0A::AccessTokenRequest=HASH(0x552dac0)') called at /home/pm/perl5/lib/perl5/Net/OAuth/Message.pm line 58
Net::OAuth::Message::new('Net::OAuth::AccessTokenRequest', 'version', 1.0, 'consumer_key', 'JvDjVqVicg7a1beGTe3MfxOrX', 'consumer_secret', 'zxki9scluDMuKa3mRrleTjxsX5rAcdmcrRlpo6KBPIt6HGvxRJ', 'request_method', 'GET', 'signature_method', 'HMAC-SHA1', 'timestamp', 1400480061, 'nonce', 2188694682, 'request_url', 'URI::https=SCALAR(0x50d33b0)', 'token', undef, 'token_secret', undef, 'verifier', 'C5oNCYEhwTxC6qsJMqAEnMIN1VyFIOY1UZJCT2xsm8') called at /home/pm/perl5/lib/perl5/Net/Twitter/Lite.pm line 181
Net::Twitter::Lite::_make_oauth_request('Net::Twitter::Lite::WithAPIv1_1=HASH(0x529e960)', 'access token', 'request_url', 'URI::https=SCALAR(0x50d33b0)', 'token', undef, 'token_secret', undef, 'verifier', 'C5oNCYEhwTxC6qsJMqAEnMIN1VyFIOY1UZJCT2xsm8') called at /home/pm/perl5/lib/perl5/Net/Twitter/Lite.pm line 226
Net::Twitter::Lite::request_access_token('Net::Twitter::Lite::WithAPIv1_1=HASH(0x529e960)', 'verifier', 'C5oNCYEhwTxC6qsJMqAEnMIN1VyFIOY1UZJCT2xsm8') called at /home/pm/web/perl/lib/PMNet/Page/Auth.pm line 100
PMNet::Page::Auth::twitter2('PMNet::Page::Auth=HASH(0x4fce500)') called at /home/pm/perl5/lib/perl5/Mojolicious.pm line 125
Mojolicious::__ANON__(undef, 'PMNet::Page::Auth=HASH(0x4fce500)', 'CODE(0x4feed40)', 1) called at /home/pm/perl5/lib/perl5/Mojolicious/Plugins.pm line 20
Mojolicious::Plugins::__ANON__ at /home/pm/web/perl/lib/PMNet.pm line 140
eval {...} at /home/pm/web/perl/lib/PMNet.pm line 126
PMNet::__ANON__('CODE(0x4fce9f8)', 'PMNet::Page::Auth=HASH(0x4fce500)', 'CODE(0x4feed40)', 1) called at /home/pm/perl5/lib/perl5/Mojolicious/Plugins.pm line 20
Mojolicious::Plugins::__ANON__ at /home/pm/perl5/lib/perl5/Mojolicious/Plugins.pm line 23
Mojolicious::Plugins::emit_chain('Mojolicious::Plugins=HASH(0x4d3a0a8)', 'around_action', 'PMNet::Page::Auth=HASH(0x4fce500)', 'CODE(0x4feed40)', 1) called at /home/pm/perl5/lib/perl5/Mojolicious/Routes.pm line 107
Mojolicious::Routes::_action('PMNet=HASH(0x2c90e90)', 'PMNet::Page::Auth=HASH(0x4fce500)', 'CODE(0x4feed40)', 1) called at /home/pm/perl5/lib/perl5/Mojolicious/Routes.pm line 195
Mojolicious::Routes::_controller('Mojolicious::Routes=HASH(0x4d39b50)', 'Mojolicious::Controller=HASH(0x4fc2570)', 'HASH(0x4fce2a8)', 1) called at /home/pm/perl5/lib/perl5/Mojolicious/Routes.pm line 44
Mojolicious::Routes::continue('Mojolicious::Routes=HASH(0x4d39b50)', 'Mojolicious::Controller=HASH(0x4fc2570)') called at /home/pm/perl5/lib/perl5/Mojolicious/Routes.pm line 52
Mojolicious::Routes::dispatch('Mojolicious::Routes=HASH(0x4d39b50)', 'Mojolicious::Controller=HASH(0x4fc2570)') called at /home/pm/perl5/lib/perl5/Mojolicious.pm line 117
Mojolicious::dispatch('PMNet=HASH(0x2c90e90)', 'Mojolicious::Controller=HASH(0x4fc2570)') called at /home/pm/perl5/lib/perl5/Mojolicious.pm line 126
Mojolicious::__ANON__(undef, 'Mojolicious::Controller=HASH(0x4fc2570)') called at /home/pm/perl5/lib/perl5/Mojolicious/Plugins.pm line 20
Mojolicious::Plugins::__ANON__ at /home/pm/perl5/lib/perl5/Mojolicious.pm line 198
eval {...} at /home/pm/perl5/lib/perl5/Mojolicious.pm line 198

@semifor
Copy link
Owner

semifor commented May 19, 2014

You need to re-seed the PRNG in each forked process. That's important not only for Net::Twitter::Lite, but for any module using the PRNG.

See perldoc -f srand.

@akarelas
Copy link
Author

It didn't work, sorry. I was already doing the srand trick to seed the PRNG on every process separately. I just tested my srand trick, and saw that it was done properly. Each process produces different random numbers right after the fork.

But I'm still getting the "Missing required parameter 'token'" error.

Is the code I showed above correct?

@akarelas
Copy link
Author

@akarelas
Copy link
Author

Actually, the video shows I have a different problem now than the one I had before:

GET https://api.twitter.com/oauth/access_token failed: 401 Unauthorized at /home/pm/perl5/lib/perl5/Net/Twitter/Lite.pm line 235.
Trace begun at /home/pm/perl5/lib/perl5/Net/Twitter/Lite.pm line 235
Net::Twitter::Lite::request_access_token('Net::Twitter::Lite::WithAPIv1_1=HASH(0x538ba00)', 'verifier', 'dMGRkPglTdL8w3Ko6yfpg72t71m1Aum0itY69ye9gvQ') called at /home/pm/web/perl/lib/PMNet/Page/Auth.pm line 99
PMNet::Page::Auth::twitter2('PMNet::Page::Auth=HASH(0x555cd50)') called at /home/pm/perl5/lib/perl5/Mojolicious.pm line 125
Mojolicious::__ANON__(undef, 'PMNet::Page::Auth=HASH(0x555cd50)', 'CODE(0x52d4178)', 1) called at /home/pm/perl5/lib/perl5/Mojolicious/Plugins.pm line 20
Mojolicious::Plugins::__ANON__ at /home/pm/web/perl/lib/PMNet.pm line 141
eval {...} at /home/pm/web/perl/lib/PMNet.pm line 127

Let me investigate further...

@akarelas
Copy link
Author

Now I get the same old error again (Missing required parameter 'token')

Does this line from the stacktrace seem correct to you?

Net::OAuth::Message::new('Net::OAuth::AccessTokenRequest', 'version', 1.0, 'consumer_key', 'JvDjVqVicg7a1beGTe3MfxOrX', 'consumer_secret', 'zxki9scluDMuKa3mRrleTjxsX5rAcdmcrRlpo6KBPIt6HGvxRJ', 'request_method', 'GET', 'signature_method', 'HMAC-SHA1', 'timestamp', 1400480061, 'nonce', 2188694682, 'request_url', 'URI::https=SCALAR(0x50d33b0)', 'token', undef, 'token_secret', undef, 'verifier', 'C5oNCYEhwTxC6qsJMqAEnMIN1VyFIOY1UZJCT2xsm8') called at /home/pm/perl5/lib/perl5/Net/Twitter/Lite.pm line 181

It's got 'token' and 'token_secret' undefined. Is that correct? Maybe those parameters should be completely missing?

@akarelas
Copy link
Author

I'll do some console tests when I get home.

@akarelas
Copy link
Author

The _nt subroutine calls new with only 3 parameters: ssl, consumer_key and consumer_secret. Is that right?

@akarelas
Copy link
Author

I ran a minimal simulation script in the console, and think that there has got to be a bug somewhere, either in N::T::L, or in one of its dependencies or Net::OAuth. Here's my script:

#!/usr/bin/perl

use v5.18;
use warnings;

use PConf::Private;

use Net::Twitter::Lite::WithAPIv1_1;

my $pconf = \%PConf::Private::conf;

sub _nt {
        return Net::Twitter::Lite::WithAPIv1_1->new(
                ssl => 1,
                %{ $pconf->{'twitter'} },
        );
}

say _nt->get_authentication_url(callback => 'http://local.perlmodules.net/');

print "\nEnter verifier: ";
chomp(my $verifier = <STDIN>);

_nt->request_access_token(verifier => $verifier);

I run this script, visit the produced URL with my browser, click on "Accept" when at the Twitter authentication screen, then see the "verifier" GET param in my browser's URL bar, copy & paste that into my script when asked.

The script above produces an error, because (just like the forking server) it does not use the exact same Net::Twitter::Lite object in both get_authentication_url and request_access_token. However, replacing _nt with the method below does NOT produce an error, because the script will now use the same Net::Twitter::Lite object between the two method calls:

sub _nt {
        state $nt = Net::Twitter::Lite::WithAPIv1_1->new(
                ssl => 1,
                %{ $pconf->{'twitter'} },
        );
}

(Replace %{ $pconf->{'twitter'} } with consumer_key & consumer_secret settings.

What do you think of that? Does the program behave in the same way on your computer? What does this mean? I have the freshest perl modules installed (cpanm -L ~/perl5 blabla), installed them just one or two days ago.

@semifor
Copy link
Owner

semifor commented May 19, 2014

After you call $nt->get_authentication_url, you need to save the values returned by $nt->request_token and $nt->request_token_secret in session data. Then, in your Twitter authentication callback handler, set them, before calling $nt->request_access_token.

@akarelas
Copy link
Author

Thank you very much, I'll try that. It's surprising though that your example here does not mention that: https://metacpan.org/source/MMIMS/Net-Twitter-Lite-0.12006/examples/oauth_webapp.pl

@akarelas
Copy link
Author

Also, since I don't have sessions for unauthenticated users, is it secure if I save the $nt->request_token and $nt->request_token_secret inside cookies in the user's browser? I'm not very familiar with the OAuth 1 protocol...

@akarelas
Copy link
Author

Scrap my last question, I found a better way - Twitter gives me the request token again, so I just save the token_secret server side, under key = $request_token

@akarelas
Copy link
Author

Thanks, that did the trick. I included token => ... and token_secret => ... parameters in the $nt->request_access_token(...) call. It was not mentioned in the documentation that I may place these parameters there. Neither is request_access_token mentioned at all in the API_1.1 documentation.

Before we close this ticket, I think it would be a good idea to save those parameters in the example as well (as browser cookies, or session data, or whatever), and also maybe mention that request_access_token can accept token and token_secret parameters?

Thanks a lot again.

@semifor semifor closed this as completed in 8fa21a8 Dec 5, 2016
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants