Skip to content

Commit

Permalink
resolve #1 - implement Implicit Grant flow
Browse files Browse the repository at this point in the history
Implicit Grant is just a subset of the Authorization Code Grant flow
were, instead of using a client secret, we use an optional redirect
uri and skip the auth code dance

so most of the work here is adding tests to make sure we are doing
the right thing, actual library code added is very little.

the example oauth2_server_simple.pl has been updated to add a client
that supports implicit grant, here's an example of using it:

run the server:

	[leejohnson@MacBook-Pro-de-Lee J1 C10038 13:43:29 master]
	/Volumes/code_partition/net-oauth2-authorizationserver > morbo examples/oauth2_server_simple.pl
	Server available at http://127.0.0.1:3000

get an access token:

	[leejohnson@MacBook-Pro-de-Lee J0 C9996 13:38:13 leejo/implement_implicit_grant]
	*/Users/leejohnson/working/net-oauth2-authorizationserver > curl -XGET "http://127.0.0.1:3000/oauth/authorize?response_type=token&clien
	t_id=TrendyNewServiceImplicitGrant&redirect_uri=https://localhost/cb" -v
	*   Trying 127.0.0.1...
	* Connected to 127.0.0.1 (127.0.0.1) port 3000 (#0)
	> GET /oauth/authorize?response_type=token&client_id=TrendyNewServiceImplicitGrant&redirect_uri=https://localhost/cb HTTP/1.1
	> Host: 127.0.0.1:3000
	> User-Agent: curl/7.43.0
	> Accept: */*
	>
	< HTTP/1.1 302 Found
	< Content-Length: 0
	< Location: https://localhost/cb?access_token=MTQ3MjY0MzUwOS0zNTc3Mi0wLjAyNzczMzE3NjIzODU3NzEteUxFSml2VFJDN2dnbWVnMUMxS0tMRVIwVDF2YzI3&token_type=bearer&expires_in=3600
	< Server: Mojolicious (Perl)
	< Date: Wed, 31 Aug 2016 11:38:29 GMT
	<
	* Connection #0 to host 127.0.0.1 left intact

server logs show:

	[Wed Aug 31 13:43:36 2016] [debug] GET "/oauth/authorize"
	[Wed Aug 31 13:43:36 2016] [debug] Routing to a callback
	[Wed Aug 31 13:43:36 2016] [debug] OAuth2::Server: Resource owner is logged in
	[Wed Aug 31 13:43:36 2016] [debug] 302 Found (0.002632s, 379.939/s)

bump VERSION and Changes for CPAN release
  • Loading branch information
leejo committed Aug 31, 2016
1 parent 7b550be commit 8d7727f
Show file tree
Hide file tree
Showing 13 changed files with 588 additions and 17 deletions.
3 changes: 3 additions & 0 deletions Changes
@@ -1,5 +1,8 @@
Revision history for Net-OAuth2-AuthorizationServer

0.08 2016-08-31
- Add Net::OAuth2::AuthorizationServer::ImplicitGrant

0.07 2016-05-12
- Transfer repo from G3S to Humanstate

Expand Down
7 changes: 6 additions & 1 deletion MANIFEST
Expand Up @@ -14,6 +14,7 @@ examples/oauth2_server_simple_jwt.pl
lib/Net/OAuth2/AuthorizationServer.pm
lib/Net/OAuth2/AuthorizationServer/AuthorizationCodeGrant.pm
lib/Net/OAuth2/AuthorizationServer/Defaults.pm
lib/Net/OAuth2/AuthorizationServer/ImplicitGrant.pm
lib/Net/OAuth2/AuthorizationServer/Manual.pod
lib/Net/OAuth2/AuthorizationServer/PasswordGrant.pm
t/001_compiles_pod.t
Expand All @@ -23,8 +24,12 @@ t/net/oauth2/authorizationserver/authorizationcodegrant.t
t/net/oauth2/authorizationserver/authorizationcodegrant_legacy_args.t
t/net/oauth2/authorizationserver/authorizationcodegrant_no_jwt.t
t/net/oauth2/authorizationserver/authorizationcodegrant_tests.pm
t/net/oauth2/authorizationserver/defaults.t
t/net/oauth2/authorizationserver/implicitgrant.t
t/net/oauth2/authorizationserver/implicitgrant_legacy_args.t
t/net/oauth2/authorizationserver/implicitgrant_no_jwt.t
t/net/oauth2/authorizationserver/implicitgrant_tests.pm
t/net/oauth2/authorizationserver/passwordgrant.t
t/net/oauth2/authorizationserver/passwordgrant_legacy_args.t
t/net/oauth2/authorizationserver/passwordgrant_no_jwt.t
t/net/oauth2/authorizationserver/passwordgrant_tests.pm
t/net/oauth2/authorizationserver/defaults.t
6 changes: 4 additions & 2 deletions README.md
Expand Up @@ -11,7 +11,7 @@ Authorization Server

# VERSION

0.07
0.08

# SYNOPSIS

Expand Down Expand Up @@ -42,7 +42,9 @@ See [Net::OAuth2::AuthorizationServer::AuthorizationCodeGrant](https://metacpan.

## implicit\_grant

Not yet implemented.
OAuth Implicit Grant as document at [https://tools.ietf.org/html/rfc6749#section-4.2](https://tools.ietf.org/html/rfc6749#section-4.2).

See [Net::OAuth2::AuthorizationServer::ImplicitGrant](https://metacpan.org/pod/Net::OAuth2::AuthorizationServer::ImplicitGrant).

## password\_grant

Expand Down
7 changes: 7 additions & 0 deletions examples/oauth2_server_simple.pl
Expand Up @@ -14,6 +14,13 @@
"annoy_friends" => 1,
},
},
TrendyNewServiceImplicitGrant => {
redirect_uri => 'https://localhost/cb',
scopes => {
"post_images" => 1,
"annoy_friends" => 1,
},
},
},
};

Expand Down
20 changes: 15 additions & 5 deletions lib/Net/OAuth2/AuthorizationServer.pm
Expand Up @@ -11,7 +11,7 @@ Authorization Server
=head1 VERSION
0.07
0.08
=head1 SYNOPSIS
Expand Down Expand Up @@ -41,9 +41,10 @@ use Moo;
use Types::Standard qw/ :all /;

use Net::OAuth2::AuthorizationServer::AuthorizationCodeGrant;
use Net::OAuth2::AuthorizationServer::ImplicitGrant;
use Net::OAuth2::AuthorizationServer::PasswordGrant;

our $VERSION = '0.07';
our $VERSION = '0.08';

=head1 GRANT TYPES
Expand All @@ -57,12 +58,21 @@ See L<Net::OAuth2::AuthorizationServer::AuthorizationCodeGrant>.

sub auth_code_grant {
my ( $self, @args ) = @_;
return Net::OAuth2::AuthorizationServer::AuthorizationCodeGrant->new( @args, );
return Net::OAuth2::AuthorizationServer::AuthorizationCodeGrant->new( @args );
}

=head2 implicit_grant
Not yet implemented.
OAuth Implicit Grant as document at L<https://tools.ietf.org/html/rfc6749#section-4.2>.
See L<Net::OAuth2::AuthorizationServer::ImplicitGrant>.
=cut

sub implicit_grant {
my ( $self, @args ) = @_;
return Net::OAuth2::AuthorizationServer::ImplicitGrant->new( @args );
}

=head2 password_grant
Expand All @@ -74,7 +84,7 @@ See L<Net::OAuth2::AuthorizationServer::PasswordGrant>.

sub password_grant {
my ( $self, @args ) = @_;
return Net::OAuth2::AuthorizationServer::PasswordGrant->new( @args, );
return Net::OAuth2::AuthorizationServer::PasswordGrant->new( @args );
}

=head2 client_grant
Expand Down
Expand Up @@ -102,9 +102,9 @@ The validity period of the generated authorization code in seconds. Defaults to
The following callbacks are supported by this grant type:
verify_client_cb
login_resource_owner_cb
confirm_by_resource_owner_cb
verify_client_cb
store_auth_code_cb
verify_auth_code_cb
store_access_token_cb
Expand Down
19 changes: 11 additions & 8 deletions lib/Net/OAuth2/AuthorizationServer/Defaults.pm
Expand Up @@ -135,7 +135,7 @@ sub _delegate_to_cb_or_private {
for ( $method ) {

/login_resource_owner|confirm_by_resource_owner|verify_client/ && do {
return $cb->( $obj, @args{ qw/ client_id scopes / } );
return $cb->( $obj, @args{ qw/ client_id scopes redirect_uri / } );
};

$self->_uses_user_passwords && /verify_user_password/ && do {
Expand Down Expand Up @@ -240,16 +240,19 @@ sub _store_access_token {
$self->access_tokens->{ $access_token } = {
scope => $scope,
expires => time + $expires_in,
refresh_token => $refresh_token,
refresh_token => $refresh_token // undef,
client_id => $c_id,
};

$self->refresh_tokens->{ $refresh_token } = {
scope => $scope,
client_id => $c_id,
access_token => $access_token,
( $self->_uses_auth_codes ? ( auth_code => $auth_code ) : () ),
};
if ( $refresh_token ) {

$self->refresh_tokens->{ $refresh_token } = {
scope => $scope,
client_id => $c_id,
access_token => $access_token,
( $self->_uses_auth_codes ? ( auth_code => $auth_code ) : () ),
};
}

if ( $self->_uses_auth_codes ) {
$self->auth_codes->{ $auth_code }{ access_token } = $access_token;
Expand Down
195 changes: 195 additions & 0 deletions lib/Net/OAuth2/AuthorizationServer/ImplicitGrant.pm
@@ -0,0 +1,195 @@
package Net::OAuth2::AuthorizationServer::ImplicitGrant;

=head1 NAME
Net::OAuth2::AuthorizationServer::ImplicitGrant - OAuth2 Resource Owner Implicit Grant
=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 scopes
my $confirmed = $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 => [ qw/ list of scopes / ],
redirect_uri => $redirect_uri,
user_id => $user_id, # optional
);
# store access token
$Grant->store_access_token(
client_id => $client,
access_token => $access_token,
scopes => [ qw/ list of scopes / ],
);
# verify an access token
my ( $is_valid,$error ) = $Grant->verify_access_token(
access_token => $access_token,
scopes => [ qw/ list of scopes / ],
);
=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 } ) {

foreach my $scope ( @{ $scopes_ref // [] } ) {

if ( !exists( $self->clients->{ $client_id }{ scopes }{ $scope } ) ) {
return ( 0, 'invalid_scope' );
}
elsif ( !$self->clients->{ $client_id }{ scopes }{ $scope } ) {
return ( 0, 'access_denied' );
}
}

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' );
}

return ( 1 );
}

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;
7 changes: 7 additions & 0 deletions t/net/oauth2/authorizationserver.t
Expand Up @@ -35,4 +35,11 @@ isa_ok(
'Net::OAuth2::AuthorizationServer::PasswordGrant'
);

isa_ok(
$Grant = $Server->implicit_grant(
clients => { foo => {} },
),
'Net::OAuth2::AuthorizationServer::ImplicitGrant'
);

done_testing();

0 comments on commit 8d7727f

Please sign in to comment.