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