Failed CSRF check! in postman when trying POST method (API with slim 3) - rest

I am working with a Slim 3 project and I installed the CSRF package ("slim/csrf": "^0.8.2",)
In order to make POSTs request I am using postman. When sending the action I get the following error:
Failed CSRF check!
Here are my API routes (in this case, focus on the POST route):
<?php
/* RESTful endpoints or routes */
use App\Controllers\api\users\UserController;
$app->group('/api',function () use ($app){
$app->group('/users', function () {
$this->get('', UserController::class.':index');
$this->get('/{id}', UserController::class.':show');
$this->post('', UserController::class.':store');
});
});
Here is the controller supposed to get the info from the POST request where I get the error:
//Save a user via API
public function store($request,$response,$args)
{
//todo: validation!
var_dump($request->getParams());//todo: CSRF check failed!
die();
}
Here is where I registered the CSRF component:
//Register the CSRF Component
$container['csrf'] = function ($container){
return new \Slim\Csrf\Guard();
};
I tried this solution: https://stackoverflow.com/a/48266488/1883256 but it didn't work.
Is there any workaround to make it work? How do I prevent the CSRF to run on my API routes?
* Solved *
As Zamrony P. Juhara suggested, I decided to apply CSRF to the web routes except for the APIs routes.
Grouping all my web routes:
$app->group('',function ()use($app,$container){
/* ******* H O M E ********** */
require __DIR__ . '/web/home/home.php';
/* ********** T O P I C s ********** */
require __DIR__ . '/web/topics/topics.php';
/* ********** C O N T A C T *********** */
require __DIR__ . '/web/contact/contact.php';
/* And so on and etcetera ....*/
/* ********************************************************************************* */
})->add($container->get('csrf'));//Adding CSRF protection only for web routes
And, for example, inside the topics.php routes file I have:
$app->group('/topics',function(){
$this->get('',TopicController::class.':index');
$this->get('/{id}',TopicController::class.':show')->setName('topics.show');
});
And as for the API routes, they stay the same.
Finally, inside my container I commented the following:
//$app->add($container->get('csrf')); //I've commented this line in order to add CSRF for specific routes (all except APIs ones)

You need to make sure that you add Slim\Csrf\Guard middleware to route or application (if you want to apply csrf for all routes). For example
To apply csrf middleware to all routes
$csrf = $container->csrf;
$app->add($csrf);
or to apply for certain routes only
$csrf = $container->csrf;
$app->group('/api',function () use ($app, $csrf){
$app->group('/users', function () {
$this->get('', UserController::class.':index')
->add($csrf);
$this->get('/{id}', UserController::class.':show');
$this->post('', UserController::class.':store')
->add($csrf);
});
});
You also need to make sure that there are Csrf token name/value data passed with request. When you use Postman, you need to find a way to obtain token name key/value pair before execute POST.
Following code is excerpt from Slim Csrf Readme.
// CSRF token name and value
$nameKey = $this->csrf->getTokenNameKey();
$valueKey = $this->csrf->getTokenValueKey();
$name = $request->getAttribute($nameKey);
$value = $request->getAttribute($valueKey);
// Render HTML form which POSTs to /bar with two hidden input fields for the
// name and value:
// <input type="hidden" name="<?= $nameKey ?>" value="<?= $name ?>">
// <input type="hidden" name="<?= $valueKey ?>" value="<?= $value ?>">
Read Slim Csrf Readme for more information.

Related

Slim 3 how to save JWT Token on local storage and use it in my routes for authentication

