@@ -, +, @@ --- Koha/OAuth.pm | 2 ++ Koha/REST/V1/OAuth.pm | 10 ++++-- api/v1/swagger/paths/oauth.json | 12 +++++++ t/db_dependent/api/v1/oauth.t | 79 +++++++++++++++++++++++++++++++++++++++++ 4 files changed, 101 insertions(+), 2 deletions(-) create mode 100755 t/db_dependent/api/v1/oauth.t --- a/Koha/OAuth.pm +++ a/Koha/OAuth.pm @@ -21,6 +21,8 @@ sub _verify_client_cb { my ($client_id, $scopes_ref, $client_secret) = @args{ qw/ client_id scopes client_secret / }; + return (0, 'unauthorized_client') unless $client_id; + my $clients = C4::Context->config('api_client'); $clients = [ $clients ] unless ref $clients eq 'ARRAY'; my ($client) = grep { $_->{client_id} eq $client_id } @$clients; --- a/Koha/REST/V1/OAuth.pm +++ a/Koha/REST/V1/OAuth.pm @@ -10,10 +10,16 @@ use C4::Context; sub token { my $c = shift->openapi->valid_input or return; - my $grant = Koha::OAuth->grant; + my $grant_type = $c->validation->param('grant_type'); + unless ($grant_type eq 'client_credentials') { + return $c->render(status => 400, openapi => {error => 'Unimplemented grant type'}); + } + my $client_id = $c->validation->param('client_id'); my $client_secret = $c->validation->param('client_secret'); - my $scope = [ split / /, $c->validation->param('scope') ]; + my $scope = [ split / /, ($c->validation->param('scope') // '') ]; + + my $grant = Koha::OAuth->grant; # verify a client against known clients my ( $is_valid, $error, $scopes ) = $grant->verify_client( --- a/api/v1/swagger/paths/oauth.json +++ a/api/v1/swagger/paths/oauth.json @@ -42,10 +42,22 @@ "properties": { "access_token": { "type": "string" + }, + "token_type": { + "type": "string" + }, + "expires_in": { + "type": "integer" } } } }, + "400": { + "description": "Bad Request", + "schema": { + "$ref": "../definitions.json#/error" + } + }, "403": { "description": "Access forbidden", "schema": { --- a/t/db_dependent/api/v1/oauth.t +++ a/t/db_dependent/api/v1/oauth.t @@ -0,0 +1,79 @@ +#!/usr/bin/env perl + +# This file is part of Koha. +# +# Koha is free software; you can redistribute it and/or modify it under the +# terms of the GNU General Public License as published by the Free Software +# Foundation; either version 3 of the License, or (at your option) any later +# version. +# +# Koha is distributed in the hope that it will be useful, but WITHOUT ANY +# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR +# A PARTICULAR PURPOSE. See the GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License along +# with Koha; if not, write to the Free Software Foundation, Inc., +# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. + +use Modern::Perl; + +use Test::More tests => 1; +use Test::Mojo; + +use Koha::Database; + +use t::lib::Mocks; + +my $t = Test::Mojo->new('Koha::REST::V1'); +my $schema = Koha::Database->new->schema; + +subtest '/oauth/token tests' => sub { + plan tests => 17; + + $schema->storage->txn_begin; + + # Missing parameter grant_type + $t->post_ok('/api/v1/oauth/token') + ->status_is(400); + + # Wrong grant type + $t->post_ok('/api/v1/oauth/token', form => { grant_type => 'password' }) + ->status_is(400) + ->json_is({error => 'Unimplemented grant type'}); + + # No client_id/client_secret + $t->post_ok('/api/v1/oauth/token', form => { grant_type => 'client_credentials' }) + ->status_is(403) + ->json_is({error => 'unauthorized_client'}); + + my ($client_id, $client_secret) = ('client1', 'secr3t'); + t::lib::Mocks::mock_config('api_client', { + 'client_id' => $client_id, + 'client_secret' => $client_secret, + 'scope' => ['patrons.read'], + }); + + my $formData = { + grant_type => 'client_credentials', + client_id => $client_id, + client_secret => $client_secret, + scope => 'patrons.read', + }; + $t->post_ok('/api/v1/oauth/token', form => $formData) + ->status_is(200) + ->json_is('/expires_in' => 3600) + ->json_is('/token_type' => 'Bearer') + ->json_has('/access_token'); + + my $access_token = $t->tx->res->json->{access_token}; + + # Without access token, it returns 401 + $t->get_ok('/api/v1/patrons')->status_is(401); + + # With access token, it returns 200 + my $tx = $t->ua->build_tx(GET => '/api/v1/patrons'); + $tx->req->headers->authorization("Bearer $access_token"); + $t->request_ok($tx)->status_is(200); + + $schema->storage->txn_rollback; +}; --