I have a route with a response in json to make accessible for logged in users but also anonymous users but in this case with a response with a status code 401.
I tried to add a firewall;
route_name:
pattern: ^/path
anonymous: true
but with this configuration, i get always an anonymous user (in profiler), even if user is logged in.
I tried also adding configuration in access_control instead;
access_control:
- { path: ^/path, role: IS_AUTHENTICATED_ANONYMOUSLY }
but i keep getting the login form instead.
Any idea how to handle this case? Thanks
Related
This is a Symfony 3.4 application with a website frontend and a mobile app accessing the same backend. Users can log in by
submitting a username and password (form login)
authentication with a google account
authentication with a facebook account
Previously, there was only one way to log in (username+password). Authentication and the firewall configuration worked. Adding the social network authentication required changes in the firewall configuration and now the guard authentication is partly broken.
What I'm confident about so far is that we need separate firewalls for the web users (main) and mobile app users (api). Web users are authenticated once and then the logged in user info is stored in the session cookie but the mobile app users are authenticated with every incoming request. When mobile users log in successfully, they get a jwt token as a response which they will send with every subsequent request.
What seems to be the biggest problem is the rest of the firewall configuration. In the current configuration, the "main" firewall works as intended. But something about the "api" firewall for mobile users is broken. Logging in works with the same guard authenticators so that they return the jwt token as expected. But the subsequent requests that are sent with the token all result in a 403 access denied response. I suspect that lexic authenticator never gets the jwt token from the request so it looks like the user never logged in.
The authenticators have been tested and they seem to work correctly for both web users and mobile users. The configuration of lexik jwt authenticator is also correct - or it hasn't changed since the time mobile users still had a single authenticator. This means the keys, pass phrase, token ttl.
An idea that might work would be to have a separate firewall for the mobile login urls and the rest of the mobile routes because they're handled by different authenticators. I tried it without any improvement in the situation: logging in works but jwt authentication doesn't. The relevant parts of security.yml below:
security:
...
providers:
db_users:
entity: { ... }
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js|api)/(password/reset)
security: false
api:
pattern: ^/api/
stateless: true
lexik_jwt: ~
anonymous: false
guard:
provider: db_users
authenticators:
- main.form_login_authenticator
- main.google_login_authenticator
- main.fb_login_authenticator
- lexik_jwt_authentication.jwt_token_authenticator
entry_point: main.form_login_authenticator
main:
pattern: ^/
guard:
provider: db_users
authenticators:
- main.form_login_authenticator
- main.google_login_authenticator
- main.fb_login_authenticator
entry_point: main.form_login_authenticator
form_login:
remember_me: true
login_path: login
check_path: login
always_use_default_target_path: true
default_target_path: /redirect
target_path_parameter: _target_path
use_referer: false
require_previous_session: false
anonymous: true
...
What's broken here? How should I debug this issue? (other than using postman to emulate json requests from the mobile app)
Additional info: All three custom authenticators create a jwt token, for example:
public function onAuthenticationSuccess(Request $request, TokenInterface $token, $providerKey)
{
// mobile users:
if ( $request->getRequestFormat() == 'json') {
return new JsonResponse(['token' => $this->jwtManager->create($token->getUser())]);
// web users handled here
}
Then about the access patterns (or how the request is handled and by which authenticator), here's an example from main.form_login_authenticator
public function supports(Request $request)
{
return ('login' === $request->attributes->get('_route') || 'api_login_check' === $request->attributes->get('_route'))
&& $request->isMethod('POST');
}
The authenticators seem to work as intended, though. Logging in works. What doesn't work is staying logged in with the jwt token.
Finally it works. The main idea of the solution was to separate the firewalls: one for mobile login routes and another for the rest of the mobile (api) routes. Here's the configuration that works, omissions marked with three dots. main firewall configured as shown in the question.
security:
...
firewalls:
...
api_login:
pattern: ^/api/(login|google-login|fb-login)
stateless: true
anonymous: true
guard:
provider: db_users
authenticators:
- main.form_login_authenticator
- main.google_login_authenticator
- main.fb_login_authenticator
entry_point: main.form_login_authenticator
api:
pattern: ^/api
stateless: true
guard:
provider: db_users
authenticators:
- lexik_jwt_authentication.jwt_token_authenticator
main:
...
The prefix of the authenticator service names originally came from the name of the firewall main which doesn't make sense anymore as they're shared between multiple firewalls now.
First, for the api firewall you should only have the jwt authenticator because even if the auth. Is done with social network you must generate your own jwt token.
Secondly, can you send us your access pattern ? This can give us more clues.
Third, iam not sure that the entrypoint for the api firewall should be the same that the form one (https://symfony.com/doc/current/components/security/firewall.html#entrypoint)
Can someone explain why I can access login route when I'm already logged in even though I've set in security.yaml for only anonymous users to access the route?
access_control:
- { path: ^/login, roles: IS_AUTHENTICATED_ANONYMOUSLY }
How can I prevent from accessing this route when I'm logged in ?
To answer your first question, the user always has the IS_AUTHENTICATED_ANONYMOUSLY role, even when it's authenticated.
https://symfony.com/doc/current/security/access_control.html
I am identifying access to certain routes through x509 digital certificate (pre-authentication).
For this I defined the security.yml as follows:
- security:
providers:
x509Provider_Provider:
id: x509Provider_Service
firewalls:
dev:
pattern: ^/(_(profiler|wdt)|css|images|js)/
security: false
xes:
pattern: ^/xes
x509:
provider: x509Provider_Provider
user: "SSL_CLIENT_S_DN"
access_control:
- { path: ^/xes, roles: ROLE_ADMIN, requires_channel: https }
Where I get the SSL_CLIENT_S_DN and I use as username.
In the Provider
x509Provider class implements UserProviderInterface
consult the database if the user exists and has access permissions and based on this
I create an object
**x509User class implements UserInterface, EquatableInterface**
where I store information access permissions and other data.
Apparently it works correctly, in the Symfony profiler I have a user with their roles and marked as authenticated.
The problem is: whenever I access a route (pattern: ^ / XES) the authentication process and access to the database is launched to obtain roles and user permissions.
It should not authenticate only the first time and once created the session use in subsequent requests ?.
I hope I have explained my question correctly.
Greetings
Nowadays, it's quite usual to authenticate the user via an API key
(when developing a web service for instance). The API key is provided
for every request and is passed as a query string parameter or via an
HTTP header.
I think that it's the same for the token.
I'm running a Symfony 2.6 application that uses the FOSUserBundle. When users log in they are always 'remembered' because I've configured these settings in my security.yml:
http://symfony.com/doc/current/cookbook/security/remember_me.html
Security.yml:
remember_me:
key: "%secret%"
lifetime: 604800 # One week
path: /
domain: ~
always_remember_me: true
secure: true
This creates a REMEMBERME cookie when the user logs in; that's all working fine. However, when I store the cookies (For e.g. by using EditThisCookie in Google Chrome), I can still re-use these cookies after the user has logged out.
My scenerio:
- User logs in
- Copy REMEMBERME cookie
- User logs out
- Paste REMEMBERME cookie and refresh the page
- The user is logged in again (which I do not want!)
How can I prevent these cookies from being used again after the user has logged out?
The REMEMBERME cookie is not used to restore an existing session, but to login again the user. So of course you can login again with the cookie after you've been logged out, else what would be the purpose of REMEMBERME? In other words, that's expected behaviour.
Anyway you can add a "Logout" event listener that deletes the cookie.
http://api.symfony.com/2.5/Symfony/Component/Security/Http/Firewall/LogoutListener.html
I can't reccomend a specific tutorial about that but you can find tons of informations on the net. If you don't find enough, add a new specific question on how to implement a logout handler.
I want to check whether a user is logged before calling most of the methods in my web application. I don't know how to do that. I want something like before_filter in Ruby On Rails. I have checked the before filter in the symfony2 documentation but it does not help me. I need a real life example for the login.
This is the link I have checked. http://symfony.com/doc/current/cookbook/event_dispatcher/before_after_filters.html
There are a number of ways to handle access control in Symfony. The most basic is with URL matching, which is handy when you want to restrict access to a URL like /admin and anything that follows. This is configured in security.yml.
# app/config/security.yml
security:
access_control:
- { path: ^/admin, roles: ROLE_ADMIN }
This page shows the different ways to secure your application:
http://symfony.com/doc/current/book/security.html#access-control
There is even a way to secure any service even if it's not a controller:
http://symfony.com/doc/current/cookbook/security/securing_services.html