I want to implement jwt authentication for slim app, i followed tuupora's PRS7 jwt authentication middleware and its working fine when i use Postman because there are options to use header as "Authorization: Bearer tokenString" as here bellow when i request "/auth/ibice" route
these returned data are protected by the middleware-- screenshot
and am using the token string that returned when i request this route "/authtoken" as you see it bellow
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJ3d3cuYXNpZC5ydyIsImlhdCI6MTQ4Njk5MjcyNCwiZXhwIjoxNDg4Mjg4NzI0LCJjb250ZXh0Ijp7InVzZXIiOnsicGhvbmVubyI6IjA3ODQyMjY4OTUiLCJ1c2VyX2lkIjoiMSJ9fX0.1kFu4A16xxJriaRA9CccIJ3M9Bup06buK2LAh13Lzy4",
"user_id": "1"
}
this my middleware.php that protect all routes of "/auth/"
<?php
// Application middleware
$container["jwt"] = function ($container) {
return new StdClass;
};
$app->add(new \Slim\Middleware\JwtAuthentication([
"environment" => "HTTP_X_TOKEN",
"header" => "Authorization",
"path" => ["/auth"],
"passthrough" => ["/authtoken"],
"secret" => "your_secret_key",
"error" => function ($request, $response, $arguments) {
$data["status"] = "error";
$data["message"] = $arguments["message"];
return $response->withStatus(401)
->withHeader("Content-Type", "application/json")
->write(json_encode($data, JSON_UNESCAPED_SLASHES | JSON_PRETTY_PRINT));
},
"callback" => function ($request, $response, $arguments) use ($container) {
$container["jwt"] = $arguments["decoded"];
}
]));
and my routes that i want to request with authorization header that is stored either from cookie or local storage but i have no idea how to do that!!
$app->group('/auth',function(){
$this->get('/admin','App\Controllers\apiController:login')->setName('admin');
//fetch ibice
$this->get('/ibice','App\Controllers\apiController:ibice')->setName('Ibice');
//fetch ibice by id
$this->get('/igice/{id}', 'App\Controllers\apiController:igice')->setName('igiceId');
//search ibice
$this->get('/igice/search/[{query}]', 'App\Controllers\apiController:igice_search')->setName('Igice Search');
//imitwe igize igice
$this->get('/igice/{id}/imitwe','App\Controllers\apiController:imitwe')->setName('Imitwe');
//ingingo ziherereye mumutwe runaka
$this->get('/umutwe/{id}/ingingo', 'App\Controllers\apiController:ingingoBundle')->setName('Ingingo.bundle');
//ingingo ziri mucyiciro runaka
$this->get('/ingingo/icyiciro/{id}', 'App\Controllers\apiController:allstuff')->setName('Icyiciro');
//kuzana ikibazo kimwe kiri mungingo runaka
$this->get('/ingingo/{ingingoid}/question/{id}', 'App\Controllers\apiController:question')->setName('One_Exercise');
//kuzana ibibazo byose biri mungingo
$this->get('/ingingo/{ingingoid}/questions', 'App\Controllers\apiController:questions')->setName('One_Exercise');
//check if the answer is True or False
$this->get('/question/{id}/check/[{query}]','App\Controllers\apiController:checkQuestions')->setName('Check_Questions');
//get questions ids from ingingo
$this->get('/question/{ingingoid}','App\Controllers\apiController:questionsIDs')->setName('Check_Questions');
});
please help me i have no idea how to do this !!
I have never used Slim before but Maybe You can use little Javascript to access localstorage bcz you can't access local storage with php (php works on server side) while localstorage is in browser(client side) here there steps you can do first get Auth token with php by hitting this /authtoken endpoint $app->get('/authtoken') then you need to json_decode returned json into php array then if suppose your php array containing token is $arr then you can you little javascript to save that token in localstorage likes this <script>localStorage.setItem('token', '<?php echo $arr['token'];?>');</script> then whenever you want to read it also you can use javascript to read it from localstorage
<?php
$token = "<script>document.write(localStorage.getItem('token'));</script>"; ?>

Can't resolve route - basic login

