Comments (16)
Access token can be accessed from response (using PathUserResponse#getAccessToken()
mehod), in this way, you can save/re-validate token when user signs-in in your UserProvider
.
There is no integrated way to handle invalidation/expiration of tokens as it was not required, but this bundle allows easily extending of specific resource owners allowing you to handle that if you need it.
from hwioauthbundle.
In wich part of the login/registration process can i access to the PathUserResponse?
Can you provide a quick demo snippet?
from hwioauthbundle.
Sorry, mentioned wrong class, mainly because I forgot that the access token is also set in built-in OAuthToken
.
I guess you know (at least in theory) how to connect users and store specific info in db ;-)
from hwioauthbundle.
Don't worry. How can I retrieve the OAuthToken
instance and where?
I just tried something, but mostly experimenting. As you know the documentation for the bundle is incomplete and many topics are not covered in detail. Some more examples would be great!
Can you provide an example on how to store the access token at registration/connect and update it once the user logins again with facebook?
Thanks in advance
from hwioauthbundle.
About how to get access token in any part of Symfony:
$accessToken = $this->container->get('security.context')->getToken()->getAccessToken();
To save this token at user entity at login phase, you could implement own UserProvider
with interface OAuthAwareUserProviderInterface
, as example look at built-in EntityUserProvider
.
from hwioauthbundle.
Thanks @stloyd .
Thanks to your comment I think I understood something more.
I partly solved my issues and I'm currently able to store the user facebook access token when he/she signs up or connects with facebook (but I am not able to update the token, in case it changed, when the user logins again using facebook).
I'll point out what I did. I settled up a test project here that you can check out to find out some detail that i would probably miss.
-
I added the field
facebookAccessToken
in my User class (based on FOSUserBundle) -
I created my custom user provider by extending the default
FOSUBUserProvider
class -
I tweaked the
connect()
method by adding some lines to store the facebook access token on thefacebookAccessToken
field. (Follows the whole implementation). I tried to make the integration somewhat generic to be able to store access token for other services just by adding more fields to the user entity (e.g.githubAccessToken
,twitterAccessToken
, etc.).<?php namespace LMammino\Bundle\JHACBundle\Security\Core\User; use HWI\Bundle\OAuthBundle\OAuth\Response\UserResponseInterface; use HWI\Bundle\OAuthBundle\Security\Core\User\FOSUBUserProvider as BaseClass; class FOSUBUserProvider extends BaseClass { /** * {@inheritDoc} */ public function connect($user, UserResponseInterface $response) { $property = $this->getProperty($response); $setter = 'set'.ucfirst($property); if (!method_exists($user, $setter)) { throw new \RuntimeException(sprintf("Class '%s' should have a method '%s'.", get_class($user), $setter)); } $username = $response->getUsername(); if (null !== $previousUser = $this->userManager->findUserBy(array($property => $username))) { $previousUser->$setter(null); $this->userManager->updateUser($previousUser); } $user->$setter($username); $serviceAccessTokenName = $response->getResourceOwner()->getName() . 'AccessToken'; $serviceAccessTokenSetter = 'set' . ucfirst($serviceAccessTokenName); if(method_exists($user, $serviceAccessTokenSetter)) $user->$serviceAccessTokenSetter($response->getAccessToken()); $this->userManager->updateUser($user); } } Just few questions now: 1. Is this a good/correct approach (i.e. extendible, scalable, compliant, etc...)? 2. What to do to check the access token each time the user logins with facebook? It would be great to be able to refresh the access token stored in the database if it changed (for any reason: e.g. invalidated, expired or app secret changed) 3. What to do if i want my users to be able to remove their _connection_? I thought that just removing the stored facebook id and access token would be enough, but it would be glad to have some confirmation... I also would like to suggest to improve the configurability of the default FOSUBUserProvider just to handle this case (storing the access token of each service). I thought that would not be so difficult to implement this kind of configuration:
hwi_oauth:
fosub:
properties:
github: githubId
google: googleId
facebook: facebookId
access_token:
github: githubAccessCode
google: googleAccessCode
facebook: facebookAccessToken
or
hwi_oauth:
fosub:
properties:
github:
id: githubId
accessToken: githubAccessToken
google:
id: googleId
accessToken: googleAccessToken
facebook:
id: facebookId
accessToken: facebookAccessToken
(but the latter would break compatibility)
Thanks again for the great support!
from hwioauthbundle.
Hey @lmammino,
For case you mentioned: update token in case of users sign-in, as I mentioned you need to update other method, called loadUserByOAuthUserResponse
, as you extend the FOSUBUserProvider
, it should be something like:
/**
* {@inheritdoc}
*/
public function loadUserByOAuthUserResponse(UserResponseInterface $response)
{
$user = parent::loadUserByOAuthUserResponse($response);
$serviceName = $response->getResourceOwner()->getName();
$setter = 'set' . ucfirst($serviceName) . 'AccessToken';
if (method_exists($user, $setter)) {
$user->$setter($response->getAccessToken());
}
return $user;
}
Note: this will update token on every sign-in call.
About questions:
- IMO yes, of course if you will not add to much calls to update etc., some look-up in your code, how often such updates are called would be necessary,
- Update of token just suggested above, invalidation etc., with built-in code it's not simple IMO, but you can easily extend resource provider to add support for it, in overall case for this bundle, built-in support is not necessary, as it always tries to get up-to-date token from resource,
- You mean: remove connection to resource owner at bundle controller state ? It's not supported, mainly because not all resource owners provide API way to do this, but if your user removes access at state of resource owner (i.e. removes access for your app at facebook), you will be not notified about that, so your access token will just get an error with access denied, but if user removes that access and tries again to sign-in using resource owner, he will be asked to give your app access, same way as he would do this first time.
About new fields: this bundle is not right place for this, mainly because it's not necessary, and in bundle itself it will be never used, also you can very easily add this to your bundle (same way you have done it already).
from hwioauthbundle.
Thanks again @stloyd ! ;)
Your explanation was very complete and clear and I finally got a good idea about how the bundle works.
Just one more question about updating the access token when user logins:
Should I call the method $this->userManager->updateUser($user)
to effectively trigger the update at database level, or its done out-of-the-box?
Anyway I suppose i can create a getter for the service access token (e.g. getFacebookAccessToken
) and use it to compare the two access tokens. I would trigger the update just if the two tokens are different. Doing so i would avoid updating the user instance if not necessary.
So definitely i suppose i would have a code like this:
/**
* {@inheritdoc}
*/
public function loadUserByOAuthUserResponse(UserResponseInterface $response)
{
$user = parent::loadUserByOAuthUserResponse($response);
$serviceName = $response->getResourceOwner()->getName();
$serviceAccessTokenSetter = 'set' . ucfirst($serviceName) . 'AccessToken';
$serviceAccessTokenGetter = 'get' . ucfirst($serviceName) . 'AccessToken';
if ( method_exists($user, $serviceAccessTokenSetter) &&
method_exists($user, $serviceAccessTokenGetter) &&
$user->$serviceAccessTokenGetter() !== $response->getAccessToken()
)
{
$user->$serviceAccessTokenSetter($response->getAccessToken());
$this->userManager->updateUser($user);
}
return $user;
}
from hwioauthbundle.
In overall if it's your code, I would suggest to remove all those method_exists()
calls to save a bit, mainly because you can be sure you have implemented those method in your entity, those checks are useful only in case that your bundle is open sourced, and user can implement entity on his own.
And yes, I forgot about update of user in db, personally I don't like changing user object in loaders, as it's smelly for me, I prefer using events here, but in most cases such approach is good enough.
About comparing tokens: it's good idea, but in some edge cases could not work as expected, mainly because as mentioned before, this bundle requests for up-to-date tokens, as there is no easy way to make (in)validation (of tokens) in easy way for all built-in providers.
from hwioauthbundle.
Thanks for the good suggestions once more @stloyd. I'll surely apply them in production.
Anyway, I didn't get the last point.
You said that the bundle requests up-to-date tokens. So why comparing up-to-date token with my previously stored token may not work?
Can you provide me a direct example of one of the edge case you mentioned?
I would also be glad to have an example about how to update the user by using events. (Probably it would be a good lesson to learn something more about Symphony)
from hwioauthbundle.
About edge case, it's more like theory now, not sure it can be easily found. Let's say you call facebook, you get an token and it will expire within one day (86400 secs), but you have in you app session lifetime set to one hour (3600 secs), so you will logout user automatically after one hour, and force him to sign-in, and here you could get new access token from facebook (not sure how they handle such requests), so at least in theory, you will have two valid tokens =) as said, it's really edge case, and you probably will not meet it... =)
About events: Symfony (not Symphony :P), has built-in security events, which are called when provider success or fails to authenticate user.
To use them you need to set-up your own event listener, which in you case, should look for security.authentication.success
event to be called.
In event listener, you need to inject User Manager (from FOSUB, or your own if you have such) or Entity/Object Manager (from Doctrine), as an event object you will get AuthenticationEvent
, from which you can easily extract OAuthToken
and wired with it User entity, it could look like:
services:
success_login_listener:
class: 'LMammino\Bundle\JHACBundle\Listener\SecuritySuccessListener'
arguments: [@fos_user.user_manager]
tags:
- { name: kernel.event_listener, event: security.authentication.success, method: handle }
// src/LMammino/Bundle/JHACBundle/Listener/SecuritySuccessListener.php
namespace LMammino\Bundle\JHACBundle\Listener;
use FOS\UserBundle\Model\UserManagerInterface;
use FOS\UserBundle\Model\UserInterface;
use Symfony\Component\Security\Core\Event\AuthenticationEvent;
class SecuritySuccessListener
{
private $userManager;
public function __construct(UserManagerInterface $userManager)
{
$this->userManager = $userManager;
}
public function handle(AuthenticationEvent $event)
{
$user = $event->getAuthenticationToken()->getUser();
if ($user instanceof UserInterface) {
// do an update of user object with tokens here
$this->userManager->updateUser($user);
}
}
}
from hwioauthbundle.
Note that this listener will also be called on normal form sign-in, so you probably should check in handle()
method for that the token is an instance of OAuthToken
from this bundle if you plan to use such approach for signing-in too.
from hwioauthbundle.
Whoops! I know it's Symfony and not Symphony, but it's not natural to me and sometimes my hands go by themselves on my keyboard! =)
As far as I know, facebook does not allows multiple active accesstoken anymore. If a new token is generated ( e.g. due to app permission change, user changing password or app changing secret) the old one(s) is(are) invalidated.
So, in the scenario you proposed, I suppose that the stored access token would not be valid while the user remains logged in thanks to the app session lifetime, but when he/she disconnects and logs in again the access token should be updated and becomes functional again.
I know it's not perfect but it's a good compromise at the moment.
I think a better solution would be to handle the token invalidation/expiration on the app internal social API layer (that may run as parallel process or in a separate machine). When the app triggers a request to facebook it should check if the given token has expired or it's not valid anymore (by reading the facebook api call response code) and, eventually, notify the user who owns the token (e.g. by mail) to disconnect and reconnect again to the website to trigger a token update.
Thanks for the "quick lesson" about Symfony security events... I'll surely adopt your code!
from hwioauthbundle.
@lmammino Can this issue be closed? :)
from hwioauthbundle.
of course @asm89 👍
from hwioauthbundle.
Check :)
from hwioauthbundle.
Related Issues (20)
- RefreshTokenListener calls resourceOwner::getUserInformation on each request
- OAuth for Apple using auto-generating secret IDs is broken HOT 1
- Add support Discord Oauth2 HOT 1
- when this bundle will be available for symfony 6.2? HOT 2
- 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
- How to use user entity provider HOT 3
- 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
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.