-
-
Notifications
You must be signed in to change notification settings - Fork 9.6k
[Security] Add a JSON authentication listener #18952
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
👍 A great help for JSON APis! |
Unless you have to pass the JWT and the login/password in the same request, the standard way for password authentication is HTTP Basic. This is supported out of the box in Symfony and easy to do with jQuery.ajax. |
@GromNaN I agree about that HTTP Basic and form-data stay the standard way for getting a JWT (& simpler for most cases). |
Does this make https://github.com/gfreeau/GfreeauGetJWTBundle obsolete? |
@teohhanhui AFAIK, GfreeauGetJWTBundle is for x-www-formencoded data, not for JSON documents. |
But the usual intention is to avoid |
Why not a Request body listener like FOSRestBundle has? But that way you can use also XML for example... I'm already using exactly this way to login with Json on 3.0.6 with FOSRestBundle 2 |
throw new BadCredentialsException(sprintf('Missing key "%s".', $this->options['password_parameter'])); | ||
} | ||
|
||
$username = $data[$this->options['username_parameter']]; |
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.
What about using property paths here? One might not always have the required information on the top level, but may want to wrap it in some other structure.
I've done some changes to this PR:
It is ready for review. |
{ | ||
public function loginCheckAction() | ||
{ | ||
throw new \RuntimeException('loginAction() should never be called.'); |
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.
Should be loginCheckAction() should never be called.
.
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.
could make use of __FUNCTION__
actually
LGTM |
The min version in the @dunglas Can you rebase here to hopefully let Travis run the job on PHP 7 again. |
8475884
to
cd86c9e
Compare
Rebased against master and comments fixed. |
163a36a
to
653bebb
Compare
ping @symfony/deciders (HHVM error not related) |
Imo you should also add a functional test case covering a config where the |
@xabbuh test updated. |
5c085a6
to
ee40718
Compare
public function handle(GetResponseEvent $event) | ||
{ | ||
$request = $event->getRequest(); | ||
$data = json_decode($request->getContent()); |
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.
Should we protect here ourselves by checking the max. length of the request content in the same way that we check the max username (Security::MAX_USERNAME_LENGTH
) and password a bit later?
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.
@javiereguiluz but what is the max length of the request? The login and password can be stored in any JSON document. It would be similar to checking the length of the form in the traditional login form and AFAIK we don't do it currently.
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'm not an expert about this topic. I just saw this json_decode()
that accepts the raw user's input and I was thinking about sending a 100MB request to make the application explode ;)
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'm not a security expert either, but it's the same for all endpoints in this case :) There are some configurations in PHP to avoid such problems (like post_max_size
, max_execution_time
and memory_limit
). The check on the username length isn't related to DDOS attacks.
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.
See #18733
Docs added symfony/symfony-docs#7081 |
} | ||
|
||
if (!is_string($username)) { | ||
throw new BadCredentialsException(sprintf('The key "%s" must contain a string.', $this->options['username_path'])); |
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 key "%s" must be a string.
?
} | ||
|
||
if (!is_string($password)) { | ||
throw new BadCredentialsException(sprintf('The key "%s" must contain a string.', $this->options['password_path'])); |
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 key "%s" must be a string.
?
try { | ||
$username = $this->propertyAccessor->getValue($data, $this->options['username_path']); | ||
} catch (AccessException $e) { | ||
throw new BadCredentialsException(sprintf('Missing key "%s".', $this->options['username_path'])); |
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 "%s" key must be provided.
?
try { | ||
$password = $this->propertyAccessor->getValue($data, $this->options['password_path']); | ||
} catch (AccessException $e) { | ||
throw new BadCredentialsException(sprintf('Missing key "%s".', $this->options['password_path'])); |
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 "%s" key must be provided.
?
ee40718
to
8fd5666
Compare
👍 for 3.3 |
8fd5666
to
e0bb4e4
Compare
e0bb4e4
to
10ecbe1
Compare
Thank you @dunglas. |
This PR was squashed before being merged into the 3.3-dev branch (closes #18952). Discussion ---------- [Security] Add a JSON authentication listener | Q | A | | --- | --- | | Branch? | master | | Bug fix? | no | | New feature? | yes | | BC breaks? | no | | Deprecations? | no | | Tests pass? | yes | | Fixed tickets | n/a | | License | MIT | | Doc PR | symfony/symfony-docs#7081 | Add a new authentication listener allowing to login by sending a JSON document like: `{"_username": "dunglas", "_password": "foo"}`. It is similar to the traditional form login (but take a JSON document as entry) and is convenient for APIs, especially used in combination with JWT. See api-platform/core#563 and lexik/LexikJWTAuthenticationBundle#123 (comment) for previous discussions. - [x] Add functional tests in security bundle Commits ------- 02178bc [Security] Add a JSON authentication listener
This PR was merged into the master branch. Discussion ---------- JSON authentication listener docs Docs for symfony/symfony#18952. Commits ------- b192ab3 Fixed a syntax issue 16ae3f6 Reworded and simplified the article 820f28e Fixed the name of the "username" property 6d018f6 Fixed a syntax error ab5259b Removed a tip which is not too relevant for the article d2dd895 Fixed the JSON format (this time for real) 6ff24da Fixed the JSON format 0961128 Show the simple example first and then explain the complex use case 68bd9a5 JSON authentication listener docs
Add a new authentication listener allowing to login by sending a JSON document like:
{"_username": "dunglas", "_password": "foo"}
.It is similar to the traditional form login (but take a JSON document as entry) and is convenient for APIs, especially used in combination with JWT.
See api-platform/core#563 and lexik/LexikJWTAuthenticationBundle#123 (comment) for previous discussions.