0

I implemented an Auth class in PHP responsible for authenticating with the (external domain) API, apart from my API class which is responsible for doing all the normal POST requests to this API.

The class Auth works as such:

class Auth {
    
    private $api;
    
    public function __construct() {
        $this->api = new API;
    }

    public function handleSession() {
        if (!isset($_SESSION)) {
            session_start();
        }
        
        if (!isset($_SESSION["ACCESS_TOKEN"])) {
            $_SESSION["REFERER"] = $_SERVER["PHP_SELF"];
        
            header("Location: auth.php");
            exit;
        } else {
            unset($_SESSION["REFERER"]);
        }
    }
}

It uses dependency injection with an API instance. There are other methods that access private API attributes through accessors, such $this->api->getAttr();.

At the start of every page the client accesses, I use the following at the start of the page to check if a token is established to proceed. If not, it will redirect to auth.php and start the authentication process.

$api_auth = new Auth();
$api_auth->handleSession();

However, I want to change it to the following cleaner way, so whenever I'm dealing with this api, I simply instantiate API and can access Auth from it, as such:

$api = new API();
$api->auth->handleSession();

To add an Auth instance in the constructor of API, so it would be more encapsulated, however I couldn't really think about how to achieve this, since Auth would still need to access API attributes to actually authenticate.

I tried adding arguments to Auth methods and also implement them in API:

class Auth {
    // ...
    public function makeRequest($id, $attr) {
        // make request to api
    }
    // ...
}
class API {
    // ...
    public function handleSession() {
        $this->auth->handleSession();
    }
    
    public function makeRequest() {
        $this->auth->makeRequest($this->id, $this->attr);
    }
    // ...
}

And it would be instantiated as such:

$api = new API;
$api->handleSession();

However this defeats the purpose of having an Auth class in the first place. So I ask you, how can I implement it as I could call handleSession as $api->auth->handleSession();?

James
  • 1
  • 6
  • You seem to be validating local users against the remote API, is that correct? Are you using the remote API for anything else? – Alex Howansky May 17 '23 at 15:17
  • Actually, this program works as an intermediator between the user and the remote API. This API has an auth endpoint that I communicate with to generate a valid token so the user will be available to call other endpoints of this API. The class API has the actual methods that call the other remote endpoints. – James May 17 '23 at 16:43
  • Does the API auth change per local user? Or are you just using session storage as a means to keep the token handy? In other words, could multiple local users share the same token? – Alex Howansky May 17 '23 at 16:45
  • It changes between users since each user has a different ID, hence it generates a different token (which also keeps refreshing every 6 hours). – James May 17 '23 at 16:48
  • And no, It would be invalid if another user used an already generated token. – James May 17 '23 at 17:08

1 Answers1

1

I don't see a need for the Auth class at all. Your current design works like this:

  • user without a token makes a request to a resource page (which needs a token)
  • the resource page detects that there's no token and redirects to auth.php
  • user makes another request, to auth.php
  • auth.php gets the token and stores it in the session
  • auth.php redirects the user back to the resource page
  • user (now with a token) makes a third request, to the resource page again
  • resource page makes an API hit to get the resource

All of this could be done on a single request:

  • user without a token makes a request to a resource page (which needs a token)
  • the resource page detects that there's no token, immediately gets one, stores it for future hits
  • the resource page makes an API hit to get the resource, using the stored token

So, I'd build transparent lazy auth right into the API class. By this, I mean:

"transparent" : The programmer using the API class shouldn't have to manage tokens, the class should do it automatically. I.e., if another person was using your API class, they shouldn't even know that tokens exist.

"lazy" : The API class should defer getting a token until it needs one.

class API
{
    // Note this is protected. It's only called within this class.
    protected function getToken(): string
    {
        if ($youHaveNoToken) {
            $token = getTokenFromApi();
            storeTokenSomewhere($token);
        }
        return getStoredToken();
    }

    public function getAttr()
    {
        $token = $this->getToken();
        // make API hit with token
    }
}

If you want PHP sessions to be your storage mechanism, you might do something like this:

    protected function getToken(): string
    {
        if (empty($_SESSION['token'])) {
            $token = // API hit to generate new token
            $_SESSION['token'] = $token;
        }
        return $_SESSION['token'];
    }

This now allows you to use any sort of storage for your tokens. You could use Redis or a database or a text file, and the consumer of the API never knows.

Alex Howansky
  • 50,515
  • 8
  • 78
  • 98
  • Thank you for your complete answer! Actually, I really don't need an Auth class. I implemented it in first place to separate different concerns regarding API. The API class has the `makeRequest` type of methods while Auth would solely care about generating a token. I saw I was trying to replicate inner classes behavior that is not supported by PHP. More information [here](https://stackoverflow.com/questions/16424257/nested-or-inner-class-in-php). I'll stick to the recommendations you gave me. – James May 17 '23 at 17:49