For our Laravel 5.3 API, I want to remove the need for CSRF tokens since everything is handled with OAuth2 and JWT.
Currently the API operates on the subdomain: api.example.com
I tried this but it still requests CSRF tokens:
class VerifyCsrfToken extends BaseVerifier
{
protected $except = [
'api.*'
];
}
I'd also like to be able to disable the CSRF protection when I am doing random route tests on the main app which is on app.example.dev, but adding 'app.*' to $except also doesn't work.
Is it possible to do this?
You can overwrite the shouldPassThrough method in the BaseVerifier class, where-in it would support a subdomain in $except such as api.yourdomain.com
/**
* Determine if the request has a URI that should pass through CSRF verification.
*
* #param \Illuminate\Http\Request $request
* #return bool
*/
protected function shouldPassThrough($request)
{
foreach ($this->except as $except) {
if (Str::is($except, $request->url())) {
// break out of CSRF check
return true;
}
}
return false;
}
Related
Normally everything works fine on localhost. When I throw the project to the server, only the homepage works.
When I add manually to this section from the database, I can pull the data.
Web site here: http://elsetea.com/public (running)
but when I send a message from the contact section, my routes don't work (http://elsetea.com/public/#messages). What can I do in this situation.
Contact routing is as follows.
Route::namespace('Frontend')->group(function () {
Route::post('contact', 'ContactPageController#store')->name('contact-page.store');
});
I have taken the action as below and sending the message has been successful. But there was no informative message that should come out later.
<?php
namespace App\Http\Middleware;
use Illuminate\Foundation\Http\Middleware\VerifyCsrfToken as Middleware;
class VerifyCsrfToken extends Middleware
{
/**
* Indicates whether the XSRF-TOKEN cookie should be set on the response.
*
* #var bool
*/
protected $addHttpCookie = true;
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
//
'/contact'
];
}
I would like a group of routes to be accessible with either the standard auth:api middleware, or via the CheckClientCredentials middleware.
I can't see how this would be possible as there is no ability to set middleware as only requiring one of the listed middleware.
Is there a Passport middleware which allows any type of API authentication that I don't know about?
Or is there a clean way of creating a custom middleware which tests for either of the middleware?
I took Joshuas advice about the similar answer here and created a new middleware encompassing the two auth middleware.
Working Middleware class for anyone else hitting this issue below. This will try the Auth guard middleware first and if this fails to authenticate the request it will then try to authenticate using the Client Credentials Passport middleware.
namespace App\Http\Middleware;
use Closure;
use Illuminate\Auth\AuthenticationException;
use Illuminate\Auth\Middleware\Authenticate as AuthGuardMiddleware;
use Laravel\Passport\Http\Middleware\CheckClientCredentials as ClientCredMiddleware;
class AuthenticateWithApiOrClientCreds
{
/**
* Authenticate a request with either Authenticate OR CheckClientCredentials Middleware
*
* #param $request
* #param Closure $next
* #param mixed ...$scopes
* #return \Illuminate\Http\RedirectResponse|\Illuminate\Routing\Redirector
*/
public function handle($request, Closure $next, ...$scopes)
{
$auth_guard_middleware = app()->make(AuthGuardMiddleware::class);
try {
$response = $auth_guard_middleware->handle($request, $next, 'api');
} catch (AuthenticationException $e) {
$client_cred_middleware = app()->make(ClientCredMiddleware::class);
$response = $client_cred_middleware->handle($request, $next, ...$scopes);
}
return $response;
}
}
Laravel doesn't provide the OR middleware. Although there are several workarounds like it was previously asked here.
If you are looking for the way to change the api.php default middleware (default to auth:api), you can see it in directory: app\Providers\RouteServiceProvider.php with function name: mapApiRoutes().
I ended up storing all the routes that need authentication in a separate route file called api-authenticated.php. In RouteServiceProvider.php I'm applying the web and api middleware groups for those routes conditionally based on the presence of the Authorization header. When the Authorization header is set, you know the user is an API client.
In Providers/RouteServiceProvider.php:
/**
* Define the "api" routes for the application.
*
* These routes are typically stateless.
*
* #return void
*/
protected function mapApiRoutes()
{
Route::prefix('api')
->middleware('api')
->namespace($this->apiNamespace)
->name('api.')
->group(__DIR__ . '/../../routes/api.php');
}
/**
* Define the authenticated "api" routes for the application.
*
* These routes are typically stateless, but are also used in
* a stateful context as the first-party uses cookies for
* authenticaton
*
* #return void
*/
public function mapAuthenticatedApiRoutes()
{
$stateless = request()->hasHeader('Authorization');
Route::prefix('api')
->middleware($stateless ? ['api', 'auth:api'] : ['web', 'auth'])
->namespace($this->apiNamespace)
->name('api.')
->group(__DIR__ . '/../../routes/api-authenticated.php');
}
I've set up a laravel app with client authentification. I send it my client id and client secret and it gives me a token. I'm able to log in to my laravel app, but I can't figure out how to get the id of the client that's been authorized.
I've seen hints to use auth()->user()->Token()->getAttribute('client_id') to get the client id, but since I'm only using clients there is no user and I get an error about trying to call Token() on a null object. Auth::id() also returned nothing. I grabbed the token from the header with Request::header('Authorization'), but it didn't match anything in the database.
I'm assuming you're using client credentials grant tokens, and the CheckClientCredentials middleware.
You can get this information from the bearer token, but it's not that straightforward. You would need to create a new PSR7 request with the token, and send it off to the oauth server to have it converted to readable data.
This is already done inside the CheckClientCredentials middleware provided by Passport. So, one way to do this would be to extend the CheckClientCredentials middleware and just manually set the needed fields on the request object from inside the middleware.
First, create app/Http/Middleware/MyCheckClientCredentials.php:
namespace App\Http\Middleware;
use Closure;
use Illuminate\Auth\AuthenticationException;
use League\OAuth2\Server\Exception\OAuthServerException;
use Symfony\Bridge\PsrHttpMessage\Factory\DiactorosFactory;
use Laravel\Passport\Http\Middleware\CheckClientCredentials;
class MyCheckClientCredentials extends CheckClientCredentials
{
/**
* The Resource Server instance.
*
* #var \League\OAuth2\Server\ResourceServer
*/
private $server;
/**
* Handle an incoming request.
*
* #param \Illuminate\Http\Request $request
* #param \Closure $next
* #param mixed ...$scopes
* #return mixed
* #throws \Illuminate\Auth\AuthenticationException
*/
public function handle($request, Closure $next, ...$scopes)
{
$psr = (new DiactorosFactory)->createRequest($request);
try {
$psr = $this->server->validateAuthenticatedRequest($psr);
// This is the custom line. Set an "oauth_client_id" field on the
// request with the client id determined by the bearer token.
$request['oauth_client_id'] = $psr->getAttribute('oauth_client_id');
} catch (OAuthServerException $e) {
throw new AuthenticationException;
}
$this->validateScopes($psr, $scopes);
return $next($request);
}
}
Next, update your app/Http/Kernel.php to use your custom middleware instead of the build in Passport middleware:
protected $routeMiddleware = [
'client' => \App\Http\Middleware\MyCheckClientCredentials::class,
];
Apply the middleware to your route as normal:
Route::get('/user', function(Request $request) {
// Should show "oauth_client_id" field.
dd($request->all());
})->middleware('client');
If you don't want to do this inside a middleware, you can study how the Passport middleware works and reuse this code in some type of service if you'd like.
NB: all untested.
I had to do something similar in a logger middleware of mine:
.......................
$user = $request->user();
if($user) {
// assume the authorization header exists, since the user is authenticated
$header = $request->headers->get('authorization');
if($header) { // authorization header is not set when testing via Passport::actingAs()
/**
* Stolen from League\OAuth2\Server\AuthorizationValidators\BearerTokenValidator#63
*/
// Get the actual jwt string from the header
$jwt = trim(preg_replace('/^(?:\s+)?Bearer\s/', '', $header));
// Parse the token from the string
$token = (new Lcobucci\JWT\Parser())->parse($jwt);
// Get the ID from the token
$oauthClientId = $token->getClaim('aud');
}
}
.......................
I am trying to write a method in laravel 5.2 for the patch HTTP verb for REST, but it's showing a token mismatch. This is my VerifyCsrfToken class:
class VerifyCsrfToken extends BaseVerifier
{
/**
* The URIs that should be excluded from CSRF verification
*
* #var array
*/
protected $except = ['v0.1/api/mc-api','v0.1/api/mc-api/{mc_api}'];
}
In my routes, I have the following:
Route::group(array('prefix'=>'v0.1/api'),function(){
Route::resource('mc-api','ApiController');
});
Route::any('v0.1/api/mc-api/*',['nocsrf'=>'true','uses'=>'ApiController#update']);
Based on chat I have tried changing my routes to the following but it doesn't help:
Route::any('v0.1/api/mc-api/*',['nocsrf'=>true,'uses'=>'ApiController#update']);
Nor does:
Route::any('v0.1/api/mc-api/{mc_api}', 'ApiController#update');
In your class VerifyCsrfToken $except array
change
'v0.1/api/mc-api/{mc_api}'
to
'v0.1/api/mc-api/*'
I want to call my functions from android side.
now, I want to send post values from android side.
How can I disable csrf token for some functions ?
because without it I got error message .
like this:
Route::post('mobile/android/getlastnews','NewsController#getLastNews');
For Laravel 5.2, inside app/Http/Middleware/VerifyCsrfToken.php you just need to add it to the $except array.
class VerifyCsrfToken extends BaseVerifier
{
/**
* The URIs that should be excluded from CSRF verification.
*
* #var array
*/
protected $except = [
'mobile/android/getlastnews'
];
}
If you wanted to allow all routes for mobile/ you can do the following
protected $except = [
'mobile/*'
];
Open app/Http/Middleware/VerifyCsrfToken.php and in the protected $except = []; array add your URL that you want to be skipped for CSRF validation.