Multiple APIs using separate OAuth2 adapters with different token lifetimes

I have developed a few APIs with Apigility that use OAuth2 for authentication (authorization code grant type and password grant type workflows), and would like to have different (access and refresh) token lifetimes for each API, depending upon the API being called. I am using the latest version of Apigility 1.5.1.

Having looked through the way the OAuth2 adapter is built, I have come to the conclusion that you can’t have different lifetimes without having a separate OAuth2 adapter instance with it’s own configuration. However, the configuration array key that is used to configure OAuth2 adapters (zf-oath2) is hard-coded into \ZF\MvcAuth\Factory\NamedOAuth2ServerFactory (itself being an override to \ZF\OAuth2\Factory\OAuth2ServerFactory provided by the standard Apigility installation), and so does not appear to be set up for multiple configurations.

So far I have managed to make modifications in my own code so that I can have separate configuration values for another OAuth2 adapter module. (This entailed modifying the application configuration on a ModuleEvent::EVENT_MERGE_CONFIG listener in my own module, overriding \ZF\MvcAuth\Factory\NamedOAuth2ServerFactory with my own module’s version that looks for a new configuration array in advance of the hard-coded zf-oath2 key in \ZF\MvcAuth\Factory\NamedOAuth2ServerFactory:42 - the override being analogous to the auto-generated zf-mvc-auth-oauth2-override.global.php file but pointing to my own module instead of \ZF\MvcAuth\Factory\NamedOAuth2ServerFactory).

This appears to be working (as in different token lifetimes are seen in the database and response if the new adapter is used, and that I can make requests to API endpoints with tokens from the new and old OAuth2 adapter), but I’m finding that token lifetimes are not being consistently picked up from the relevant configuration.

At this point I am questioning the approach taken…

My questions are:

  • am I doing this particular change the right way (i.e right idea in principle, not quite executed correctly);
  • is this the right way to tackle the original idea (having different configuration for different APIs using the same authentication method - are multiple OAuth2 modules required or have I missed something);
  • is the original idea sensible?

Any guidance would be gratefully received!

The configuration read by the OAuth2ServerFactory is ‘zf-oauth2’ or nothing. You will need to create your own copy of the ZF\OAuth2\Factory\OAuth2ServerFactory functionality.

namespace ZF\OAuth2\Factory;
use Interop\Container\ContainerInterface;
class OAuth2ServerFactory  // GIVE IT A NEW NAME LOCAL TO YOUR APP
{
    public function __invoke(ContainerInterface $container)
    {
        $config = $container->get('config');
// CHANGE THE LINE BELOW TO A CUSTOM CONFIGURATION FOR EACH OAUTH2 SERVER
        $config = isset($config['zf-oauth2']) ? $config['zf-oauth2'] : [];
        return new OAuth2ServerInstanceFactory($config, $container);
    }

    public function createService($container)
    {
        return $this($container);
    }
}

I figured this out by reviewing my api-skeletons/zf-oauth2-doctrine code which uses individual configurations by default.

Thank you for your response @TomHAnderson!

From what I can see, the approach you suggest is already taken by the Apigility codebase by overriding the OAuth2ServerFactory with their own implementation that itself looks up the same hardcoded key zf-oauth; it is that overriding code that I’ve had to re-implement myself with and extra lookup for my own choice of configuration key. However, I’ll dig deeper into what you’ve suggested to see if I have misunderstood something.

From looking at the “Doctrine in Apigility” documentation you’ve written I’ve already seen that having multiple OAuth2 servers is a reasonable thing to do, which answers one part of my question at least.

Here’s the blog article about multiple oauth2: https://blog.tomhanderson.com/2015/08/using-zf-oauth2-doctrine-for-multiple.html

Thanks for the link :+1: - I’d read that as part of my research.