-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[WIP][Security] OAuth2 component #31952
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Conversation
Here's the default value that I've defined, not sure about it: ->scalarNode('client_id')->defaultValue('12345678')->end()
->scalarNode('client_secret')->defaultValue('12345678')->end()
->scalarNode('authorization_url')->defaultValue('https://foo.com/authenticate')->end()
->scalarNode('redirect_uri')->defaultValue('https://myapp.com/oauth')->end()
->scalarNode('access_token_url')->defaultValue('https://foo.com/token')->end() Any recommendation? 🤔 |
@Koc Configuration keys name fixed 🙂 |
My point was to remove these options from the default options. I don't see any case where something could be generic for these options (and merged into the options of the provider), and keep the "required" argument for them in the provider section. |
Looks promising. Should be a sub component of the security one IMO (i.e. moved under the |
@chalasr Thanks for the feedback, yes, I totally agree with this idea, as OAuth is totally linked to the security. Plus, it make no sense at all to use the default |
@Taluu Oh ok, sorry, I've missed the point the first time 😄 That's right, there's no case where an option can be "shared" with many providers, nice one 👍 |
@chalasr About the configuration, this is more a DX question but after thinking about it, here's my idea, what about moving this configuration: framework:
oauth:
redirect_uri: '%env(REDIRECT_URI)%' # Can be useful in order to share the configuration?
providers:
github:
type: 'authorization_code'
client_id: '%env(CLIENT_ID)%'
client_secret: '%env(CLIENT_SECRET)%'
authorization_url: '%env(API_AUTHORIZATION_URL)%'
access_token_url: '%env(API_ACCESS_TOKEN_URL)%'
google:
type: 'authorization_code' Here: security:
providers:
github_oauth:
oauth:
type: 'authorization_code'
client_id: '%env(CLIENT_ID)%'
client_secret: '%env(CLIENT_SECRET)%'
authorization_url: '%env(API_AUTHORIZATION_URL)%'
access_token_url: '%env(API_ACCESS_TOKEN_URL)%'
google_oauth:
# ...
firewalls:
main:
provider: github_oauth I'm not a fan of the first one now as it sounds like the OAuth configuration is linked to the framework one (which is not now as it's been moved as a sub-component), plus, this make the configuration similar to the entity, id, ldap ones 🤔 Any suggestion? |
Couldn't we promote @weaverryan's oauth2-client-bundle or HWIOAuthBundle as an official Symfony package instead of starting a new component from scratch? |
I think that any official Symfony package should use |
Of course, but patching the two existing packages to use it is easy. Way easier than re-creating something from scratch. |
@dunglas I don't know if it's easier to patch the existing packages (and hoping that nothing breaks) rather than adding this package, I've used Ryan's package and it's great, don't get me wrong, problem is, it's not "native" on Symfony and it force us to use an external package rather than using an "official" one which use the latest tools provided by Sf. I could be wrong but (and if it's the case, just stop me) for me, it's important that Sf provide this kind of feature with an official package (which is not fragmented/splitted in sub-packages) that helps every developers to implement OAuth with a standard API and a "top-class" (not actually, I agree but I'm working on it 😄) integration on the framework. I agree with @ajgarlag for Actually, the package take 3-4 days to be created so it's not a big work for now, I see it as an experimentation and if it's clear that Sf doesn't have any gains on using it and prefer to promote @weaverryan's oauth2-client-bundle or HWIOAuthBundle as officials packages, it's okay for me 🙂 |
I don't know about @weaverryan's bundle, but for HWIO, it's a mess in there and something that is (almost) unmaintainable because of the providers which makes it really hard to use and implement a custom provider (hard-coded config). |
Ryan's bundle is simpler, and more modern, but doesn't support OAuth 1. |
@Guikingone I agree with the rationale of having more things, including an OAuth implementation, in the |
My point was that using the KNP client is simple, I agree with it but what if you need to add a new provider? And what about the maintainability? On the current "component", it's simple, you have a default provider which is linked to the RFC requirements, you only need to instantiate it and configure the options (without creating a dedicated provider thanks to the Security configuration but you can if you need, the default classes are here to guide you and provide a RFC compliant implementation). When it comes to maintainability, simple example, if you take a look at https://github.com/knpuniversity/oauth2-client-bundle/blob/master/src/Client/Provider/GitlabClient.php and https://github.com/knpuniversity/oauth2-client-bundle/blob/master/src/Client/Provider/GoogleClient.php, where are the differences that requires you to create a dedicated provider? For me, there's no reason to have dedicated provider as you don't override the arguments, don't change the The component in this PR can get rid of this code duplication and improve the DX as the don't need to use multiples classes to do the same things. As always, I can get wrong and if it's the case, just stop me 🙂 |
Well, PHPLeague also follows semver, so that would still not break your app. Symfony's BC policy is "we follow semver" + "we always provide deprecation warnings before doing BC breaks" (+ a time-based release schedule so that you know when you can expect a major version, but that's not necessary for such good experience, especially for smaller packages like the ones from PHPLeague which may not have a need for major versions as often) |
/** | ||
* @author Guillaume Loulier <contact@guillaumeloulier.fr> | ||
*/ | ||
trait Psr18ProviderTrait |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this empty trait is useless
$query['scope'] = $options['scope']; | ||
} else { | ||
if ($this->logger) { | ||
$this->logger->warning('The scope key is optional, the expected behaviour can vary.'); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
the message should be about The scope is not provided
, not The scope key is optional
. If you provide it explicitly, it is still optional.
/** | ||
* @author Guillaume Loulier <contact@guillaumeloulier.fr> | ||
*/ | ||
trait Psr7Trait |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is there some PSR-7 here ? HttpClient is used for requests.
* file that was distributed with this source code. | ||
*/ | ||
|
||
namespace Symfony\Component\Security\OAuth\Server; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why is there a Server namespace when the readme says we don't handle the server-side protocol ?
/** | ||
* @author Guillaume Loulier <contact@guillaumeloulier.fr> | ||
*/ | ||
abstract class AbstractGrantType implements GrantTypeInterface |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
such empty abstract class is useless.
|
||
public function handleRefreshTokenRequest(AuthorizationRequest $request); | ||
|
||
public function returnResponsePayload(): array; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
this looks like an interface enforcing to make the service stateful. That's a bad idea. Stateful services are a huge pain (as we then need to reset the state at appropriate time)
"symfony/http-foundation": "~4.3" | ||
}, | ||
"require-dev": { | ||
"psr/http-message": "~1.0" |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why would you need PSR-7 interfaces ?
@Guikingone I think this should all be named such that it's clearly part of OAuth 2.0 as opposed to OAuth 1.0 / 1.0a |
@kralos Hey 👋 Sorry for the delay, yes, it could be a good idea, maybe naming it as |
@Guikingone JWT's are not part of OAuth2's specification, they are supported via an OAuth2 Extension https://tools.ietf.org/html/rfc7519#page-19 [Opinion] Do you think we should solve core vanilla OAuth2, then add Extensions as optional pluggable add ons ( Developers might want to plug their own OAuth2 Extensions into the Symfony core OAuth2 implementation. If you start implementing extensions in Symfony Core, people might expect you to implement all extensions. |
Hi @kralos 👋 Yes, in fact, I was experimenting on this one, the core idea is to solve core OAuth2 specifications then if developers wants to add extensions, providing a set of interfaces (like contracts?) to do it. That's more or less the idea behind As long as you implement this interface, you can add every provider that you need. For now, I'm focusing on resolving core issues when it comes to authorization attacks as described here: I was also thinking about implementing Dynamic Client registration as described in 7591 but I don't know if it's really needed for now (as the Client is almost ready for usage). |
When can we start using it ? |
Hi @manu-sparheld, for now, the client is available and open for alpha testing using the repository, I'm currently working on the security aspect of the component and polishing the DX, feel free to give feedback 🙂 |
*/ | ||
abstract class GenericProvider implements ProviderInterface | ||
{ | ||
private const DEFAULT_OPTIONS = [ |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think that some of those keys are not always required including redirect_uri
according the use case (client credentials flow for example).
Unless I miss something, it may concern the option userDetails_url
too as a user is not always involved.
This looks very promising. What is the progress? =) @Guikingone |
Hi @j-mywheels, Thanks for the interest, the component is almost done for the I'm currently working on the Symfony integration (mostly Security component and DIC usages) 🙂 |
And given we are already past the feature freeze (way past it), this component won't be part of 4.4 and 5.0. It will have to wait for 5.1 |
Also a couple questions, will the AccessToken, RefreshToken, Client, Scope.. etc. be linked to Doctrine entities? Would it be possible to create extension grants (https://tools.ietf.org/html/rfc6749#section-4.5)? it might be useful to see how we can implement two factor authentication in the early stage of this component (using the extension grant for example)? |
Hi @jamalo 👋 For now, the OAuth2Client isn't linked to Doctrine, the "bridge" with the framework can bring a relation due to security internal logic (mostly when it comes to providers) but the core classes aren't linked, the only "relations" are HttpClient and OptionsResolver. When it comes to Extension Grant, for now, it's not planned as the main requirements are related to the endpoint that you're using (SAML, etc) but it can easily added thanks to |
Hopefully this will make FOSOAuthServerBundle past time. Good luck @Guikingone |
I have not looked at the code. This is interesting as OAuth is everywhere nowadays. Before going further, I'd like to get thoughts from @weaverryan (as it maintains a similar bundle) and @wouterj as he is actively working on modernizing the Symfony security system (and we talked about OAuth support recently). |
I've never used OAuth, everything below this paragraph is based on a few google searches. I apologize if it doesn't make any sense, in that case feel free to ignore it. I'm a bit on the same track as @dunglas in the comments above. I feel like OAuth consists of 2 completely saturated PHP communities: OAuth and Http clients. I don't think it makes sense to oversaturate it by creating yet another OAuth component. Apart from that, this also means someone has to maintain the Symfony OAuth component and keep it up to date with the specs, etc. From what I read here, it seems like the lack of HTTP Client support is the biggest "issue" with the League OAuth package (which is used by the Knp bundle). Can't we discuss with the PHP League maintainers to find out if they are open for building against a standard interface (which they already did before 1.0)? Symfony's HTTP Client and Guzzle are implementing tons of interopability interfaces, it must be possible to find one that can be used imho. Also, PRs like symfonycorp/connect#95 makes me think there might not be need for a full-blown OAuth component and a couple abstract authenticators might be enough of a help? If someone wants multiple social logins, they can use the popular OAuth bundles already. |
I agree with @wouterj that having support for HttpClient in League OAuth package would be much better (https://github.com/thephpleague/oauth2-client). |
Hi everyone 👋
Here's my first "big" PR on Symfony, recently, I've decided to implement OAuth authentication in a side-project application, in order to do it, I've used
HttpClient
(thanks to Nicolas Grekas) and I've been amazed by the simplicity of the implementation but something was weird ... Why Symfony doesn't have a "native" OAuth support/Client/Server/Component?Then I saw that the hackaton highlighted the idea of having a native support: #30914
So, I've decided to take time to work on a strict implementation (well, as strict as I could do) thanks to the RFC and
HttpClient
and here's the first result, a new "OAuth component" (actually, that's an experimentation 😄), for now, this experimentation only support the "client" aspect (the server has been started) and that I'm aware that a lot of feature can be lacking or to update.So, why opening this PR now ?
First, I'm aware that this is a WIP and that a lot of feedback can be linked to some choices that I've made, please, do not hesitate to explain me if there's a better option/way to do it.
Second, I think that it's a good idea to discuss about this idea and explain why it's not a good idea/option to do it as a component (or even as a feature?) before going further on the development 🙂
So here's the PR with the first parts of the components, feel free to comments and have a great day 🙂
This PR replace the following: #31937
EDIT 13/06/2019:
Here's the actual documentation of this "component":
A set of class which allows to use OAuth thanks to
HttpClient
andOptionsResolver
.Usage
Using
symfony/security-oauth-client
outside of the framework is pretty straight forward.Let's assume that we need to use the Github OAuth API.
First, let's instantiate the provider:
Informations
GenericProvider
can probably be improved.AuthorizationCodeGrantAccessToken::getOptionValue()
is similar toParameterBag::get()
.AuthorizationCodeResponse
isn't critical as it's a simple "DTO", we caneasily return an array.
Symfony\Components\OAuth\Provider\GenericProvider
doesn't allowto fetch the user details directly (as it's not part of the RFC),
the final request is up to the user.
to return the
error
key returned by the API.Symfony\Component\OAuth\Provider\ProviderInterface
can benefit ofhaving the
fetchAuthorizationCode
andfetchAccessToken
methods if acontract is created.
\RuntimeException
which is thrown during the$provider->fetchAuthenticationCode
call (if the provider can't use it)can be moved to a
trigger_error
.Supported providers
For now, the component deliver the main providers described in the RFC: https://tools.ietf.org/html/rfc6749
Tests
The component isn't fully tested as it's considered as a POC,
the main providers are tested and validated.
The tokens should be fully tested.
Framework integration (WIP)
This part isn't ready to use, it's just an idea
As an application can use multiples providers, it could be a good idea to
allows to use a factory (like
HttpClient
?):This way, a newly created service can be accessed via
@oauth.github
,time to configure the
security.yaml
:Let's imagine that we need to inject the provider in a service:
Creating a custom provider
As the component allows to use a
ProviderInterface
, creating a custom provideris as easy as it sounds:
Or if needed, we can directly extends the
GenericProvider
class.