/
ImplicitGrant.pm
206 lines (154 loc) · 5.36 KB
/
ImplicitGrant.pm
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
package Net::OAuth2::AuthorizationServer::ImplicitGrant;
=head1 NAME
Net::OAuth2::AuthorizationServer::ImplicitGrant - OAuth2 Resource Owner Implicit Grant
You "SHOULD NOT" use this grant type (see L<https://tools.ietf.org/html/draft-ietf-oauth-security-topics-15>)
=head1 SYNOPSIS
my $Grant = Net::OAuth2::AuthorizationServer::ImplicitGrant->new(
clients => {
TrendyNewService => {
# optional
redirect_uri => 'https://...',
# optional
scopes => {
post_images => 1,
annoy_friends => 1,
},
},
}
);
# verify a client against known clients
my ( $is_valid,$error,$scopes ) = $Grant->verify_client(
client_id => $client_id,
redirect_uri => $uri, # optional
scopes => [ qw/ list of scopes / ], # optional
);
if ( ! $Grant->login_resource_owner ) {
# resource owner needs to login
...
}
# have resource owner confirm (and perhaps modify) scopes
my ( $confirmed,$error,$scopes_ref ) = $Grant->confirm_by_resource_owner(
client_id => $client_id,
scopes => [ qw/ list of scopes / ],
);
# generate a token
my $token = $Grant->token(
client_id => $client_id,
scopes => $scopes_ref,
redirect_uri => $redirect_uri,
user_id => $user_id, # optional
jwt_claims_cb => sub { ... }, # optional, see jwt_claims_cb in Manual
);
# store access token
$Grant->store_access_token(
client_id => $client,
access_token => $access_token,
scopes => $scopes_ref,
);
# verify an access token
my ( $is_valid,$error ) = $Grant->verify_access_token(
access_token => $access_token,
scopes => $scopes_ref,
);
=head1 DESCRIPTION
This module implements the OAuth2 "Resource Owner Implicit Grant" flow as described
at L<http://tools.ietf.org/html/rfc6749#section-4.2>.
=head1 CONSTRUCTOR ARGUMENTS
Along with those detailed at L<Net::OAuth2::AuthorizationServer::Manual/"CONSTRUCTOR ARGUMENTS">
the following are supported by this grant type:
=head1 CALLBACK FUNCTIONS
The following callbacks are supported by this grant type:
verify_client_cb
login_resource_owner_cb
confirm_by_resource_owner_cb
store_access_token_cb
verify_access_token_cb
Please see L<Net::OAuth2::AuthorizationServer::Manual/"CALLBACK FUNCTIONS"> for
documentation on each callback function.
=cut
use strict;
use warnings;
use Moo;
with 'Net::OAuth2::AuthorizationServer::Defaults';
use Carp qw/ croak /;
use Types::Standard qw/ :all /;
sub _uses_auth_codes { 0 };
sub _uses_user_passwords { 0 };
sub BUILD {
my ( $self, $args ) = @_;
if (
# if we don't have a list of clients
!$self->_has_clients
# we must know how to verify clients and tokens
and ( !$args->{ verify_client_cb }
and !$args->{ store_access_token_cb }
and !$args->{ verify_access_token_cb } )
)
{
croak __PACKAGE__ . " requires either clients or overrides";
}
}
sub _verify_client {
my ( $self, %args ) = @_;
my ( $client_id, $scopes_ref, $redirect_uri )
= @args{ qw/ client_id scopes redirect_uri / };
if ( my $client = $self->clients->{ $client_id } ) {
my $client_scopes = [];
foreach my $scope ( @{ $scopes_ref // [] } ) {
if ( ! exists($self->clients->{ $client_id }{ scopes }{ $scope }) ) {
return ( 0, 'invalid_scope' );
}
elsif ( $self->clients->{ $client_id }{ scopes }{ $scope } ) {
push @{$client_scopes}, $scope;
}
}
if (
# redirect_uri is optional
$self->clients->{ $client_id }{ redirect_uri }
&& (
! $redirect_uri
|| $redirect_uri ne $self->clients->{ $client_id }{ redirect_uri }
)
) {
return ( 0, 'invalid_request' );
}
if (
# implies Authorization Code Grant, not Implicit Grant
$self->clients->{ $client_id }{ client_secret }
) {
return ( 0, 'unauthorized_client' );
}
return ( 1, undef, $client_scopes );
}
return ( 0, 'unauthorized_client' );
}
sub _verify_access_token {
my ( $self, %args ) = @_;
delete( $args{is_refresh_token} ); # not supported by implicit grant
return $self->_verify_access_token_jwt( %args ) if $self->jwt_secret;
my ( $a_token, $scopes_ref ) =
@args{ qw/ access_token scopes / };
if ( exists( $self->access_tokens->{ $a_token } ) ) {
if ( $self->access_tokens->{ $a_token }{ expires } <= time ) {
$self->_revoke_access_token( $a_token );
return ( 0, 'invalid_grant' );
}
elsif ( $scopes_ref ) {
foreach my $scope ( @{ $scopes_ref // [] } ) {
return ( 0, 'invalid_grant' )
if !$self->_has_scope( $scope, $self->access_tokens->{ $a_token }{ scope } );
}
}
return ( $self->access_tokens->{ $a_token }{ client_id }, undef );
}
return ( 0, 'invalid_grant' );
}
=head1 AUTHOR
Lee Johnson - C<leejo@cpan.org>
=head1 LICENSE
This library is free software; you can redistribute it and/or modify it under
the same terms as Perl itself. If you would like to contribute documentation
or file a bug report then please raise an issue / pull request:
https://github.com/Humanstate/net-oauth2-authorizationserver
=cut
__PACKAGE__->meta->make_immutable;