1

We are using Janrain in our app, but I have to create an interface to make this service easily replaced (because Janrain is so custom!, it does have common things in Oauth however)

However, I'm getting a hard time trying to achieve this.

What I don't want is to have, in my main UserManager service, a JanrainClientInterface, it should be neutral.

We need to use both Entity and Access APIs from Janrain:

interface OauthProviderInterface {

    public function getAccessToken(array $params);

    public function getAuthorizationCode(array $params);

    public function getCreationToken(array $params);

    public function getVerificationCode(array $params);

    public function useVerificationCode($verificationCode);

    public function authenticate(array $params);

    public function linkAccount(array $params);

    public function register(array $params); // registerNative in janrain API

    public function registerNativeTraditional(array $params);

    public function token(array $params);

    public function sendVerificationEmail(array $params);

    public function sendPasswordRecoveryEmail(array $params);
}

interface AuthenticationProviderInterface {

    public function authenticate($username, $password); // AuthNativeTraditional in Janrain API
}

These are more or less the API calls I will be using. I wonder if it makes sense what I'm trying to do, and how could I approach this.

  • what language is this, PHP? – gnat Mar 12 '18 at 14:00
  • @gnat yes it is PHP – JorgeeFG Mar 12 '18 at 14:01
  • You should as this on StackOverflow... You'll get more responses. – visc Mar 12 '18 at 14:15
  • 1
    @visc: It would have to be a better question than this. In its present form, it would be off-topic there. – Robert Harvey Mar 12 '18 at 16:12
  • I'm counting 12 functions in one interface. Are you telling me you have one object that uses every one of them? If not I'm going to throw the Interface Segregation Principle at you. If you do, well, do know what a God Object is? – candied_orange Mar 12 '18 at 19:40
2

I don't know if I did good, but in the end I came up with this:

These are Janrain specific interfaces:

interface JanrainAuthenticationInterface {

    public function getAccessToken(array $params);

    public function getAuthorizationCode(array $params);

    public function getCreationToken(array $params);

    public function getVerificationCode(array $params);

    public function useVerificationCode($verificationCode);

    public function authNative(array $params);

    public function authNativeTraditional(array $params);

    public function linkAccount(array $params);

    public function registerNative(array $params);

    public function registerNativeTraditional(array $params);

    public function token(array $params);

    public function verifyEmailNative($params);

    public function forgotPasswordNative($params);
}

interface JanrainEntityInterface {

    public function count($entityType, $filter = '');

    public function create(array $params);

    public function bulkCreate(array $params);

    public function delete($uuid, array $params = []);

    public function deleteById($id, array $params = []);

    public function deleteByAttribute($attributeKey, $attributeValue, array $params);

    public function bulkDelete(array $params);

    public function find(array $params);

    public function purge($entityType, $commit = false);

    public function replace($uuid, array $params);

    public function replaceById($id, array $params);

    public function replaceByAttribute($attributeKey, $attributeValue, array $params);

    public function update($uuid, array $params);

    public function updateById($id, array $params);

    public function updateByAttribute($attributeKey, $attributeValue, array $params);
}


interface JanrainClientInterface extends JanrainEntityInterface, JanrainAuthenticationInterface {

    public function getCurrentLocale();
    public function setCurrentLocale($localeName);
    public function getLocales();
}

These are provider agnostic interfaces:

interface AuthenticationProviderInterface {

    public function authenticate($username, $password);
}


interface OauthProviderInterface {

    public function getAccessToken(array $params);

    public function getAuthorizationCode(array $params);

    public function getCreationToken(array $params);

    public function getVerificationCode(array $params);

    public function useVerificationCode($verificationCode);

    public function authenticate(array $params);

    public function linkAccount(array $params);

    public function register(array $params);

    public function registerNativeTraditional(array $params);

    public function token(array $params);

}

interface UserEmailerInterface {

    public function sendVerificationEmail($userId, array $extraParams = []);

    public function sendPasswordRecoveryEmail($userId, array $extraParams = []);
}

interface UserProviderInterface {

    public function count($entityType, $filter = '');

    public function create(array $params);

    public function bulkCreate(array $params);

    public function delete($uuid, array $params = []);

    public function deleteById($id, array $params = []);

    public function deleteByAttribute($attributeKey, $attributeValue, array $params);

    public function bulkDelete(array $params);

    public function find(array $params);

    public function purge($entityType, $commit = false);

    public function replace($uuid, array $params);

    public function replaceById($id, array $params);

    public function replaceByAttribute($attributeKey, $attributeValue, array $params);

    public function update($uuid, array $params);

    public function updateById($id, array $params);

    public function updateByAttribute($attributeKey, $attributeValue, array $params);
}

Then there are four Janrain services implementing the provider agnostic interfaces, for example:

class JanrainAuthenticationProvider implements AuthenticationProviderInterface {

    protected $janrainClient;

    public function __construct(JanrainClientInterface $janrainClient) {
        $this->janrainClient = $janrainClient;
    }

    public function authenticate($username, $password) {
        $params = [
            'signInEmailAddress' => $username,
            'currentPassword' => $password,
        ];
        return $this->janrainClient->authNativeTraditional($params);
    }
}

Then JanrainClient implementing JanrainClientInterface.

What I do is, is some kind of facade in the previous four services, relaying all calls to JanrainClient, with little logic.

Then, UserManager (Application Service, describing use cases) has a constructor where I inject those four services using provider agnostic interfaces:

class UserManager {

    /** @var UserProviderInterface */
    protected $userProvider;
    /** @var AuthenticationProviderInterface */
    protected $authenticationProvider;
    /** @var OauthProviderInterface */
    protected $oauthProvider;
    /** @var UserEmailerInterface */
    protected $userEmailer;

    /**
     * UserManager constructor.
     * @param UserProviderInterface $userProvider
     * @param AuthenticationProviderInterface $authenticationProvider
     * @param OauthProviderInterface $oauthProvider
     * @param UserEmailerInterface $userEmailer
     */
    public function __construct(
        UserProviderInterface $userProvider,
        AuthenticationProviderInterface $authenticationProvider,
        OauthProviderInterface $oauthProvider,
        UserEmailerInterface $userEmailer
    ) {
        $this->userProvider = $userProvider;
        $this->authenticationProvider = $authenticationProvider;
        $this->oauthProvider = $oauthProvider;
        $this->userEmailer = $userEmailer;
    }


    public function getUserBy($criteria) {
        // TODO: Implement
    }

    public function doTraditionalLogin($username, $password) {
        $response = $this->authenticationProvider->authenticate($username, $password);
        return $response;
    }
}

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.