GithubHelp home page GithubHelp logo

Comments (3)

stloyd avatar stloyd commented on June 14, 2024 1

As pointed in #1964, the documentation could do better job, there is an entity provider already, you can setup it like:

services:
    hwi_oauth.user.provider.entity:
        class: HWI\Bundle\OAuthBundle\Security\Core\User\EntityUserProvider
        arguments:
            $class: App\Entity\User
            $properties:
                'facebook': 'facebook'
                'google': 'google'
# security.yaml
security:
    firewalls:
        main:
            # ...
            oauth:
                #...
                oauth_user_provider:
                    service: hwi_oauth.user.provider.entity

from hwioauthbundle.

maciekpaprocki avatar maciekpaprocki commented on June 14, 2024

I agree. I am struggling to find documentation on how to actually do that. Seems like fosuserbundle is really not needed anymore.

from hwioauthbundle.

maciekpaprocki avatar maciekpaprocki commented on June 14, 2024

Actually, i implemented that, but there's only basic testing on my side for now.

Entity

<?php

namespace App\Entity\User;

use App\Repository\User\UserOAuthRepository;
use Doctrine\ORM\Mapping as ORM;

#[ORM\Entity(repositoryClass: UserOAuthRepository::class)]
class UserOAuth
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\Column(length: 40)]
    private ?string $provider = null;

    #[ORM\Column(length: 255)]
    private ?string $identifier = null;

    #[ORM\Column(length: 255, nullable: true)]
    private ?string $accessToken = null;

    #[ORM\Column(length: 255, nullable: true)]
    private ?string $refreshToken = null;

    #[ORM\ManyToOne(inversedBy: 'userAuths')]
    #[ORM\JoinColumn(nullable: false)]
    private ?User $user = null;

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getProvider(): ?string
    {
        return $this->provider;
    }

    public function setProvider(string $provider): static
    {
        $this->provider = $provider;

        return $this;
    }

    public function getIdentifier(): ?string
    {
        return $this->identifier;
    }

    public function setIdentifier(?string $identifier): static
    {
        $this->identifier = $identifier;

        return $this;
    }

    public function getAccessToken(): ?string
    {
        return $this->accessToken;
    }

    public function setAccessToken(?string $accessToken): static
    {
        $this->accessToken = $accessToken;

        return $this;
    }

    public function getRefreshToken(): ?string
    {
        return $this->refreshToken;
    }

    public function setRefreshToken(?string $refreshToken): static
    {
        $this->refreshToken = $refreshToken;

        return $this;
    }

    public function getUser(): ?User
    {
        return $this->user;
    }

    public function setUser(?User $user): static
    {
        $this->user = $user;

        return $this;
    }
}
<?php

declare(strict_types=1);

namespace App\Security;

use App\Entity\User\User;
use App\Entity\User\UserOAuth;
use App\Repository\User\UserOAuthRepository;
use App\Repository\User\UserRepository;
use BadMethodCallException;
use Doctrine\ORM\EntityManagerInterface;
use HWI\Bundle\OAuthBundle\Connect\AccountConnectorInterface;
use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface;
use HWI\Bundle\OAuthBundle\Security\Core\User\OAuthAwareUserProviderInterface;
use Symfony\Component\Security\Core\User\UserInterface;

use Webmozart\Assert\Assert;
use function sha1;
use function substr;


class OAuthUserProvider implements AccountConnectorInterface, OAuthAwareUserProviderInterface
{
    public function __construct(
        private UserRepository         $userRepository,
        private UserOAuthRepository    $userAuthRepository,
        private EntityManagerInterface $em,
    )
    {
    }

    public function connect(UserInterface $user, UserResponseInterface $response): void
    {
        $this->updateUserByOAuthUserResponse($user, $response);
    }

    public function loadUserByOAuthUserResponse(UserResponseInterface $response)
    {

        $oauth = $this->userAuthRepository->findOneBy([
            'provider' => $response->getResourceOwner()->getName(),
            'identifier' => $response->getUsername(),
        ]);

        if ($oauth instanceof UserOAuth) {
            return $oauth->getUser();
        }

        if (null !== $response->getEmail()) {
            $user = $this->userRepository->findOneByEmail($response->getEmail());
            if (null !== $user) {
                return $this->updateUserByOAuthUserResponse($user, $response);
            }

            return $this->createUserByOAuthUserResponse($response);
        }

        throw new BadMethodCallException('Email is null or not provided');

    }

    private function createUserByOAuthUserResponse(UserResponseInterface $response): User
    {

        $user = new User();


        $user->setEmail($response->getEmail());
        $user->setPassword(substr(sha1($response->getAccessToken()), 0, 20));

        return $this->updateUserByOAuthUserResponse($user, $response);
    }


    private function updateUserByOAuthUserResponse(UserInterface $user, UserResponseInterface $response): User
    {
        /** @var User $user */
        Assert::isInstanceOf($user, User::class);


        $oauth = new UserOAuth();
        $oauth->setIdentifier($response->getUsername());
        $oauth->setProvider($response->getResourceOwner()->getName());
        $oauth->setAccessToken($response->getAccessToken());
        $oauth->setRefreshToken($response->getRefreshToken());

        $user->addUserOAuth($oauth);
        $this->em->persist($user);
        $this->em->persist($oauth);
        $this->em->flush();

        return $user;
    }
}

And original User entity with some extra fields