so I'm currently looking into Neos CMS and wanted to create a very basic login logic. [for practice]
I basically followed: http://flowframework.readthedocs.io/en/stable/TheDefinitiveGuide/PartIII/Security.html#authentication
My Code: [neos/ being the root dir]
Routes: [neos/Configuration/Routes.yaml] Note that's what I added in the beginning of the file, not the whole content of the file.
-
name: 'Authentication'
uriPattern: 'authenticate'
defaults:
'#package': 'VMP.Auth'
'#controller': 'Authentication'
'#action': 'authenticate'
AuthenticationController.php [neos/Packages/Plugins/VMP.Auth/Classes/VMP/Auth/Controller/]
<?php
namespace VMP\Auth\Controller;
use TYPO3\Flow\Annotations as Flow;
use TYPO3\Flow\Mvc\ActionRequest;
use TYPO3\Flow\Security\Authentication\Controller\AbstractAuthenticationController;
class AuthenticationController extends AbstractAuthenticationController {
/**
* Displays a login form
*
* #return void
*/
public function indexAction() {
}
/**
* Will be triggered upon successful authentication
*
* #param ActionRequest $originalRequest The request that was intercepted by the security framework, NULL if there was none
* #return string
*/
protected function onAuthenticationSuccess(ActionRequest $originalRequest = NULL) {
if ($originalRequest !== NULL) {
$this->redirectToRequest($originalRequest);
}
$this->redirect('someDefaultActionAfterLogin');
}
/**
* Logs all active tokens out and redirects the user to the login form
*
* #return void
*/
public function logoutAction() {
parent::logoutAction();
$this->addFlashMessage('Logout successful');
$this->redirect('index');
}
public function fooAction() {
print "lol";
}
}
NodeTypes.yaml [neos/Packages/Plugins/VMP.Auth/Configuration/]
'VMP.Auth:Plugin':
superTypes:
'TYPO3.Neos:Plugin': TRUE
ui:
label: 'Auth Login Form'
group: 'plugins'
Policy.yaml [neos/Packages/Plugins/VMP.Auth/Configuration/]
privilegeTargets:
'TYPO3\Flow\Security\Authorization\Privilege\Method\MethodPrivilege':
'VMP.Auth:Plugin':
matcher: 'method(TYPO3\Flow\Security\Authentication\Controller\AbstractAuthenticationController->(?!initialize).*Action()) || method(VMP\Auth\Controller\AuthenticationController->(?!initialize).*Action())'
roles:
'TYPO3.Flow:Everybody':
privileges:
-
# Grant any user access to the FrontendLoginLoginForm plugin
privilegeTarget: 'VMP.Auth:Plugin'
permission: GRANT
Settings.yaml [neos/Packages/Plugins/VMP.Auth/Configuration/]
TYPO3:
Neos:
typoScript:
autoInclude:
'VMP.Auth': TRUE
Flow:
security:
authentication:
providers:
'AuthAuthenticationProvider':
provider: 'PersistedUsernamePasswordProvider'
Index.html [neos/Packages/Plugins/VMP.Auth/Resources/Private/Templates/Authentication/]
<form action="authenticate" method="post">
<input type="text"
name="__authentication[TYPO3][Flow][Security][Authentication][Token][UsernamePassword][username]" />
<input type="password" name="__authentication[TYPO3][Flow][Security][Authentication][Token][UsernamePassword][password]" />
<input type="submit" value="Login" />
</form>
**Root.ts2 [neos/Packages/Plugins/VMP.Auth/Resources/TypoScript/]
prototype(VMP.Auth:Plugin) < prototype(TYPO3.Neos:Plugin)
prototype(VMP.Auth:Plugin) {
package = 'VMP.Auth'
controller = 'Authentication'
action = 'index'
}
Problem:
if I call: www.neos.dev/authenticate I get:
Validation failed while trying to call VMP\Auth\Controller\AuthenticationController->authenticateAction().
So I think, the route itself does work. I now added the login form of my VMP.Auth Plugin to some page and logged in (with an existing user). The login form uses /authenticate as its action, but now I get the following error:
Page Not Found
Sorry, the page you requested was not found.
#1301610453: Could not resolve a route and its corresponding URI for the given parameters. This may be due to referring to a not existing package / controller / action while building a link or URI. Refer to log and check the backtrace for more details.
I don't really know what's the issue here. I guess my routing is wrong but I can't see it.
your onAuthenticationSuccess method has:
$this->redirect('someDefaultActionAfterLogin');
which is probably triggered (correctly) now. That tries to redirect to an action someDefaultActionAfterLoginAction in your AuthenticationController but this action does not exist. For starters try
$this->redirectToUri('/') to just have a redirect to the homepage.

Laravel 5: POST without CSRF checking

