Capturing the login failed event in Symfony3 - json

I have this set up in security.yml;
failure_path: /login
This redirects a user to the log in page if they are not authenticated when trying to access specific URLs set in access_control e.g.
access_control:
- { path: ^/admin, role: ROLE_ADMIN }
But I cannot seem to capture this redirect.
I have tried to use a service for the security.authentication.failure event but this does not work
app.security.authentication_failure_event_listener:
class: MemberBundle\Event\AuthenticationListener
tags:
- { name: kernel.event_listener, event: security.authentication.failure, method: onAuthenticationFailure }
The redirect still occurs and the onAuthenticationFailure is never called. Presumably because authentication has not explicitly failed. The user just isn't logged in and is redirected.
The reason is because I want to give different response based on the expected format. For example I want html requests to go to the login page, but JSON requests should return valid JSON - and not the HTML login form page.
I feel like the FOSRestBundle may handle this, but it seems overkill for this relatively simple need. And its not a RESTFul web site so I shouldn't need that bundle to do this?
I also tried a service to listen for Exceptions but this did not work either. I guess the redirect that occurs doesn't throw an Exception?
app.exception_listener:
class: AppBundle\EventListener\ExceptionListener
tags:
- { name: kernel.event_listener, event: kernel.exception }
So how can I always capture the redirect event that occurs when failure_path is set in security.yml?

My Tip (also for the future) is to "Create a Custom Authentication System with Guard. Much More simple and more flexible/customizable to handle that stuff (specially if a bit complicated with many authenticators like facebook, twitter, etc... and with a remember me option activated...).
But... as showed in the SecurityBundle Configuration ("security") you can set a failure_handler parameter for each login system in that firewall:
security:
firewalls:
your_firewall_name:
form_login:
failure_handler: your_custom_failure_handler_service_name <-- THIS ONE
Then you can create a service injecting the needed dependencies to handle the stuff you need and to return a different response based on the request format.
NOTE: I'm not sure if you can achieve this also creating a listener for the Security authentication event failure, but you can give it a try.

So the answer to this lay in a subtle thing about Symfony and the AccessDeniedException.
In that the Exception is only thrown based on your access controls if the current user is not allowed access. It is not thrown if you are not authenticated at all.
The same is true for any sort of authentication failure hook. Its only called with some sort of auth fails, not if you're simply not logged in and therefore not allowed.
So its done via two new services
app.exception_listener:
class: AppBundle\EventListener\ExceptionListener
tags:
- { name: kernel.event_listener, event: kernel.exception }
app.entry_point:
class: AppBundle\EventListener\EntryPoint
arguments: ["#router"]
The first does capture the AccessDeniedException as you would expect when you're denied access to a resource.
class ExceptionListener
{
public function onKernelException(GetResponseForExceptionEvent $event)
{
$exception = $event->getException();
do {
if ($exception instanceof AccessDeniedException) {
$request = $event->getRequest();
if ('json' == $request->get("format") || $request->isXmlHttpRequest()) {
$json = new JsonResponse(["status" => 0, 'not_logged_in' => true, "msg" => "You must be logged in"]);
$event->setResponse($json);
}
}
} while (null !== $exception = $exception->getPrevious());
}
}
The entry point service needs to be set in security.yml like to
security
firewalls:
main:
entry_point: app.entry_point
This class handles the start of the firewall and handles the redirection to authenticate. I check for the Exception is present and then test for XHR and json format and handle accordingly.
/**
* Returns a response that directs the user to authenticate.
*
* This is called when an anonymous request accesses a resource that
* requires authentication. The job of this method is to return some
* response that "helps" the user start into the authentication process.
*
* Examples:
* A) For a form login, you might redirect to the login page
* return new RedirectResponse('/login');
* B) For an API token authentication system, you return a 401 response
* return new Response('Auth header required', 401);
*
* #param Request $request The request that resulted in an AuthenticationException
* #param AuthenticationException $authException The exception that started the authentication process
*
* #return Response
*/
class EntryPoint implements AuthenticationEntryPointInterface
{
/** #var Router */
protected $router;
public function __construct(Router $router)
{
$this->router = $router;
}
public function start(Request $request, AuthenticationException $authException = null) {
if($authException) {
if('json' == $request->get("format") || $request->isXmlHttpRequest()) {
return new JsonResponse(["status" => 0, 'not_logged_in' => true, "msg" => "You must be logged in"]);
}
}
return new RedirectResponse($this->router->generate("fos_user_security_login"));
}
}

