Comments (3)
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.
I agree. I am struggling to find documentation on how to actually do that. Seems like fosuserbundle is really not needed anymore.
from hwioauthbundle.
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)
- LinkedIn Auth - Reminder about access and refresh token requests after June 30, 2023 HOT 2
- Version for PHP 8.1 HOT 1
- Deprecation warnings HOT 1
- Issue with https configuration. Keeps redirecting to http HOT 1
- AzureResourceOwner and OID HOT 1
- hwi/oauth-bundle 2.0.0 conflicts with symfony/security-bundle >=6.0,<6.3.4. HOT 1
- How to register a user without FOSUB (Symfony 6)? HOT 2
- Guidance on runtime dynamic Resource Owner configuration HOT 3
- Support for php-http/httplug-bundle? HOT 1
- Has EntityUserProvider ever worked? HOT 1
- [2.x] Resource owner custom logic HOT 3
- symfony 7
- Compliance to digital privacy laws in Germany and the EEA of Identity Authentication for malicious purposes
- 2.x not compatible with \Symfony\Component\Security\Http\Authentication\AuthenticatorManager::authenticateUser HOT 2
- Update Facebook API version HOT 1
- Incorrect provider in docs? HOT 1
- Connect functionality is broken when using authenticators (Symfony 6+)
- Best practice to handle access token for API requests after login HOT 3
- Since 2.2.0 firewalls failure_handler gets overriden HOT 2
Recommend Projects
-
React
A declarative, efficient, and flexible JavaScript library for building user interfaces.
-
Vue.js
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
-
Typescript
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
-
TensorFlow
An Open Source Machine Learning Framework for Everyone
-
Django
The Web framework for perfectionists with deadlines.
-
Laravel
A PHP framework for web artisans
-
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.
-
Visualization
Some thing interesting about visualization, use data art
-
Game
Some thing interesting about game, make everyone happy.
Recommend Org
-
Facebook
We are working to build community through open source technology. NB: members must have two-factor auth.
-
Microsoft
Open source projects and samples from Microsoft.
-
Google
Google ❤️ Open Source for everyone.
-
Alibaba
Alibaba Open Source for everyone
-
D3
Data-Driven Documents codes.
-
Tencent
China tencent open source team.
from hwioauthbundle.