I am developing a RESTFUL API in Symfony 5.2 that allows users to authenticate with username and password in order to access it.
My firewall config for this looks like:
login:
pattern: '^/v1/login'
methods: [POST]
anonymous: true
stateless: true
provider: user_provider
user_checker: App\Security\UserChecker
json_login:
check_path: '/v1/login'
success_handler: App\Security\Handler\AuthenticationSuccessHandler
failure_handler: lexik_jwt_authentication.handler.authentication_failure
I have created a validator and a matching route so that I can ensure users submit valid data, like not submitting empty passwords:
v1_login:
path: /v1/login
methods: POST
controller: App\Http\Token\Action\LoginAction
defaults:
_format: json
All my other routes are setup the same way with validators, but this one is not being called. I think it's Symfony is ignoring this and using the user_provider, user_checker and success handler to manage it all. But it means I can't validate the request first.
Is there something else I need to do?
Related
I'm banging my head on the keyboard for a while now trying to solve something that should be simple but for some reason it has not been.
Here is the scenario. A user will use a form on my website to login (username/pwd). And the following workflow should work:
Try to authenticate the user on the LDAP server:
1.1. If it passes:
1.1.1. Check if there is an user with the same username on our database;
1.1.1.1. If it has, load some data and log in the user;
1.1.1.2. If it has not, throws a failed login.
1.2. If it fails:
1.2.1. Check if there is an user with the same username on our database;
1.2.1.1. If it exists
1.2.1.1.1 Check if the pwd match the save one, if it matches, log the user
1.2.1.2. If it has not, throws a failed login.
So basically, I'm trying to authenticate the user on the LDAP server, and if that fails, I want to try to authenticate locally.
I tried configuring multiple User Providers, using a chain provider, but it does not work. If the first UserProvider fails, it never hits the second one.
# config/security.yaml
security:
providers:
# base local provider
app_user_provider:
entity:
class: App\Entity\User\User
property: username
# LDAP provider
my_ldap:
ldap:
service: Symfony\Component\Ldap\Ldap
base_dn: '%env(resolve:LDAP_BASEDN)%'
search_dn: '%env(resolve:LDAP_SEARCHDN)%'
search_password: '%env(resolve:LDAP_SEARCHPWD)%'
default_roles: ROLE_USER
extra_fields: '%env(resolve:LDAP_EXTRAFIELDS)%'
uid_key: uid
filter: null
# chain
all_users:
chain:
providers: ['my_ldap', 'app_user_provider']
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
lazy: true
provider: all_users
form_login_ldap:
enable_csrf: true
default_target_path: app_homepage
login_path: app_login
check_path: app_login
service: Symfony\Component\Ldap\Ldap
username_parameter: username
password_parameter: password
dn_string: '%env(resolve:LDAP_BASEDN)%'
query_string: '%env(resolve:LDAP_QUERYSTR)%'
search_dn: '%env(resolve:LDAP_SEARCHDN)%'
search_password: '%env(resolve:LDAP_SEARCHPWD)%'
What am I missing? I found a couple of answers here (like Symfony 5: ldap authentication with custom user entity) but it uses the old Authenticator Guard that does not exists.
Good afternoon. Please i'm using LexikJWTAuthenticationBundle in a symfony 4 api project. I'm using UserProvider for Doctrine.
After Configure Doctrine User Provider, I've install and configure LexikJWTAuthenticationBundle. But when i tried to athentificate using Postman on the url http://localhost:8000/api/login_check whith this JSON {"username":"ak",
"password":"ak"} I've this error: {
"code": 401,
"message": "Bad credentials"
}.
See below my Security.yaml config file. I've read forums to tried to solve this issue but i've not yet found the solution. Can you please help me?
security:
encoders:
App\Entity\Utilisateur:
algorithm: bcrypt
providers:
#in_memory: { memory: ~ }
our_db_provider:
entity:
class: App\Entity\Utilisateur
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
login:
pattern: ^/api/login
stateless: true
anonymous: true
json_login:
check_path: /api/login_check
success_handler: lexik_jwt_authentication.handler.authentication_success
failure_handler: lexik_jwt_authentication.handler.authentication_failure
api:
pattern: ^/api
stateless: true
guard:
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
main:
pattern: ^/
user_checker: App\Security\UtilisateurChecker
anonymous: true
provider: our_db_provider
access_control:
- { path: ^/api/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/api, roles: IS_AUTHENTICATED_FULLY }
Have you missed a piece of configuration during installation? I dont see the lexik one:
lexik_jwt_authentication:
secret_key: '%kernel.project_dir%/config/jwt/private.pem' # required for token creation
public_key: '%kernel.project_dir%/config/jwt/public.pem' # required for token verification
pass_phrase: 'your_secret_passphrase' # required for token creation, usage of an environment variable is recommended
token_ttl: 3600
Good morning All. I've found the solution of my problem. In fact, i was typing a bad User Password.
To solve this article, i've used this article https://numa-bord.com/miniblog/symfony-4-les-base-dune-gestion-des-utilisateurs-inscription-connexion-droits-dacces/
I've created au database user by using create user command implemented in the article.
After i have been connected with this previous created user sucessfully. ApiPlatform generate a web tocken for me.
Thank you very much
I want two authentications methods in my application.
One for the entity User, and other (admin) with a plaintext.
Very simple.
Thus, when I configure security.yaml, I specify the providers:
security:
providers:
user:
entity:
class: App\Entity\User
property: username
in_memory:
memory:
users:
admin:
password: admin
roles: 'ROLE_ADMIN'
encoders:
App\Entity\User: bcrypt
Symfony\Component\Security\Core\User\User: plaintext
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
admin:
provider: in_memory
pattern: ^/admin/
guard:
provider: in_memory
form_login:
login_path: admin_login
check_path: admin_login
logout:
path: /admin/logout
target: /
default:
provider: user
anonymous: ~
guard:
provider: user
form_login:
login_path: login
check_path: login
default_target_path: login_redirect
use_referer: true
logout:
path: /logout
target: /
access_control:
- { path: ^/admin/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
- { path: ^/admin, roles: ROLE_ADMIN }
- { path: ^/dashboard, roles: ROLE_USER }
And return the error:
In GuardAuthenticationFactory.php line 121:
Because you have multiple guard configurators, you need to set the "guard.e
ntry_point" key to one of your configurators ()
Then, if I have to set the guard.entry_point, I need do something like this:
admin:
entry_point: app.form_admin_authenticator
main:
entry_point: app.form_user_authenticator
And therefore, if I undestard, I need to configure a Authentication Listener like this: https://symfony.com/doc/current/components/security/authentication.html
(btw, this particular help page is very ambiguous and incomplete)
Is it necessary? It seems too complex for my purpose
I ran into this particular error. My situation might be a little different, but I had a similar need to authenticate using different authentication strategies depending on the entry point to the application.
One thing your config doesn't include is a reference to any Guard Authenticator objects. See this documentation for an intro to what role those objects play, and how to define them. Symfony's Security package is pretty complicated, and I found that using Guard Authenticators made the process a lot simpler for my use case.
Here is an example of a security.yaml config referencing two different authenticators. The entry_point configuration tells Symfony which one to try first, because in my particular case, Symfony otherwise wouldn't know which authentication strategy to apply first to an incoming request.
security:
providers:
user:
id: App\My\UserProviderClass
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
main:
anonymous: ~
logout:
path: app_logout
guard:
entry_point: App\My\MainAuthenticator
authenticators:
- App\My\MainAuthenticator
- App\My\OtherAuthenticator
Custom Guard Authenticators contain a method called supports. This method takes the incoming request as its only argument, and returns true or false based on whether the given authenticator should be applied to the incoming request. A common practice might be to check the request's Symfony route name (as defined by the controller) or perhaps something like the full URI for the request. For example:
/**
* Does the authenticator support the given Request?
*
* If this returns false, the authenticator will be skipped.
*
* #param Request $request
*
* #return bool
*/
public function supports(Request $request): bool
{
$matchesMyRoute = 'some_route_name' ===
$request->attributes->get('_route');
$matchesMyUri = '/path/to/secured/resource' ===
$request->getUri();
return $matchesMyRoute || $matchesMyUri;
}
You can imagine that if multiple Guard Authenticators exist in the same application, it's likely the case that one would only want them to apply to a request of a certain type, whether the differentiation is based on the kind of auth applied (eg. a header with an API key vs. a stateful session cookie), whether the difference is more about the specific route being hit, or perhaps a combination of factors.
In this case, telling Symfony which Guard Authenticator to try first may be important for your security strategy, or may have performance ramifications. For example, if you had two authenticators, and one had to hit the database to verify a stateful session, but another could verify the request's authentication statelessly, eg. by verifying a JWT's signature, you'd probably want to make the JWT authenticator run first, because it might not need to make a round trip to the database to authenticate the request.
See this article for a deeper explanation: https://symfonycasts.com/screencast/symfony-security/entry-point
I have been working on setting up the FOSUserBundle/RestBundle/OAuthServerBundle trio to create a headless back end that I can then place a separate front end on top of, and eventually expand to mobile, and possible third party API access. I have the general configuration in place based on the various resources and instructions available and can generate an access token using client credentials.
The application this is being added to is an existing one that uses standard Symfony/Twig for front end/back end interaction and uses FOSUserBundle for authentication.
I have two problems related to the flow of authentication.
I want the user to be able to access some parts of the API without authenticating past the client level, and some parts will require user-level authentication to verify they own the requested resources. I am not finding a way to do this. I've found posts talking about the possibility but nothing giving any direction on how it might be achieved. I believe I'll need to check at the controller level for appropriate access, maybe using custom voters, as checking for 'IS_AUTHENTICATED_FULLY' is coming back as true after just authenticating with the client. I want to be able to programmatically authenticate the user, bypassing the UI login form - that might just be overriding the FOSUserBundle login controller, but I'm not sure.
I either need to create a client without an access token expiration or find a way to implement the refresh token. I don't really see why my own app should need to refresh a token, but if that is the standard way to do it I'm ok with following specs on that.
Below is some relevant code, though by and large the code is pretty box standard stuff copied over from the FOSOAuthServer setup guide.
security.yml
security:
encoders:
FOS\UserBundle\Model\UserInterface: sha512
role_hierarchy:
ROLE_ADMIN: ROLE_USER
ROLE_SUPER_ADMIN: ROLE_ADMIN
providers:
fos_userbundle:
id: fos_user.user_provider.username_email
firewalls:
oauth_token:
pattern: ^/oauth/v2/token
security: false
rest:
pattern: ^/rest(?!/doc)
fos_oauth: true
stateless: true
anonymous: false
main:
pattern: ^/
form_login:
provider: fos_userbundle
csrf_token_generator: security.csrf.token_manager
success_handler: security.authentication.success_handler
use_referer: true
logout: true
anonymous: true
access_control:
- { path: ^/rest, roles: [ IS_AUTHENTICATED_FULLY ] }
config.yml snippet
fos_user:
db_driver: orm
firewall_name: main
user_class: AppBundle\Entity\User
registration:
form:
type: AppBundle\Form\Type\RegistrationFormType
profile:
form:
type: user_profile
fos_oauth_server:
db_driver: orm
client_class: AppBundle\Entity\Client
access_token_class: AppBundle\Entity\AccessToken
refresh_token_class: AppBundle\Entity\RefreshToken
auth_code_class: AppBundle\Entity\AuthCode
service:
user_provider: fos_user.user_provider.username_email
options:
supported_scopes: user
fos_rest:
view:
view_response_listener: force
formats:
json: true
templating_formats:
html: true
mime_types:
json: ['application/json', 'application/json;version=1.0', 'application/json;version=1.1']
jpg: ['image/jpeg']
png: ['image/png']
body_listener: true
param_fetcher_listener: true
allowed_methods_listener: true
format_listener:
rules:
- { path: ^/, priorities: [html, json], fallback_format: json, prefer_extension: false }
AD 1)
I solved your problem with two firewalls in security.yml.
Since Symfony is looking first match in security.yml I put first firewall to let anonymous users in:
api_anonym_area:
pattern: (^/api/forgotten-password/.*)
stateless: true
fos_oauth: true
anonymous: true
I catch URL with regex and give anonymous: true
As second firewall I have regex that catches all
api_auth_area:
pattern: ^/
fos_oauth: true
stateless: true
anonymous: false
So in your case, if you want anonymous users to get to /rest/doc, put in front of your firewall:rest something like this:
rest_doc:
pattern: ^/rest/doc
fos_oauth: true
stateless: true
anonymous: true
AD 2)
Its not good practice to have unlimited access token lifetime, but you can do it in config.yml by setting big integer to access_token_lifetime:
fos_oauth_server:
service:
options:
access_token_lifetime: #number in seconds#
To sign in with refresh token just
/oauth/v2/token?client_id=CLIENT_ID&client_secret=CLIENT_SECRET&grant_type=refresh_token&refresh_token=REFRESH_TOKEN
its in FOSOAuthServerBundle out of the box
Using Symfony 2, I am looking for more information about the handlers that you can define in the security configuration file app/config/security.yml (official documentation). The documentation doesn't give any informations about handlers. Here is an extract of the security file :
# app/config/security.yml
security:
...
firewalls:
somename:
form_login:
...
# login failure redirecting options (read further below)
failure_path: /foo
failure_forward: false
failure_path_parameter: _failure_path
failure_handler: some.service.id
success_handler: some.service.id
logout:
path: /logout
target: /
invalidate_session: false
delete_cookies:
a: { path: null, domain: null }
b: { path: null, domain: null }
handlers: [some.service.id, another.service.id]
success_handler: some.service.id
anonymous: ~
In both form_login ang logout part there is a success_handler field. Moreover, for logout part you can define several handlers using handlers field.
I have two questions :
If I define a succes_handler service (using for example AuthenticationSuccessHandlerInterface or LogoutHandlerInterface), will it overide the default success handler provided in the framework ?
For the logout part of the configuration, how work the handlersfield ?
For information, in logout part of app/config/security.yml :
handlers: [some.service.id, another.service.id] => Here you have to define services implementing Symfony\Component\Security\Http\Logout\LogoutHandlerInterface. Theses handles do not need to return a response. In my case I created a simple handler that creates a flash message on logout.
success_handler: some.service.id => Here you have to define a service implementing => Symfony\Component\Security\Http\Logout\LogoutSuccessHandlerInterface. This handler have to return a response. This handler is called by the constructor of Symfony\Component\Security\Http\Firewall\LogoutListener (firewall listener).
I tried with success the next solution
https://gist.github.com/marydn/8061424
Seems to be what you are trying to do.