PersistingAuthenticator::authenticate()   A
last analyzed

Complexity

Conditions 6
Paths 24

Size

Total Lines 36
Code Lines 14

Duplication

Lines 0
Ratio 0 %

Code Coverage

Tests 19
CRAP Score 6

Importance

Changes 0
Metric Value
cc 6
eloc 14
nc 24
nop 1
dl 0
loc 36
ccs 19
cts 19
cp 1
crap 6
rs 9.2222
c 0
b 0
f 0
1
<?php
2
/**
3
 * @author Tharanga Kothalawala <[email protected]>
4
 * @date 30-12-2018
5
 *
6
 * This is similar to the Default version.
7
 * But this also stores the vendor token user details in a given storage by mapping the client application user record.
8
 * use this if you want to connect & use multiple vendor logins such as Facebook and/or Google.
9
 */
10
11
namespace TSK\SSO\Auth;
12
13
use TSK\SSO\AppUser\AppUser;
14
use TSK\SSO\AppUser\AppUserRepository;
15
use TSK\SSO\AppUser\ExistingAppUser;
16
use TSK\SSO\Auth\Exception\AuthenticationFailedException;
17
use TSK\SSO\Storage\Exception\DataCannotBeStoredException;
18
use TSK\SSO\Storage\FileSystemThirdPartyStorageRepository;
19
use TSK\SSO\Storage\ThirdPartyStorageRepository;
20
use TSK\SSO\ThirdParty\Exception\NoThirdPartyEmailFoundException;
21
use TSK\SSO\ThirdParty\Exception\ThirdPartyConnectionFailedException;
22
use TSK\SSO\ThirdParty\VendorConnection;
23
24
/**
25
 * @package TSK\SSO\Auth
26
 * @see AppUserAwarePersistingAuthenticator
27
 *
28
 * Use this to do a signup/signin via a third party vendor connection by persisting vendor data.
29
 * It is recommended to use this if you are planning to have more than one sso integration.
30
 */
31
class PersistingAuthenticator implements Authenticator
32
{
33
    /**
34
     * @var AppUserRepository
35
     */
36
    private $appUserRepository;
37
38
    /**
39
     * @var ThirdPartyStorageRepository
40
     */
41
    private $storageRepository;
42
43
    /**
44
     * @param AppUserRepository $appUserRepository client application specific user repository implementation to use
45
     *        to provision or validate users.
46
     * @param ThirdPartyStorageRepository $storageRepository a storage implementation to store the third party auth
47
     *        data. By default uses file system as the storage.
48
     */
49 4
    public function __construct(
50
        AppUserRepository $appUserRepository,
51
        ThirdPartyStorageRepository $storageRepository = null
52
    ) {
53 4
        $this->appUserRepository = $appUserRepository;
54 4
        $this->storageRepository = is_null($storageRepository)
55 4
            ? new FileSystemThirdPartyStorageRepository()
56
            : $storageRepository;
57 4
    }
58
59
    /**
60
     * This will try to authenticate a user using any given vendor connection.
61
     * Upon a successful attempt, returns the authenticated user.
62
     *
63
     * @param VendorConnection $thirdPartyConnection vendor connection to use to perform an auth
64
     * @return AppUser
65
     *
66
     * @throws AuthenticationFailedException
67
     * @throws DataCannotBeStoredException
68
     * @throws NoThirdPartyEmailFoundException
69
     * @throws ThirdPartyConnectionFailedException
70
     */
71 4
    public function authenticate(VendorConnection $thirdPartyConnection)
72
    {
73 4
        $accessToken = $thirdPartyConnection->grantNewAccessToken();
74
75 4
        $thirdPartyUser = $thirdPartyConnection->getSelf($accessToken);
76
77
        // a SIGN-IN attempt
78
        // check if this is a signin attempt with an existing user account
79 4
        $existingAppUser = $this->appUserRepository->getUser($thirdPartyUser->email());
80
81
        // if no user account found with the same vendor email as the app email,
82
        // do a mapping lookup in the storage across all vendors
83 4
        if (is_null($existingAppUser)) {
84 3
            $mappedUser = $this->storageRepository->getUser($thirdPartyUser->email());
85 3
            if (!is_null($mappedUser)) {
86 1
                $existingAppUser = new ExistingAppUser($mappedUser->appUserId(), $mappedUser->vendorEmail());
87 1
            }
88 3
        }
89
90
        // a SIGN-UP attempt
91
        // if no user found previously, let's create a new user as this seems like a signup attempt
92 4
        if (is_null($existingAppUser)) {
93 2
            $existingAppUser = $this->appUserRepository->create($thirdPartyUser);
94 2
        }
95
96
        // let's add/update the mapping of the newly created or the existing user's access token before we acknowledge
97 4
        if (!is_null($existingAppUser)) {
98 3
            $this->storageRepository->save($existingAppUser, $thirdPartyUser, $accessToken);
99 3
        }
100
101
        // throw error, if still the an app user cannot be resolved.
102 4
        if (is_null($existingAppUser)) {
103 1
            throw new AuthenticationFailedException('This user cannot be authenticated at this moment');
104
        }
105
106 3
        return $existingAppUser;
107
    }
108
}
109