store->selectCollection(self::COLLECTION_NAME)->findOne([ 'tid' => $tenant, 'provider' => $provider, 'external_subject' => $externalSubject ]); if (!$entry) { return null; } return (array)$entry; } /** * Find all external identities for a user * * @param string $tenant Tenant identifier * @param string $userId Local user identifier * @return array List of external identity records */ public function findByUser(string $tenant, string $userId): array { $cursor = $this->store->selectCollection(self::COLLECTION_NAME)->find([ 'tid' => $tenant, 'uid' => $userId ]); $result = []; foreach ($cursor as $entry) { $result[] = (array)$entry; } return $result; } /** * Find external identity for a user from a specific provider * * @param string $tenant Tenant identifier * @param string $userId Local user identifier * @param string $provider Provider identifier * @return array|null External identity record or null */ public function findByUserAndProvider(string $tenant, string $userId, string $provider): ?array { $entry = $this->store->selectCollection(self::COLLECTION_NAME)->findOne([ 'tid' => $tenant, 'uid' => $userId, 'provider' => $provider ]); if (!$entry) { return null; } return (array)$entry; } /** * Link an external identity to a local user * * @param string $tenant Tenant identifier * @param string $userId Local user identifier * @param string $provider Provider identifier * @param string $externalSubject External subject identifier * @param array $attributes Optional attributes from provider * @return bool Whether the operation was successful */ public function linkIdentity( string $tenant, string $userId, string $provider, string $externalSubject, array $attributes = [] ): bool { $now = time(); // Use upsert to handle both create and update $result = $this->store->selectCollection(self::COLLECTION_NAME)->updateOne( [ 'tid' => $tenant, 'provider' => $provider, 'external_subject' => $externalSubject ], [ '$set' => [ 'uid' => $userId, 'attributes' => $attributes, 'last_login' => $now ], '$setOnInsert' => [ 'tid' => $tenant, 'provider' => $provider, 'external_subject' => $externalSubject, 'linked_at' => $now ] ], ['upsert' => true] ); return $result->isAcknowledged(); } /** * Unlink an external identity from a user * * @param string $tenant Tenant identifier * @param string $userId Local user identifier * @param string $provider Provider identifier * @return bool Whether the operation was successful */ public function unlinkIdentity(string $tenant, string $userId, string $provider): bool { $result = $this->store->selectCollection(self::COLLECTION_NAME)->deleteOne([ 'tid' => $tenant, 'uid' => $userId, 'provider' => $provider ]); return $result->isAcknowledged(); } /** * Update last login timestamp for an external identity * * @param string $tenant Tenant identifier * @param string $provider Provider identifier * @param string $externalSubject External subject identifier * @return bool Whether the operation was successful */ public function updateLastLogin(string $tenant, string $provider, string $externalSubject): bool { $result = $this->store->selectCollection(self::COLLECTION_NAME)->updateOne( [ 'tid' => $tenant, 'provider' => $provider, 'external_subject' => $externalSubject ], ['$set' => ['last_login' => time()]] ); return $result->isAcknowledged(); } /** * Update cached attributes for an external identity * * @param string $tenant Tenant identifier * @param string $provider Provider identifier * @param string $externalSubject External subject identifier * @param array $attributes New attributes to store * @return bool Whether the operation was successful */ public function updateAttributes(string $tenant, string $provider, string $externalSubject, array $attributes): bool { $result = $this->store->selectCollection(self::COLLECTION_NAME)->updateOne( [ 'tid' => $tenant, 'provider' => $provider, 'external_subject' => $externalSubject ], ['$set' => ['attributes' => $attributes]] ); return $result->isAcknowledged(); } /** * Delete all external identities for a user (used when deleting user) * * @param string $tenant Tenant identifier * @param string $userId Local user identifier * @return int Number of deleted records */ public function deleteAllForUser(string $tenant, string $userId): int { $result = $this->store->selectCollection(self::COLLECTION_NAME)->deleteMany([ 'tid' => $tenant, 'uid' => $userId ]); return $result->getDeletedCount(); } }