It seems that Laravel 5 by default applies the CSRF filter to all non-get requests. This is OK for a form POST, but might be a problem to an API that POSTs DELETEs etc.
Simple Question:
How can I set a POST route with no CSRF protection?
Go to app/Http/Middleware/VerifyCsrfToken.php and then enter your routes(for which you want to disable csrf token) in the $except array.
for example:
class VerifyCsrfToken extends BaseVerifier
{
protected $except = [
'/register'
];
}
You can exclude URIs from CSRF by simply adding them to the $except property of the VerifyCsrfToken middleware (app/Http/Middleware/VerifyCsrfToken.php):
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as BaseVerifier;
class VerifyCsrfToken extends BaseVerifier
{
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
'api/*',
];
}
Documentation: http://laravel.com/docs/5.1/routing#csrf-protection
My hack to the problem:
CSRF is now a "middleware" registered globally in App\Http\Kernel.php. Removing it will default to no CSRF protection (Laravel4 behavior).
To enable it in a route:
Create a short-hand key in your app/Providers/RouteServiceProvider.php :
protected $middleware = [
// ....
'csrf' => 'Illuminate\Foundation\Http\Middleware\VerifyCsrfToken',
];
You can now enable it to any Route:
$router->post('url', ['middleware' => 'csrf', function() {
...
}]);
Not the most elegant solution IMO...
just listen to this. Just before 30 minute i was facing this same problem. Now it solved. just try this.
Goto App -> HTTP-> Kernel
open the kernel file.
there you can see : \App\Http\Middleware\VerifyCsrfToken::class,
just disable this particular code using //
Thatz it! This will work!
So that you can remove the middleware from the API calling (if you want so..)

Facebook Auth with AngularJS and Django REST Framework

