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; |