2021-05-11 08:30:20 +02:00
< ? php
/**
* @ copyright Copyright ( C ) 2010 - 2021 , the Friendica project
*
* @ 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 08:32:24 +02:00
use Friendica\Security\OAuth ;
2021-05-11 08:30:20 +02:00
/**
2021-05-12 14:00:24 +02:00
* @ see https :// docs . joinmastodon . org / spec / oauth /
2021-05-13 13:26:56 +02:00
* @ see https :// aaronparecki . com / oauth - 2 - simplified /
2021-05-11 08:30:20 +02:00
*/
class Token extends BaseApi
{
2021-11-14 23:13:47 +01:00
public function post ()
2021-05-11 08:30:20 +02:00
{
2021-05-19 08:18:42 +02:00
$request = self :: getRequest ([
2021-06-16 21:24:44 +02: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-05-19 08:18:42 +02:00
]);
2021-05-11 08:30:20 +02:00
2021-05-14 00:00:40 +02:00
// AndStatus transmits the client data in the AUTHORIZATION header field, see https://github.com/andstatus/andstatus/issues/530
2021-06-16 21:39:51 +02:00
$authorization = $_SERVER [ 'HTTP_AUTHORIZATION' ] ? ? '' ;
if ( empty ( $authorization )) {
// workaround for HTTP-auth in CGI mode
$authorization = $_SERVER [ 'REDIRECT_REMOTE_USER' ] ? ? '' ;
}
2021-06-16 22:19:26 +02:00
if ( empty ( $request [ 'client_id' ]) && substr ( $authorization , 0 , 6 ) == 'Basic ' ) {
2021-06-16 21:39:51 +02:00
$datapair = explode ( ':' , base64_decode ( trim ( substr ( $authorization , 6 ))));
2021-05-14 00:00:40 +02:00
if ( count ( $datapair ) == 2 ) {
2021-05-19 08:18:42 +02:00
$request [ 'client_id' ] = $datapair [ 0 ];
$request [ 'client_secret' ] = $datapair [ 1 ];
2021-05-14 00:00:40 +02:00
}
}
2021-05-19 08:18:42 +02:00
if ( empty ( $request [ 'client_id' ]) || empty ( $request [ 'client_secret' ])) {
2021-05-13 13:26:56 +02:00
Logger :: warning ( 'Incomplete request data' , [ 'request' => $_REQUEST ]);
DI :: mstdnError () -> UnprocessableEntity ( DI :: l10n () -> t ( 'Incomplete request data' ));
2021-05-11 08:30:20 +02:00
}
2021-06-08 08:32:24 +02:00
$application = OAuth :: getApplication ( $request [ 'client_id' ], $request [ 'client_secret' ], $request [ 'redirect_uri' ]);
2021-05-13 13:26:56 +02:00
if ( empty ( $application )) {
DI :: mstdnError () -> UnprocessableEntity ();
2021-05-11 08:30:20 +02:00
}
2021-05-19 08:18:42 +02:00
if ( $request [ 'grant_type' ] == 'client_credentials' ) {
2021-05-14 08:05:01 +02: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 08:32:24 +02:00
$token = OAuth :: createTokenForUser ( $application , 0 , '' );
2021-05-19 08:18:42 +02:00
} elseif ( $request [ 'grant_type' ] == 'authorization_code' ) {
2021-05-14 08:05:01 +02:00
// For security reasons only allow freshly created tokens
$condition = [ " `redirect_uri` = ? AND `id` = ? AND `code` = ? AND `created_at` > UTC_TIMESTAMP() - INTERVAL ? MINUTE " ,
2021-05-19 08:18:42 +02:00
$request [ 'redirect_uri' ], $application [ 'id' ], $request [ 'code' ], 5 ];
2021-05-11 08:31:48 +02:00
2021-05-14 08:05:01 +02:00
$token = DBA :: selectFirst ( 'application-view' , [ 'access_token' , 'created_at' ], $condition );
if ( ! DBA :: isResult ( $token )) {
Logger :: warning ( 'Token not found or outdated' , $condition );
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 08:30:20 +02:00
}
2021-05-13 13:26:56 +02:00
$object = new \Friendica\Object\Api\Mastodon\Token ( $token [ 'access_token' ], 'Bearer' , $application [ 'scopes' ], $token [ 'created_at' ]);
System :: jsonExit ( $object -> toArray ());
2021-05-11 08:30:20 +02:00
}
}