1515use App \Model \Repository \Users ;
1616use App \Model \Repository \Instances ;
1717use App \Helpers \EmailVerificationHelper ;
18+ use App \Helpers \FailureHelper ;
1819use Nette \Utils \Arrays ;
1920use Nette \Http \IResponse ;
2021use Firebase \JWT \JWT ;
@@ -42,6 +43,9 @@ class ExternalServiceAuthenticator
4243 /** @var EmailVerificationHelper */
4344 public $ emailVerificationHelper ;
4445
46+ /** @var FailureHelper */
47+ public $ failureHelper ;
48+
4549 /**
4650 * @var array [ name => { jwtSecret, expiration } ]
4751 */
@@ -61,22 +65,25 @@ public function __construct(
6165 Users $ users ,
6266 Logins $ logins ,
6367 Instances $ instances ,
64- EmailVerificationHelper $ emailVerificationHelper
68+ EmailVerificationHelper $ emailVerificationHelper ,
69+ FailureHelper $ failureHelper ,
6570 ) {
6671 $ this ->externalLogins = $ externalLogins ;
6772 $ this ->users = $ users ;
6873 $ this ->logins = $ logins ;
6974 $ this ->instances = $ instances ;
7075 $ this ->emailVerificationHelper = $ emailVerificationHelper ;
76+ $ this ->failureHelper = $ failureHelper ;
7177
7278 foreach ($ authenticators as $ auth ) {
7379 if (!empty ($ auth ['name ' ] && !empty ($ auth ['jwtSecret ' ]))) {
7480 $ this ->authenticators [$ auth ['name ' ]] = (object )[
7581 'jwtSecret ' => $ auth ['jwtSecret ' ],
7682 'expiration ' => Arrays::get ($ auth , 'expiration ' , 60 ),
7783 'defaultRole ' => Arrays::get ($ auth , 'defaultRole ' , null ),
78- 'usedAlgorithm ' => Arrays::get ($ auth , 'usedAlgorithm ' , 'HS256 ' ),
7984 // if set, users may register even when extrnal authenticator does not provide role
85+ 'usedAlgorithm ' => Arrays::get ($ auth , 'jwtAlgorithm ' , 'HS256 ' ),
86+ 'extraIds ' => Arrays::get ($ auth , 'extraIds ' , []),
8087 ];
8188 }
8289 }
@@ -144,6 +151,7 @@ public function authenticate(string $authName, string $token, string $instanceId
144151 }
145152 }
146153
154+ // failures throw exceptions...
147155 if ($ user === null ) {
148156 throw new WrongCredentialsException (
149157 "User authenticated through ' $ authName' has no corresponding account in ReCodEx. " ,
@@ -158,6 +166,7 @@ public function authenticate(string $authName, string $token, string $instanceId
158166 );
159167 }
160168
169+ $ this ->handleExtraIds ($ user , $ decodedToken , $ this ->authenticators [$ authName ]->extraIds );
161170
162171 return $ user ;
163172 }
@@ -237,4 +246,53 @@ private function getInstance($decodedToken, string $instanceId = null): ?Instanc
237246
238247 return null ;
239248 }
249+
250+ /**
251+ * Process possible additional (extra) identifiers present in the token.
252+ * @param User $user being authenticated
253+ * @param object $decodedToken
254+ * @param string[] $allowedServices whose extra IDs may be added from the token
255+ */
256+ private function handleExtraIds (User $ user , $ decodedToken , array $ allowedServices )
257+ {
258+ if (empty ($ decodedToken ->extId )) {
259+ return ;
260+ }
261+
262+ foreach ($ decodedToken ->extId as $ service => $ eid ) {
263+ if (!in_array ($ service , $ allowedServices )) {
264+ continue ; // skip services that are not allowed
265+ }
266+
267+ $ extUser = $ this ->externalLogins ->getUser ($ service , $ eid );
268+ if ($ extUser ) {
269+ if ($ extUser ->getId () !== $ user ->getId ()) {
270+ // Identity crysis! ID belongs to another user...
271+ $ this ->failureHelper ->report (
272+ FailureHelper::TYPE_API_ERROR ,
273+ sprintf (
274+ "User '%s' was provided with extra ID '%s' (%s), "
275+ . "but that is already associated with user '%s'. " ,
276+ $ user ->getId (),
277+ $ eid ,
278+ $ service ,
279+ $ extUser ->getId ()
280+ )
281+ );
282+ }
283+
284+ continue ; // either already exist or we cannot proceed anyway
285+ }
286+
287+ $ login = $ this ->externalLogins ->findByUser ($ user , $ service );
288+ if ($ login ->getExternalId () !== $ eid ) {
289+ // extra ID has changed (strange, but possible)
290+ $ login ->setExternalId ($ eid );
291+ $ this ->externalLogins ->persist ($ login );
292+ continue ;
293+ }
294+
295+ $ this ->externalLogins ->connect ($ service , $ user , $ eid );
296+ }
297+ }
240298}
0 commit comments