Can't create a simple Patreon login to static web page (PHP, no composer)

I’ve been struggling with this for a few days. It seems that the authorization code is not being received in the URL parameters and Patreon redirects the user back to my patreon_login.php creating an infinite loop.

I have a page to protect called protected.php. it is straight HTML. I’ve included this bit of PHP at the top:

<?php
session_start();

// Check if the access token exists in the session
if (!isset($_SESSION['access_token'])) {
    // Redirect the user to the login page if the access token is not found
    header('Location: patreon_login.php');
    exit();
}

$access_token = $_SESSION['access_token'];

// Use the access token to make API requests or perform other necessary actions
// ...

?>

After this I’m using the following code for my patreon_login.php. error_log output was added for debugging:

<?php
session_start();

require_once 'API.php';
require_once 'OAuth.php';

$client_id = 'your_client_id'; //yes I had the correct values here for the live script!
$client_secret = 'your_client_secret';
$redirect_uri = 'https://mysite.com/protected.php'; //url changed

// Check if the authorization code is present in the URL parameters
if (isset($_GET['code'])) {
    $authorization_code = $_GET['code'];

    // Create an instance of the Patreon OAuth client
    $oauth_client = new Patreon\OAuth($client_id, $client_secret);

    try {
        // Exchange the authorization code for an access token
        $tokens = $oauth_client->get_tokens($authorization_code, $redirect_uri);

        if (isset($tokens['access_token'])) {
            // Store the access token in the session
            $_SESSION['access_token'] = $tokens['access_token'];

            // Redirect the user to the protected page
            header('Location: protected.php');
            exit();
        } else {
            // Handle the case when the access token is not received
            error_log('Access token not received: ' . print_r($tokens, true));
            // Redirect the user to an error page or display an error message
            header('Location: error.php'); 
            exit();
        }
    } catch (Exception $e) {
        // Handle any errors that occur during the token exchange process
        error_log('Error exchanging authorization code for access token: ' . $e->getMessage());
        // Redirect the user to an error page or display an error message
        header('Location: error.php');
        exit();
    }
}

// Check if the user is already authenticated
if (isset($_SESSION['access_token'])) {
    // Redirect the user to the protected page
    header('Location: surround.php');
    exit();
}

// Construct the authorization URL
$authorization_url = 'https://www.patreon.com/oauth2/authorize'
    . '?response_type=code'
    . '&client_id=' . urlencode($client_id)
    . '&redirect_uri=' . urlencode($redirect_uri);

// Redirect the user to the authorization URL
header('Location: ' . $authorization_url);
exit();
?>

from the error log the most likely candidate is:
[12-May-2024 10:25:36 America/Boise] patreon_login.php: Authorization code not found in URL parameters

I’ve confirmed that my redirect URI is the same as in the API.

Is there some permissions aspect to this API that I don’t see?

If there is anything at your site/infra that snips out request parameters, or, is there any redirect that happens before the redirected user ends up at the Patreon routing page, then this could happen.

Not as far as I know. There’s no back-end at all. Is there any way to test for something like this?

You can try to get the request parameters at the top of your page/script/file. There must be non-empty a _GET parameter with the name ‘code’. If the lib or whatever script you are using cannot get that param, you can end up with this issue. If there isnt such a param at that page, try to track from where the request passes until it ends up in the script. Ie, are there any redirects at the domain level? At the CDN level (cloudflare etc)? At the server level (any Apache, nginx rewrites, or outright in the server conf)? At the app/script level?

Any redirection that does not respect the request parameters and redirects without adding them to the target url would cause the code parameter to be snipped.

Thanks, I am using Cloudflare, but have been working in development mode, so hopefully that isn’t the issue. But, just in case, I added two page-rules that disable caching on patreon_login.php and protected.php

Using the Network tab in Chrome dev tools after clicking ‘allow’ in the Patreon window, I see the URL protected.php?code=asdfasdf…?state=None;

This seems to indicate that the Patreon redirect mechanism is working and sending the code to my server?

This is my latest code:

<?php
session_start();

require_once 'API.php';
require_once 'OAuth.php';

$client_id = 'your_client_id';
$client_secret = 'your_client_secret';
$redirect_uri = 'https://mysite.com/protected.php';

error_log('patreon_login.php: Script started');
error_log('patreon_login.php: $_GET parameters: ' . print_r($_GET, true));

// Check if the 'code' parameter exists in the $_GET superglobal
if (isset($_GET['code'])) {
    $authorization_code = $_GET['code'];
    error_log('patreon_login.php: Authorization code received: ' . $authorization_code);

    // Create an instance of the Patreon OAuth client
    $oauth_client = new Patreon\OAuth($client_id, $client_secret);

    try {
        // Exchange the authorization code for an access token
        $tokens = $oauth_client->get_tokens($authorization_code, $redirect_uri);

        error_log('patreon_login.php: Tokens received: ' . print_r($tokens, true));

        if (isset($tokens['access_token'])) {
            // Store the access token in the session
            $_SESSION['access_token'] = $tokens['access_token'];

            error_log('patreon_login.php: Access token stored in session: ' . $tokens['access_token']);

            // Redirect the user to the protected page
            header('Location: protected.php');
            exit();
        } else {
            error_log('patreon_login.php: Access token not received');
            echo 'Access token not received. Please try again.';
            exit();
        }
    } catch (Exception $e) {
        error_log('patreon_login.php: Error exchanging authorization code for access token: ' . $e->getMessage());
        echo 'An error occurred. Please try again.';
        exit();
    }
} else {
    error_log('patreon_login.php: Authorization code not found in $_GET parameters');

    // Check if the user is already authenticated
    if (isset($_SESSION['access_token'])) {
        error_log('patreon_login.php: User already authenticated, redirecting to protected.php');
        header('Location: surround.php');
        exit();
    }

    error_log('patreon_login.php: Redirecting to Patreon authorization URL');

    // Construct the authorization URL
    $authorization_url = 'https://www.patreon.com/oauth2/authorize'
        . '?response_type=code'
        . '&client_id=' . urlencode($client_id)
        . '&redirect_uri=' . urlencode($redirect_uri);

    // Redirect the user to the authorization URL
    header('Location: ' . $authorization_url);
    exit();
}
?>

UPDATE: With the help of AI (don’t judge!!) It was suggested that I create a 3rd file called patreon_redirect.php and to update the URI in the API to this.

<?php
error_log('patreon_redirect.php: Script started');
error_log('patreon_redirect.php: $_GET parameters: ' . print_r($_GET, true));

if (isset($_GET['code'])) {
    error_log('patreon_redirect.php: Authorization code received: ' . $_GET['code']);
    // Redirect to patreon_login.php with the code parameter
    header('Location: patreon_login.php?code=' . $_GET['code']);
    exit();
} else {
    error_log('patreon_redirect.php: Authorization code not found in $_GET parameters');
    echo 'Authorization code not found. Please try again.';
}
?>

Adding this file to patreon_login.php and the protected.php finally made it work. I really don’t understand what the reason for it is but I’m putting this here so maybe you can explain why and maybe it will help someone.

You seem to have 2 x ?s there. The extra ? will screw up the variables.

I just typed it wrong. The one before state was meant to be an &.

In that case, you need to figure out where the code= param is being snipped. A redirect is suspect, as I said before, but it can be anything else. (ie, the local stack/framework processing request params in a different way etc)