Related

How to redirect to a client callback page after a successful authentication via Identityserver4?

I am creating a login server using Identityserver4. I am using ASP.net core 3.1 for functionality, and angular 9 project for serving static files for login/registeration screens. The angular project is being served from within the .netcore project's wwwroot folder.
My flow goes like this
javascript client calls OIDC user manager's signInRedirect() method with following configurations
This sends a call to my Login method to render the angular's login component. Once the user fills in credentials, a second call is sent to my Login method return this.http.post('Account/Login', {UserName, Password, ReturnUrl}, {headers, responseType:'text'});
On successfull login, I do a return Redirect(model.returnUrl);
[HttpGet]
public IActionResult Login(string returnUrl)
{
return Redirect("/login?ReturnUrl=" + WebUtility.UrlEncode(returnUrl));
}
[HttpPost]
public IActionResult Login([FromBody]LoginViewModel model)
{
if (ModelState.IsValid)
{
var user = _userManager.FindByNameAsync(model.UserName).Result;
var result = _signInManager.PasswordSignInAsync(model.UserName, model.Password, false, false).Result;
if(result.Succeeded)
{
return Redirect(model.ReturnUrl);
}
return Ok(new Error { ErrorMessage = "Invalid credentials" });
}
return Ok(new Error { ErrorMessage = "Some error" });
}
In my network tab, I can see that the return Url which is a call to IdentityServer's authorization endpoint /connect/authorize/callbackis successfull.
It has also made a second call to the actual javascript client in point 1 with the authentication successfull.
However, the problem arises here. This request is returning the HTML as string of the JS clients callback.html instead of actually redirecting to that URL(http://localhost:5003/callback.html)
I don't even have any way to access the URL of the returned HTML, otherwise I would have done a window.location.href. How do I capture the URL of the callback page in angular and redirect to it ?
I would need a few more details to remedy this particular situation. However, I did want to offer my expertise in the form of explaining how this is supposed to work. I have an NPM library imng-auth0-oidc that does this very thing, except that it uses NGRX.
Your callback.html should be a static (non-Angular) HTML page. You can find a copy here callback.html. The purpose of this page is to receive the OAUTH2 response and store the token in localStorage, then redirect the response to your Angular application. Once the application is loaded, you'll now have access to your token that is waiting in localStorage.
-Isaac

Can you set a default route in Fat-Free Framework

If the Fat-Free Framework (F3) sees that an incoming HTTP request does not match any of the routes defined in your application, is there a way to set a default route for these cases. For example, to put at the end of all the routes you have defined in the file, a route where any incoming HTTP request that did not match any preceding routes to go there?
Basically, I would like to route any request that doesn't find a match to a specific class/controller. It seems like this would be something that is possible to do, but I cannot find it anywhere in the F3 docs.
Not able to test it but what if you use a wildcard as last route option?
$f3->route('GET /*')
Instead of registering a default route it's better to register a custom error handler which is able to process 404 and other error codes. This approach allows to reuse the error controller or error function when triggering these errors programmatically; e.g. with Base->error(404).
Register the handler with ONERROR
Parse ERROR with the registered ONERROR handler
It's also possible to use the beforeRoute() and afterRoute() events.
Example
<?php
/** #var base $f3 */
$f3->set('ONERROR', 'App\Module\Error\Controller\ErrorController->onError');
class ErrorController
{
public function onError(Base $f3)
{
if ($f3->get('ERROR.code') == 404) {
/**
* TODO Generate an appropriate HTTP 404 page
*/
// Handled the `404` error.
return true;
}
// Let Fat-Free Framework's default error handler do the work.
return false;
}
}

Allow to get POST data from outter server YII2

i want to make a yii2 route which can receive POST data from other server (of course i know about all risk).
I tried to just send it like usual but i got this error message.
Error 400
Unable to verify your data submission.
more or less is just like this...
public function actionWriteSession()
{
if (isset($_POST))
{
print_r($_POST);
...
write to session
...
}
...
}
Any Advice?
Thanks..
You should disbale csrf verification E.g:
$this->enableCsrfValidation=false;//In your controller context
// Or if you only use this action for sending post from outer server
// you can disbalecsrf token verification only this action. So, in your controller
public function beforeAction($action)
{
if ($action->id == 'writeSession') {
Yii::$app->controller->enableCsrfValidation = false;
}
return parent::beforeAction($action);
}

How to Handle RPC Failure error before reach to OnFailure() at client side ? in GWT

In GWT when RPC fail due to any reason at that time onfailure() method execute at client side.
When onFailure() called at that time actual error is visible in browser's Networks response.
So, My Question is simple how can hide / Modify this actual error with some user friendly Error?
You can override onFailure() method to display what you need, but you cannot modify what a browser shows in the Network tab.
This is an example from my code (LoginException and VersionException are exceptions that my RPC calls throw when necessary):
#Override
public void onFailure(Throwable caught) {
if (caught instanceof LoginException) {
// Redirect a user to login page
Window.Location.assign("/");
} else if (caught instanceof IncompatibleRemoteServiceException ||
caught instanceof VersionException) {
/*
* Here I tell a user that a new version is available,
* so a user needs to refresh the page
*/
} else {
// Here I show a simple message about a connection error
}
}

Send a specific response, or at least a specific HTTP status code, using an exception

In Django, there are a couple of exceptions that are designed to be intercepted by the framework and turned into specific HTTP response codes, such as 404 Not Found and 403 Forbidden.
This is especially useful for request validation, because it allows you to factor out common validation logic into utility functions and cleanup your controller actions.
Whenever the utility functions decide that the current request must be aborted with a specific HTTP error code, they can do so by throwing the relevant exception, without any support code in the controller action, in the form of a return statement or a try/catch.
For example, given a tree of nested REST resources:
static mappings = {
"/authors" (resources: "author") {
"/sagas" (resources: "saga") {
"/books" (resources: "book") {
}
}
}
Then the URL pattern for the Book resource is /authors/$authorId/sagas/$sagaId/books/$id, which means that any of the show(), delete(), or update() actions in BookController have this signature and must include some boilerplate validation logic:
def actionName(int authorId, int sagaId, Book book) {
// -- common validation logic ----------
// fetch parent objects
def author = Author.get(authorId)
def saga = Saga.get(sagaId)
// check that they exists
if (author == null || saga == null || book == null) {
return render(status: NOT_FOUND)
}
// check consistency
if (book.author != author || book.saga != saga || saga.author != author) {
return render(status: BAD_REQUEST)
}
// -- end of commond code --------------
...
}
What is the Grails way of factoring this out into a common method, while still allowing it to terminate request processing whenever an exceptional condition occurs?
I would think the best way is a NotFoundException, ForbiddenException, BadRequestException, and so on, or maybe a generic exception that accepts a HTTP status code. Is there anything like it in Grails? If not, where is the best place to add it? A filter?
Edit: I see now that the standard method is to add an error controller with a matching URL pattern, such as:
"500" (controller: "error")
The problem with this is that Grails will still log full stacktraces for all exceptions, including those that are not programming errors. This spams log files with all sorts of useless tracebacks.
Is there a solution?
You catch the exception in the beforeInterceptor closure of your controller. I resolved this same problem by examining the exception thrown and then acting accordingly. For example:
class BaseController {
/**
* Define DRA exception handlers. This prevents the default Grails
* behavior of returning an HTTP 500 error for every exception.
*
* Instead the exceptions are intercepted and modified according to
* the exception that was thrown. These exceptions are not logged
* whereas application exceptions are.
*/
def beforeInterceptor = {
request.exceptionHandler = { exception ->
def cause = exception.cause
def exceptionBody = [:]
if(cause.class == BadRequestException) {
response.setStatus(HttpStatus.BAD_REQUEST.value()) // HTTP 400 BAD REQUEST
exceptionBody.httpStatus = HttpStatus.BAD_REQUEST.value()
exceptionBody.error = cause.message
}
// render the exception body, the status code is set above.
render exceptionBody as JSON
return true
}
}
}
In order to get this to work you will have to create an ErrorController or something where all server errors are processed and rendered. For example:
class ErrorController {
def serverError() {
def handler = request.exceptionHandler
if(handler) {
request.exceptionHandler = null
if(handler.call(request.exception)) {
return
}
}
}
I have tested this an it does work. I copied the code from a running project that I have been working on. You can build out the if statement in the beforeInterceptor to catch any type of Exception you wish.