indieauth/index.php
2024-07-17 16:00:11 +01:00

133 lines
3.9 KiB
PHP

<?php
/* Single user IndieAuth endpoint using server envvars from SSO
* Based on https://github.com/inklings-io/selfauth
*/
require_once 'functions.php';
require_once 'config.php';
$error = null;
// Verification of codes.
$code = filter_input_regexp( INPUT_POST, 'code', '@^[0-9a-f]+:[0-9a-f]{64}:@' );
if ( ! is_null($code) ){
$redirect_uri = filter_input( INPUT_POST, 'redirect_uri', FILTER_VALIDATE_URL );
$client_id = filter_input( INPUT_POST, 'client_id', FILTER_VALIDATE_URL );
if ( ! (
is_string($code) &&
is_string($redirect_uri) &&
is_string($client_id) &&
verify_signed_code( APP_KEY, USER_URL . $redirect_uri . $client_id, $code )
) ) {
$error = 'Invalid code';
http_response_code(400);
include 'form.php';
die();
}
$response = array('me' => USER_URL);
$code_parts = explode(':', $code, 3);
$accept_header = $_SERVER['HTTP_ACCEPT'] ?: '*/*';
if ( '' !== $code_parts[2] ) {
$response['scope'] = base64_url_decode($code_parts[2]);
}
$json = get_q_value('application/json', $accept_header);
$form = get_q_value('application/x-www-form-urlencoded', $accept_header);
if ( 0 === $json && 0 === $form ){
$error = 'Client does not accept JSON or Form encoded responses';
http_response_code(406);
include 'form.php';
die();
} elseif ( $json >= $form ){
header('Content-Type: application/json');
exit( json_encode($response) );
} else {
header('Content-Type: application/x-www-form-urlencoded');
exit( http_build_query($response) );
}
}
// No code submitted,
// Check login
if ( is_null($_SERVER["REMOTE_USER"]) ) {
$error = 'Not logged in. Login on the <a href="https://auth.cool110.xyz/">SSO portal</a>';
http_response_code(403);
include 'form.php';
die();
} elseif ( USER_NAME !== $_SERVER["REMOTE_USER"] ){
$error = 'This system is for ' . USER_NAME . ' only.';
http_response_code(403);
include 'form.php';
die();
}
// Filter client data
$me = filter_input( INPUT_GET, 'me', FILTER_VALIDATE_URL );
$client_id = filter_input( INPUT_GET, 'client_id', FILTER_VALIDATE_URL );
$redirect_uri = filter_input( INPUT_GET, 'redirect_uri', FILTER_VALIDATE_URL );
$state = filter_input_regexp( INPUT_GET, 'state', '@^[\x20-\x7E]*$@' );
$response_type = filter_input_regexp( INPUT_GET, 'response_type', '@^(id|code)?$@' );
$scope = filter_input_regexp( INPUT_GET, 'scope', '@^([\x21\x23-\x5B\x5D-\x7E]+( [\x21\x23-\x5B\x5D-\x7E]+)*)?$@' );
if ( ! is_string($client_id) ) {
$error .= 'Invalid Client ID<br />';
}
if ( ! is_string($redirect_uri) ){
$error .= 'Invalid redirect URI<br />';
}
if ( false === $state ) {
$error .= 'Invalid state<br />';
}
if ( false === $response_type ) {
$error .= 'Invalid response type<br />';
}
if ( $error ){
http_response_code(400);
include 'form.php';
die();
}
$csrf_code = filter_input( INPUT_POST, '_csrf', FILTER_UNSAFE_RAW );
// If form submitted
if ( ! is_null($csrf_code) ) {
if ( !verify_signed_code( APP_KEY, $client_id . $redirect_uri . $state, $csrf_code ) ){
$error = 'Invalid CSFR code';
http_response_code(400);
include 'form.php';
die();
}
$scope = filter_input_regexp( INPUT_POST, 'scopes', '@^[\x21\x23-\x5B\x5D-\x7E]+$@', FILTER_REQUIRE_ARRAY );
if ( ! is_null($scope) ) {
if ( false === $scope || in_array( false, $scope, true ) ) {
$error = 'Provided scopes contain illegal characters';
}
$scope = implode( ' ', $scope );
}
$code = create_signed_code( APP_KEY, USER_URL . $redirect_uri . $client_id, 5 * 60, $scope );
$final_redir = $redirect_uri;
if ( strpos($redirect_uri, '?') === false ) {
$final_redir .= '?';
} else {
$final_redir .= '&';
}
$parameters = array(
'code' => $code,
'me' => USER_URL
);
if ( ! is_null($state) ) {
$parameters['state'] = $state;
}
$final_redir .= http_build_query($parameters);
header('Location: ' . $final_redir, true, 302);
} else {
$csrf_code = create_signed_code(APP_KEY, $client_id . $redirect_uri . $state, 2 * 60);
include 'form.php';
}