File Coverage

blib/lib/Mojolicious/Plugin/Web/Auth/OAuth2.pm
Criterion Covered Total %
statement 15 74 20.2
branch 0 42 0.0
condition 0 6 0.0
subroutine 5 10 50.0
pod 0 2 0.0
total 20 134 14.9


line stmt bran cond sub pod time code
1             package Mojolicious::Plugin::Web::Auth::OAuth2;
2              
3 1     1   1309 use Mojo::Base 'Mojolicious::Plugin::Web::Auth::Base';
  1         2  
  1         7  
4 1     1   210 use Mojo::URL;
  1         2  
  1         7  
5 1     1   30 use Mojo::Parameters;
  1         2  
  1         6  
6 1     1   529 use Mojolicious::Types qw();
  1         943  
  1         26  
7 1     1   22 use Digest::SHA;
  1         2  
  1         1131  
8              
9             has 'scope';
10             has 'response_type';
11             has 'validate_state' => 1;
12             has 'state_generator';
13             has 'authorize_header';
14              
15             sub auth_uri {
16 0     0 0   my ( $self, $c, $callback_uri ) = @_;
17              
18 0 0         $callback_uri or die "Missing mandatory parameter: callback_uri";
19              
20 0           my $url = Mojo::URL->new( $self->authorize_url );
21 0           $url->query->param( client_id => $self->key );
22 0           $url->query->param( redirect_uri => $callback_uri );
23 0 0         $url->query->param( scope => $self->scope ) if ( defined $self->scope );
24 0 0         $url->query->param( response_type => $self->response_type ) if ( defined $self->response_type );
25              
26 0 0         if ( $self->validate_state ) {
27 0 0         my $state = $self->state_generator ? $self->state_generator->() : _state_generator();
28 0           $c->session->{oauth2_state} = $state;
29 0           $url->query->param( state => $state );
30             }
31              
32 0           return $url->to_string;
33             }
34              
35             sub callback {
36 0     0 0   my ($self, $c, $callback) = @_;
37              
38 0 0         if ( my $error = $c->req->param('error') ) {
39 0           my $error_description = $c->req->param('error_description');
40 0           return $callback->{on_error}->( $error, $error_description );
41             }
42 0 0         my $code = $c->param('code') or die "Cannot get a 'code' parameter";
43 0           my $forwarded_proto = $c->req->headers->header('x-forwarded-proto');
44 0 0 0       $c->req->url->base->scheme('https') if (defined $forwarded_proto && $forwarded_proto eq 'https');
45              
46 0 0         if ( $self->validate_state ) {
47 0           my $state = delete $c->session->{oauth2_state};
48 0 0         if ( $state ne $c->param('state') ) {
49 0           return $callback->{on_error}->('state validation failed.');
50             }
51             }
52              
53 0           my $params = +{
54             code => $code,
55             client_id => $self->key,
56             client_secret => $self->secret,
57             redirect_uri => $c->url_for->path( $c->req->url->path )->to_abs->to_string,
58             grant_type => 'authorization_code',
59             };
60              
61 0 0         my $tx = ( $Mojolicious::VERSION >= 3.85)
62             ? $self->_ua->post( $self->access_token_url => form => $params )
63             : $self->_ua->post_form( $self->access_token_url => $params ); # Mojo::UserAgent::post_form is deprecated from version 3.85
64              
65 0           my $res = $tx->res;
66 0 0         return $callback->{on_error}->( $res->body ) if $tx->error;
67              
68 0           my $dat = $self->_response_to_hash($res);
69 0 0         if ( my $err = delete $dat->{error} ) {
70 0           return $callback->{on_error}->($err);
71             }
72              
73             my $access_token = delete $dat->{access_token}
74 0 0         or die "Cannot get an access_token";
75 0           my @args = ($access_token);
76              
77 0 0         if ( $self->user_info ) {
78 0           my $url = Mojo::URL->new( $self->user_info_url );
79 0 0         $url->query->param( access_token => $access_token ) unless ( defined $self->authorize_header );
80 0 0         my $headers = defined $self->authorize_header
81             ? { 'Authorization' => $self->authorize_header.' '.$access_token }
82             : { };
83 0           my $tx = $self->_ua->get( $url->to_abs => $headers );
84 0           my $res = $tx->res;
85 0 0         return $callback->{on_error}->( sprintf( '%d %s', $res->code, $res->default_message ) )
86             if $tx->error;
87 0           push @args, $res->json;
88             } else {
89 0           push @args, undef;
90             }
91              
92 0           push @args, { %$dat }; # append rest of the response data as hashref
93              
94 0           return $callback->{on_finished}->(@args);
95             }
96              
97             sub _ua {
98 0     0     my $self = shift;
99              
100 0 0         unless ( $self->{_ua} ) {
101 0           $self->{_ua} = Mojo::UserAgent->new();
102              
103 0           my $user_agent = "Mojolicious::Plugin::Web::Auth/$Mojolicious::Plugin::Web::Auth::VERSION";
104 0 0         if ($Mojolicious::VERSION >= 4.50) {
105 0           $self->{_ua}->transactor->name($user_agent);
106 0           $self->{_ua}->proxy->detect; # supports ENV proxies
107             } else {
108             # Mojo::UserAgent#name is deprecated from version 4.50
109 0           $self->{_ua}->name($user_agent);
110             # Mojo::UserAgent#detect_proxy is deprecated from version 4.50
111 0           $self->{_ua}->detect_proxy();
112             }
113             }
114              
115 0           return $self->{_ua};
116             }
117              
118             sub _response_to_hash {
119 0     0     my ( $self, $res ) = @_;
120 0           my $types = Mojolicious::Types->new;
121 0           $types->type(json => ['application/json', 'text/javascript']);
122 0           my $exts = $types->detect( $res->headers->content_type );
123 0 0 0       return ( scalar(@$exts) && $exts->[0] eq 'json' )
124             ? $res->json
125             : Mojo::Parameters->new( $res->body )->to_hash;
126             }
127              
128             # default state param generator copy from Plack::Session::State
129             sub _state_generator {
130 0     0     Digest::SHA::sha1_hex(rand() . $$ . {} . time)
131             }
132              
133             1;