2021-05-11 08:30:20 +02:00
< ? php
/**
2023-01-01 15:36:24 +01:00
* @ copyright Copyright ( C ) 2010 - 2023 , the Friendica project
2021-05-11 08:30:20 +02: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\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 Authorize extends BaseApi
{
2021-06-10 09:02:06 +02:00
private static $oauth_code = '' ;
2021-05-11 08:30:20 +02:00
/**
* @ throws \Friendica\Network\HTTPException\InternalServerErrorException
*/
2021-11-20 15:38:03 +01:00
protected function rawContent ( array $request = [])
2021-05-11 08:30:20 +02:00
{
2021-11-28 13:22:27 +01:00
$request = $this -> getRequest ([
2021-06-16 21:24:44 +02:00
'force_login' => '' , // Forces the user to re-login, which is necessary for authorizing with multiple accounts from the same instance.
'response_type' => '' , // Should be set equal to "code".
'client_id' => '' , // Client ID, obtained during app registration.
2021-05-19 08:18:42 +02:00
'client_secret' => '' , // Isn't normally provided. We will use it if present.
2021-06-16 21:24:44 +02:00
'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 authorization code 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 (or by pluses, if using query parameters). Must be a subset of scopes declared during app registration. If not provided, defaults to "read".
2021-05-19 08:18:42 +02:00
'state' => '' ,
2021-11-28 00:30:41 +01:00
], $request );
2021-05-13 13:26:56 +02:00
2021-05-19 08:18:42 +02:00
if ( $request [ 'response_type' ] != 'code' ) {
2021-05-13 13:26:56 +02:00
Logger :: warning ( 'Unsupported or missing response type' , [ 'request' => $_REQUEST ]);
DI :: mstdnError () -> UnprocessableEntity ( DI :: l10n () -> t ( 'Unsupported or missing response type' ));
2021-05-11 08:30:20 +02:00
}
2021-05-19 08:18:42 +02:00
if ( empty ( $request [ 'client_id' ]) || empty ( $request [ 'redirect_uri' ])) {
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-06-08 08:32:24 +02:00
$application = OAuth :: getApplication ( $request [ 'client_id' ], $request [ 'client_secret' ], $request [ 'redirect_uri' ]);
2021-05-11 08:30:20 +02:00
if ( empty ( $application )) {
2021-05-12 16:00:15 +02:00
DI :: mstdnError () -> UnprocessableEntity ();
2021-05-11 08:30:20 +02:00
}
2021-05-13 13:26:56 +02:00
// @todo Compare the application scope and requested scope
2021-05-22 17:41:25 +02:00
$redirect_request = $_REQUEST ;
unset ( $redirect_request [ 'pagename' ]);
$redirect = 'oauth/authorize?' . http_build_query ( $redirect_request );
2021-05-12 08:50:27 +02:00
2022-10-20 22:59:12 +02:00
$uid = DI :: userSession () -> getLocalUserId ();
2021-05-11 08:30:20 +02:00
if ( empty ( $uid )) {
Logger :: info ( 'Redirect to login' );
2021-05-12 14:19:15 +02:00
DI :: app () -> redirect ( 'login?return_path=' . urlencode ( $redirect ));
2021-05-11 08:30:20 +02:00
} else {
Logger :: info ( 'Already logged in user' , [ 'uid' => $uid ]);
}
2021-06-08 08:32:24 +02:00
if ( ! OAuth :: existsTokenForUser ( $application , $uid ) && ! DI :: session () -> get ( 'oauth_acknowledge' )) {
2021-05-12 08:50:27 +02:00
Logger :: info ( 'Redirect to acknowledge' );
2021-05-12 14:19:15 +02:00
DI :: app () -> redirect ( 'oauth/acknowledge?' . http_build_query ([ 'return_path' => $redirect , 'application' => $application [ 'name' ]]));
2021-05-12 08:50:27 +02:00
}
2021-05-12 08:51:59 +02:00
DI :: session () -> remove ( 'oauth_acknowledge' );
2021-06-08 08:32:24 +02:00
$token = OAuth :: createTokenForUser ( $application , $uid , $request [ 'scope' ]);
2021-05-11 08:30:20 +02:00
if ( ! $token ) {
2021-05-12 16:00:15 +02:00
DI :: mstdnError () -> UnprocessableEntity ();
2021-05-11 08:30:20 +02:00
}
2021-06-10 08:26:34 +02:00
if ( $application [ 'redirect_uri' ] != 'urn:ietf:wg:oauth:2.0:oob' ) {
DI :: app () -> redirect ( $application [ 'redirect_uri' ] . ( strpos ( $application [ 'redirect_uri' ], '?' ) ? '&' : '?' ) . http_build_query ([ 'code' => $token [ 'code' ], 'state' => $request [ 'state' ]]));
}
2021-06-10 09:02:06 +02:00
self :: $oauth_code = $token [ 'code' ];
2021-06-10 08:26:34 +02:00
}
2021-11-20 15:38:03 +01:00
protected function content ( array $request = []) : string
2021-06-10 08:26:34 +02:00
{
2021-06-10 09:02:06 +02:00
if ( empty ( self :: $oauth_code )) {
2021-06-10 08:26:34 +02:00
return '' ;
}
2021-06-10 09:02:06 +02:00
return DI :: l10n () -> t ( 'Please copy the following authentication code into your application and close this window: %s' , self :: $oauth_code );
2021-05-11 08:30:20 +02:00
}
}