I am developing a SPA application with AngularJS which uses Django backend for the server. The way that I communicate with the server from the SPA is with django-rest-framework. So now I want to make authentication with facebook (google and twitter too) and I read a lot on this topic and found OAuth.io which is making the authetication on the client SPA side and python-social-auth which is doing the same thing but on the server side.
So currently I have only the client auth, my app is connecting to facebook (with OAuth.io) and login successfully. This process is returning access_token and then I am making a request to my API which have to login this user or create account for this user by given token and this part is not working. So I am not sure where I am wrong, maybe because there isn't a full tutorial about using python-social-auth so maybe I am missing something or.. I don't know..
So some code of this what I have:
On the SPA side: This is the connection with OAuth.io and is working because I am getting the access token. Then I have to make a request to my rest API. backend is 'facebook', 'google' or 'twitter'
OAuth.initialize('my-auth-code-for-oauthio');
OAuth.popup(backend, function(error, result) {
//handle error with error
//use result.access_token in your API request
var token = 'Token ' + result.access_token;
var loginPromise = $http({
method:'POST',
url: 'api-token/login/' + backend + '/',
headers: {'Authorization': token}});
loginPromise.success(function () {
console.log('Succeess');
});
loginPromise.error(function (result) {
console.log('error');
});
});
On the server in my settings.py I have added social plugin to the installed apps, template context preprocessors, some auth backends and that is my file:
INSTALLED_APPS = (
'django.contrib.auth',
'django.contrib.contenttypes',
'django.contrib.sessions',
...,
'rest_framework',
'rest_framework.authtoken',
'api',
'social.apps.django_app.default',
'social'
)
TEMPLATE_CONTEXT_PROCESSORS = ("django.contrib.auth.context_processors.auth",
"django.core.context_processors.debug",
"django.core.context_processors.i18n",
"django.core.context_processors.media",
"django.core.context_processors.static",
"django.core.context_processors.request",
"django.contrib.messages.context_processors.messages",
'social.apps.django_app.context_processors.backends',
'social.apps.django_app.context_processors.login_redirect',)
REST_FRAMEWORK = {
'DEFAULT_AUTHENTICATION_CLASSES': (
'rest_framework.authentication.TokenAuthentication',
)
}
SOCIAL_AUTH_FACEBOOK_KEY = 'key'
SOCIAL_AUTH_FACEBOOK_SECRET = 'secret'
SOCIAL_AUTH_FACEBOOK_SCOPE = ['email']
AUTHENTICATION_BACKENDS = (
'social.backends.open_id.OpenIdAuth',
'social.backends.facebook.FacebookOAuth2',
'social.backends.facebook.FacebookAppOAuth',
'social.backends.google.GoogleOpenId',
'social.backends.google.GoogleOAuth2',
'social.backends.google.GoogleOAuth',
'social.backends.twitter.TwitterOAuth',
'django.contrib.auth.backends.ModelBackend',
)
In my views.py of the API I have the following (I found it here):
from django.contrib.auth.models import User, Group
from rest_framework import viewsets, generics
from rest_framework.views import APIView
from rest_framework.response import Response
from rest_framework import authentication, permissions, parsers, renderers
from rest_framework.authtoken.serializers import AuthTokenSerializer
from rest_framework.decorators import api_view, throttle_classes
from social.apps.django_app.utils import strategy
from rest_framework.permissions import IsAuthenticated, IsAuthenticatedOrReadOnly
from django.contrib.auth import get_user_model
from django.db.models.signals import post_save
from django.dispatch import receiver
from rest_framework.authtoken.models import Token
class ObtainAuthToken(APIView):
throttle_classes = ()
permission_classes = ()
parser_classes = (parsers.FormParser, parsers.MultiPartParser, parsers.JSONParser,)
renderer_classes = (renderers.JSONRenderer,)
serializer_class = AuthTokenSerializer
model = Token
# Accept backend as a parameter and 'auth' for a login / pass
def post(self, request, backend):
serializer = self.serializer_class(data=request.DATA)
if backend == 'auth':
if serializer.is_valid():
token, created = Token.objects.get_or_create(user=serializer.object['user'])
return Response({'token': token.key})
return Response(serializer.errors, status=status.HTTP_400_BAD_REQUEST)
else:
# Here we call PSA to authenticate like we would if we used PSA on server side.
user = register_by_access_token(request, backend)
# If user is active we get or create the REST token and send it back with user data
if user and user.is_active:
token, created = Token.objects.get_or_create(user=user)
return Response({'id': user.id , 'name': user.username, 'userRole': 'user','token': token.key})
#strategy()
def register_by_access_token(request, backend):
backend = request.strategy.backend
user = request.user
user = backend._do_auth(
access_token=request.GET.get('access_token'),
user=user.is_authenticated() and user or None
)
return user
And finally I have these routes in urls.py:
...
url(r'^api-auth/', include('rest_framework.urls', namespace='rest_framework')),
url(r'^api-token-auth/', 'rest_framework.authtoken.views.obtain_auth_token'),
url(r'^api-token/login/(?P<backend>[^/]+)/$', views.ObtainAuthToken.as_view()),
url(r'^register/(?P<backend>[^/]+)/', views.register_by_access_token),
...
Everytime when I try to do auth, OAuth.io is working and the rqest to api returns
detail: "Invalid token"
I think that I missed something in the configuration of python-social-auth or I am doing everything wrong. So I will be glad if anyone has some ideas and want to help :)
Add the following line to your ObtainAuthToken class
authentication_classes = ()
and your error {"detail": "Invalid token"} will go away.
Here's why...
Your request contains the following header
Authorization: Token yourAccessToken
yet you have defined rest_framework.authentication.TokenAuthentication in DEFAULT_AUTHENTICATION_CLASSES.
Based on this Django thinks you want to perform token authentication as you have passed a Token in. It fails because this is an access token for facebook and doesn't exist in your django *_token database, hence the invalid token error. In your case all you need to do is tell Django not to use TokenAuthentication for this view.
FYI
Keep in mind you may encounter further errors as your code execution was halted before the post method of ObtainAuthToken executed. Personally when trying to step through your code I got the error
'DjangoStrategy' object has no attribute 'backend'
on
backend = request.strategy.backend
and resolved it by changing to
uri = ''
strategy = load_strategy(request)
backend = load_backend(strategy, backend, uri)
Additionally you should update your you register_by_access_token function as it doesn't line up with the working code from the blog you referenced. The blog author posted his latest code here. Your version doesn't pull the token out of the auth header which is required if you want to use it to auth with a third party like facebook.
Yea. Solved. The settings are not right and you need to add permissions.
REST_FRAMEWORK = {
# Use hyperlinked styles by default.
# Only used if the `serializer_class` attribute is not set on a view.
'DEFAULT_MODEL_SERIALIZER_CLASS':
'rest_framework.serializers.HyperlinkedModelSerializer',
# Use Django's standard `django.contrib.auth` permissions,
# or allow read-only access for unauthenticated users.
'DEFAULT_PERMISSION_CLASSES': [
'rest_framework.permissions.DjangoModelPermissionsOrAnonReadOnly'
]
}
and some info about pipeline:
SOCIAL_AUTH_PIPELINE = (
'social.pipeline.social_auth.social_details',
'social.pipeline.social_auth.social_uid',
'social.pipeline.social_auth.auth_allowed',
'social.pipeline.social_auth.social_user',
'social.pipeline.user.get_username',
'social.pipeline.social_auth.associate_by_email',
'social.pipeline.user.create_user',
'social.pipeline.social_auth.associate_user',
'social.pipeline.social_auth.load_extra_data',
'social.pipeline.user.user_details'
)
I'm using tools just like you, but I provide my login/register/.... with
django-allauth package, and then use django-rest-auth for API handling.
You just need follow the installation instruction, then use them for your rest APIs.
Adding allauth and rest-auth to your INSTALLED_APPS:
INSTALLED_APPS = (
...,
'rest_framework',
'rest_framework.authtoken',
'rest_auth'
...,
'allauth',
'allauth.account',
'rest_auth.registration',
...,
'allauth.socialaccount',
'allauth.socialaccount.providers.facebook',
)
Then add your custom urls:
urlpatterns = patterns('',
...,
(r'^auth/', include('rest_auth.urls')),
(r'^auth/registration/', include('rest_auth.registration.urls'))
)
Finally, add this line:
TEMPLATE_CONTEXT_PROCESSORS = (
...,
'allauth.account.context_processors.account',
'allauth.socialaccount.context_processors.socialaccount',
...
)
These two packages works like a charm, and you don't need to have concern about any type of login.registration, because allauth package handles both django model login and oAuth login.
I hope it helps

