package Catalyst::Authentication::Credential::OAuth;
use strict;
use warnings;

use Moose;

has _config => ( is => 'rw' );
has realm => ( is => 'ro' );
has debug => ( is => 'rw' );
has defaults => ( is => 'rw' );
has provider => ( is => 'rw' );

our $VERSION = "0.01";

use Net::OAuth;
#$Net::OAuth::PROTOCOL_VERSION = Net::OAuth::PROTOCOL_VERSION_1_0A;
use HTTP::Request::Common;
use LWP::UserAgent;
use String::Random qw/ random_string /;

use Catalyst::Exception ();

sub new {
	my ($class, $config, $c, $realm) = @_;

	my $self = { _config => { 
		%{ $config },
		%{ $realm->{config} }
	} };

	bless $self, $class;

	$self->debug( $self->_config->{debug} );

	$self->defaults( {
		request_method => 'GET',
		signature_method => 'HMAC-SHA1',
		nonce => random_string( 'ccccccccccccccccccc' ),
	} );

	return $self;
}


sub authenticate {
	my ($self, $c, $realm, $auth_info) = @_;
	
	Catalyst::Exception->throw( "Provider is not defined." ) 
		unless defined $self->_config->{ $auth_info->{oauth_provider} };

	$self->provider( $self->_config->{ $auth_info->{oauth_provider} } );

	$self->defaults( {
		%{ $self->defaults },
		timestamp => time,
		consumer_key => $self->provider->{key},
		consumer_secret => $self->provider->{secret},
		callback => $c->uri_for( $c->action )->as_string,
	} );

	$c->log_debug( "authenticate() called from " . $c->request->uri ) if $self->debug;

	my $ua = LWP::UserAgent->new;
	
	if( $c->req->params->{oauth_token} ) {

		my $response = Net::OAuth->response( 'user auth' )->from_hash( $c->req->params );

		Catalyst::Exception->throw( "access_token_endpoint not defined" )
			unless $self->provider->{access_token_endpoint};

		my $request = Net::OAuth->request( 'access token' )->new(
			%{ $self->defaults },
			token => $response->token,
			token_secret => '',
			request_url => $self->provider->{access_token_endpoint},
		);

		$request->sign;

		my $ua_response = $ua->request( GET $request->to_url );

		Catalyst::Exception->throw( $ua_response->status_line.' '.$ua_response->content )
			unless $ua_response->is_success;
		
		$response = Net::OAuth->response( 'access token' )->from_post_body( $ua_response->content );

		my $user = +{ 
			token => $response->token,
			token_secret => $response->token_secret,
			extra_params => $response->extra_params
		};

		my $user_obj = $realm->find_user( $user, $c );

		return $user_obj if ref $user_obj;

		$c->log->debug( 'Verified OAuth identity failed' ) if $self->debug;

		return;
	}
	else {

		Catalyst::Exception->throw( "request_token_endpoint not defined" )
			unless $self->provider->{request_token_endpoint};
		
		my $request = Net::OAuth->request( 'request token' )->new(
			%{ $self->defaults },
			request_url => $self->provider->{request_token_endpoint}
		);

		$request->sign;

		my $ua_response = $ua->request( GET $request->to_url );
				
		Catalyst::Exception->throw( $ua_response->status_line.' '.$ua_response->content )
			unless $ua_response->is_success;

		my $response = Net::OAuth->response( 'request token' )->from_post_body( $ua_response->content );

		Catalyst::Exception->throw( "user_auth_endpoint not defined" )
			unless $self->provider->{user_auth_endpoint};

		$request = Net::OAuth->request( 'user auth' )->new(
			%{ $self->defaults },
			token => $response->token,
		);

		$c->res->redirect( $request->to_url( $self->provider->{user_auth_endpoint} ) );
	}

}



1;


__END__

=head1 NAME

Catalyst::Authentication::Credential::OAuth - OAuth credential for Catalyst::Plugin::Authentication framework.

=head1 VERSION

0.01

=head1 SYNOPSIS

In MyApp.pm

 use Catalyst qw/
      Authentication
      Session
      Session::Store::FastMmap
      Session::State::Cookie
 /;


In myapp.conf

 <Plugin::Authentication>
      default_realm	oauth
      <realms>
           <oauth>
                <credential>
                     class	OAuth
		</credential>
		<example_provider>
			key my_app_key
			secret my_app_secret
			request_token_endpoint http://example.com/oauth/request_token
                        access_token_endpoint http://example.com/oauth/access_token
                        user_auth_endpoint http://example.com/oauth/authorize
		</example_provider>
           </oauth>
      </realms>
</Plugin::Authentication>


In controller code,

  sub oauth : Local {
       my ($self, $c) = @_;

       if( $c->authenticate( { oauth_provider => 'example_provider' } ) ) {
             #do something with $c->user
       }
  }



=head1 USER METHODS

=over 4

=item $c->user->token

=item $c->user->token_secret

=item $c->user->extra_params - whatever other parameters the provider sends back

=back

=head1 AUTHOR

Cosmin Budrica E<lt>cosmin@sinapticode.comE<gt>

Bogdan Lucaciu E<lt>bogdan@sinapticode.comE<gt>

=head1 COPYRIGHT

Copyright (c) 2009 Sinapticode. All rights reserved 

This program is free software; you can redistribute it and/or modify it under the same terms as Perl itself.

=cut
