2021-05-11 06:30:20 +00:00
< ? php
/**
2022-01-02 07:27:47 +00:00
* @ copyright Copyright ( C ) 2010 - 2022 , the Friendica project
2021-05-11 06:30:20 +00:00
*
* @ license GNU AGPL version 3 or any later version
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU Affero General Public License as
* published by the Free Software Foundation , either version 3 of the
* License , or ( at your option ) any later version .
*
* This program 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 Affero General Public License for more details .
*
* You should have received a copy of the GNU Affero General Public License
* along with this program . If not , see < https :// www . gnu . org / licenses />.
*
*/
namespace Friendica\Module\OAuth ;
use Friendica\Core\Logger ;
use Friendica\Core\System ;
use Friendica\Database\DBA ;
use Friendica\DI ;
use Friendica\Module\BaseApi ;
2021-06-08 06:32:24 +00:00
use Friendica\Security\OAuth ;
2021-12-02 14:19:01 +00:00
use Friendica\Util\DateTimeFormat ;
2021-12-17 15:25:04 +00:00
use Psr\Http\Message\ResponseInterface ;
2021-05-11 06:30:20 +00:00
/**
2021-05-12 12:00:24 +00:00
* @ see https :// docs . joinmastodon . org / spec / oauth /
2021-05-13 11:26:56 +00:00
* @ see https :// aaronparecki . com / oauth - 2 - simplified /
2021-05-11 06:30:20 +00:00
*/
class Token extends BaseApi
{
2021-12-17 15:25:04 +00:00
public function run ( array $request = [], bool $scopecheck = true ) : ResponseInterface
{
return parent :: run ( $request , false );
}
2021-11-28 12:44:42 +00:00
protected function post ( array $request = [])
2021-05-11 06:30:20 +00:00
{
2021-11-28 12:22:27 +00:00
$request = $this -> getRequest ([
2021-06-16 19:24:44 +00:00
'client_id' => '' , // Client ID, obtained during app registration
'client_secret' => '' , // Client secret, obtained during app registration
'redirect_uri' => '' , // Set a URI to redirect the user to. If this parameter is set to "urn:ietf:wg:oauth:2.0:oob" then the token will be shown instead. Must match one of the redirect URIs declared during app registration.
'scope' => 'read' , // List of requested OAuth scopes, separated by spaces. Must be a subset of scopes declared during app registration. If not provided, defaults to "read".
'code' => '' , // A user authorization code, obtained via /oauth/authorize
'grant_type' => '' , // Set equal to "authorization_code" if code is provided in order to gain user-level access. Otherwise, set equal to "client_credentials" to obtain app-level access only.
2021-11-27 23:30:41 +00:00
], $request );
2021-05-11 06:30:20 +00:00
2021-05-13 22:00:40 +00:00
// AndStatus transmits the client data in the AUTHORIZATION header field, see https://github.com/andstatus/andstatus/issues/530
2021-06-16 19:39:51 +00:00
$authorization = $_SERVER [ 'HTTP_AUTHORIZATION' ] ? ? '' ;
if ( empty ( $authorization )) {
// workaround for HTTP-auth in CGI mode
$authorization = $_SERVER [ 'REDIRECT_REMOTE_USER' ] ? ? '' ;
}
2021-06-16 20:19:26 +00:00
if ( empty ( $request [ 'client_id' ]) && substr ( $authorization , 0 , 6 ) == 'Basic ' ) {
2021-06-16 19:39:51 +00:00
$datapair = explode ( ':' , base64_decode ( trim ( substr ( $authorization , 6 ))));
2021-05-13 22:00:40 +00:00
if ( count ( $datapair ) == 2 ) {
2021-05-19 06:18:42 +00:00
$request [ 'client_id' ] = $datapair [ 0 ];
$request [ 'client_secret' ] = $datapair [ 1 ];
2021-05-13 22:00:40 +00:00
}
}
2021-05-19 06:18:42 +00:00
if ( empty ( $request [ 'client_id' ]) || empty ( $request [ 'client_secret' ])) {
2021-05-13 11:26:56 +00:00
Logger :: warning ( 'Incomplete request data' , [ 'request' => $_REQUEST ]);
DI :: mstdnError () -> UnprocessableEntity ( DI :: l10n () -> t ( 'Incomplete request data' ));
2021-05-11 06:30:20 +00:00
}
2021-06-08 06:32:24 +00:00
$application = OAuth :: getApplication ( $request [ 'client_id' ], $request [ 'client_secret' ], $request [ 'redirect_uri' ]);
2021-05-13 11:26:56 +00:00
if ( empty ( $application )) {
DI :: mstdnError () -> UnprocessableEntity ();
2021-05-11 06:30:20 +00:00
}
2021-05-19 06:18:42 +00:00
if ( $request [ 'grant_type' ] == 'client_credentials' ) {
2021-05-14 06:05:01 +00:00
// the "client_credentials" are used as a token for the application itself.
// see https://aaronparecki.com/oauth-2-simplified/#client-credentials
2021-06-08 06:32:24 +00:00
$token = OAuth :: createTokenForUser ( $application , 0 , '' );
2021-05-19 06:18:42 +00:00
} elseif ( $request [ 'grant_type' ] == 'authorization_code' ) {
2021-05-14 06:05:01 +00:00
// For security reasons only allow freshly created tokens
2021-12-02 14:19:01 +00:00
$condition = [ " `redirect_uri` = ? AND `id` = ? AND `code` = ? AND `created_at` > ? " ,
$request [ 'redirect_uri' ], $application [ 'id' ], $request [ 'code' ], DateTimeFormat :: utc ( 'now - 5 minutes' )];
2021-05-11 06:31:48 +00:00
2021-05-14 06:05:01 +00:00
$token = DBA :: selectFirst ( 'application-view' , [ 'access_token' , 'created_at' ], $condition );
if ( ! DBA :: isResult ( $token )) {
2022-08-31 05:01:22 +00:00
Logger :: notice ( 'Token not found or outdated' , $condition );
2021-05-14 06:05:01 +00:00
DI :: mstdnError () -> Unauthorized ();
}
} else {
Logger :: warning ( 'Unsupported or missing grant type' , [ 'request' => $_REQUEST ]);
DI :: mstdnError () -> UnprocessableEntity ( DI :: l10n () -> t ( 'Unsupported or missing grant type' ));
2021-05-11 06:30:20 +00:00
}
2021-05-13 11:26:56 +00:00
$object = new \Friendica\Object\Api\Mastodon\Token ( $token [ 'access_token' ], 'Bearer' , $application [ 'scopes' ], $token [ 'created_at' ]);
System :: jsonExit ( $object -> toArray ());
2021-05-11 06:30:20 +00:00
}
}