Zend Framework: How to redirect to original url after login?

I'm trying to implement a login system that will be smart enough to redirect a user back to the page they were on before they decided (or were forced to) go to the login page.
I know this seems like a similar question to this one, and this one, but they do not address both of my scenarios.
There are two scenarios here:
User specifically decides to go to login page:
<a href="<?php echo $this->url(array(
'controller'=>'auth',
'action'=>'login'), 'default', true); ?>">Log In</a>
User is redirected because they tried to access protected content:
if (!Zend_Auth::getInstance()->hasIdentity()) {
$this->_helper->redirector('login', 'auth');
}
How can I implement a solution for this without displaying the "redirect to" url in the address bar?
Save the destination URL in the session. I guess you have some kind of access pre-dispatch plug-in. Do it there. And then, in the login form handler, check for the destination URL in the session, and redirect to it after a successful authentication.
Sample code from my project:
class Your_Application_Plugin_Access extends Zend_Controller_Plugin_Abstract {
public function preDispatch(Zend_Controller_Request_Abstract $request) {
foreach (self::current_roles() as $role) {
if (
Zend_Registry::get('bootstrap')->siteacl->is_allowed(
$role,
new Site_Action_UriPath($request->getPathInfo())
)
) return; // Allowed
}
$this->not_allowed($request);
}
private function not_allowed(Zend_Controller_Request_Abstract $request) {
$destination_url = $request->getPathInfo();
// If the user is authenticted, but the page is denied for his role, show 403
// else,
// save $destination_url to session
// redirect to login page, with $destination_url saved:
$request
->setPathInfo('/login')
->setModuleName('default')
->setControllerName('login')
->setActionName('index')
->setDispatched(false);
}
...
}
Here, current_roles() always contains 'guest', which is unauthenticated user, for which Zend_Auth::hasIdentity() is false.