<?php

namespace App\Entity\User;

use App\Entity\Product\Product;
use App\Repository\User\UserRepository;
use Doctrine\Common\Collections\ArrayCollection;
use Doctrine\Common\Collections\Collection;
use Doctrine\ORM\Mapping as ORM;
use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity;
use Symfony\Component\Security\Core\User\PasswordAuthenticatedUserInterface;
use Symfony\Component\Security\Core\User\UserInterface;

#[ORM\Entity(repositoryClass: UserRepository::class)]
#[ORM\Table(name: '`user`')]
#[UniqueEntity(fields: ['email'], message: 'There is already an account with this email')]
class User implements UserInterface, PasswordAuthenticatedUserInterface
{
    #[ORM\Id]
    #[ORM\GeneratedValue]
    #[ORM\Column]
    private ?int $id = null;

    #[ORM\Column(length: 180, unique: true)]
    private ?string $email = null;

    #[ORM\Column]
    private array $roles = [];

    #[ORM\Column]
    private ?string $password = null;

    #[ORM\OneToMany(mappedBy: 'seller', targetEntity: Product::class, orphanRemoval: true)]
    private Collection $products;

    #[ORM\Column(type: 'boolean')]
    private $isVerified = false;

    #[ORM\ManyToMany(targetEntity: Product::class)]
    private Collection $basket;


    #[ORM\JoinTable(name: 'user_likes')]
    #[ORM\ManyToMany(targetEntity: Product::class, inversedBy: 'likingUsers')]
    private Collection $likes;

    #[ORM\OneToMany(mappedBy: 'user', targetEntity: UserOAuth::class)]
    private Collection $userOAuths;

    public function __construct()
    {
        $this->products = new ArrayCollection();
        $this->basket = new ArrayCollection();
        $this->likes = new ArrayCollection();
        $this->userOAuths = new ArrayCollection();
    }

    public function getId(): ?int
    {
        return $this->id;
    }

    public function getEmail(): ?string
    {
        return $this->email;
    }

    public function setEmail(string $email): static
    {
        $this->email = $email;

        return $this;
    }

    /**
     * A visual identifier that represents this user.
     *
     * @see UserInterface
     */
    public function getUserIdentifier(): string
    {
        return (string) $this->email;
    }

    /**
     * @see UserInterface
     */
    public function getRoles(): array
    {
        $roles = $this->roles;
        // guarantee every user at least has ROLE_USER
        $roles[] = 'ROLE_USER';

        return array_unique($roles);
    }

    public function setRoles(array $roles): static
    {
        $this->roles = $roles;

        return $this;
    }

    /**
     * @see PasswordAuthenticatedUserInterface
     */
    public function getPassword(): string
    {
        return $this->password;
    }

    public function setPassword(string $password): static
    {
        $this->password = $password;

        return $this;
    }

    /**
     * @see UserInterface
     */
    public function eraseCredentials(): void
    {
        // If you store any temporary, sensitive data on the user, clear it here
        // $this->plainPassword = null;
    }

    /**
     * @return Collection<int, Product>
     */
    public function getProducts(): Collection
    {
        return $this->products;
    }

    public function addProduct(Product $product): static
    {
        if (!$this->products->contains($product)) {
            $this->products->add($product);
            $product->setSeller($this);
        }

        return $this;
    }

    public function removeProduct(Product $product): static
    {
        if ($this->products->removeElement($product)) {
            // set the owning side to null (unless already changed)
            if ($product->getSeller() === $this) {
                $product->setSeller(null);
            }
        }

        return $this;
    }

    public function isVerified(): bool
    {
        return $this->isVerified;
    }

    public function setIsVerified(bool $isVerified): static
    {
        $this->isVerified = $isVerified;

        return $this;
    }

    /**
     * @return Collection<int, Product>
     */
    public function getBasket(): Collection
    {
        return $this->basket;
    }

    public function addBasket(Product $basket): static
    {
        if (!$this->basket->contains($basket)) {
            $this->basket->add($basket);
        }

        return $this;
    }

    public function removeBasket(Product $basket): static
    {
        $this->basket->removeElement($basket);

        return $this;
    }

    /**
     * @return Collection<int, Product>
     */
    public function getLikes(): Collection
    {
        return $this->likes;
    }

    public function addLike(Product $like): static
    {
        if (!$this->likes->contains($like)) {
            $this->likes->add($like);
        }

        return $this;
    }

    public function removeLike(Product $like): static
    {
        $this->likes->removeElement($like);

        return $this;
    }

    /**
     * @return Collection<int, UserOAuth>
     */
    public function getUserOAuths(): Collection
    {
        return $this->userOAuths;
    }

    public function addUserOAuth(UserOAuth $userAuth): static
    {
        if (!$this->userOAuths->contains($userAuth)) {
            $this->userOAuths->add($userAuth);
            $userAuth->setUser($this);
        }

        return $this;
    }

    public function removeUserOAuth(UserOAuth $userAuth): static
    {
        if ($this->userOAuths->removeElement($userAuth)) {
            // set the owning side to null (unless already changed)
            if ($userAuth->getUser() === $this) {
                $userAuth->setUser(null);
            }
        }

        return $this;
    }
}

Seems to be working for now. Will see once things start expiring :)

Please don't consider that production code. I am not sure what I am really doing.

from hwioauthbundle.

Related Issues (20)

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.