Laravel Signed URL Middlware - laravel

So I'm working with URL generation with laravel and I was wondering if you can use multiple routes with the same URL Signature. I have my routes grouped into the signed middlware group as below:
Route::middleware('signed')->group(function () {
Route::get('load/client/{client}/quote/{quote}', 'QuoteController#getClientQuote')->name('clientquote');
Route::post('submit/client/{client}/quote/{quote}', 'QuoteController#submitClientQuote')->name('clientquote');
Route::post('save/client/{client}/quote/{quote}', 'QuoteController#saveClientQuote')->name('clientquote');
Route::get('/client/{client}/quote/{quote}', 'QuoteController#getClientQuoteBlade')->name('clientquote');
});
I also have the URL generated in an email here:
'url' => URL::signedRoute('clientquote', ['client' => $event->client, 'quote' => $event->quote]),
The email is sent through mailgun and when I click on the link in the email it takes me to the last get route in my middlware. Once that route's component is mounted i make a secondary axios call to get the load route:
axios
.get(
"/load/client/" + clientNumber + "/quote/" + quoteNumber + window.location.search
)
leaving off the other code but I get a 403 error and just to verify in the console here is the route:
/load/client/2/quote/1?signature=5d2e3273e51429ba688f85969911bd3a279d36348f2e74bd10f871a56218e722
is what I'm asking for even possible or do I need to generate a new Signed URL for each subsequent route?

If you have a route under signed middleware, it means that all those routes should have valid signature. Otherwise it will give you 403 error.
When you call URL::signedRoute(..), that signature particularly represent that specific route url. So if you try to attach the same signature to a different route altogether, it will not work.
What you can do is, when you are sending the data to blade view in clientquote route, send the signed url generated as well for /load/client/ route and then use that in axois.

Related

Laravel - Get fragment from URL

I am receiving an auth code from Microsoft that looks like this:
localhost:8000/token#auth=123456
In Laravel I have a route that points to a controller:
Route::get('/token', 'AuthController#getToken');
The function looks like this:
public function getToken(Request $request) {
session([
'request' => $request->fullUrl(),
]);
}
The problem is that fullUrl() returns localhost:8000/token so I can't see the auth token.
Is there a way round this?
Assuming you get that URL as a string, you can use parse_​url to get the fragment part:
<?php
$url = 'localhost:8000/token#auth=123456';
var_dump(parse_url($url, PHP_URL_FRAGMENT));
will output: string(11) "auth=123456"
If you want to get the value after the hash mark (#) as shown in a user's browser: This isn't possible with "standard" HTTP as this value is never sent to the server (hence it won't be available in $_SERVER["REQUEST_URI"] or similar predefined variables). You would need some sort of JavaScript magic on the client side, e.g. to include this value as a POST parameter.
So when you call :
localhost:8000/token#auth=123456
Your server get this :
localhost:8000/token
The browser delete everything from the hash (#) of your url, then it pass to the server http process

When I click the email verification link sended by laravel to my gmail it redirects me to the page which says - 403 This action is unauthorized

In my laravel project, i created the authentication successfully. register, login, and logout works fine. i did made the email verification, it sends the verification email to the user successfully. but when i click the verification email sended to my gmail by laravel, it redirects me to a page which says: 403 This action is unauthorized.
I am using Laravel 7.
my routes in web.php file
Route::get('/', function () {
return redirect(app()->getLocale());
});
Route::get('email/verify', 'Auth\VerificationController#show')->name('verification.notice');
Route::get('email/verify/{id}', 'Auth\VerificationController#verify')->name('verification.verify');
Route::post('email/resend', 'Auth\VerificationController#resend')->name('verification.resend');
Route::group([
"prefix" => "{language}",
'where' => ['locale' => '[a-zA-Z]{2}'],
'middleware' => 'setlocale'
], function () {
Auth::routes();
Route::get('/home', 'HomeController#index')->name('home');
});
I set the email verification routes manually, because priviously when i set the
Auth::routes(['verify' => true]);
I got an error, so i set the email verification outside the route group manually to fix that error.
In verify.blade.php the resend verification email also works fine, it resends the verification email successfully.
I have solved the problem. the problem was that verification.verify route was wrong,
when you set the
Auth::routes(['verify' => true]);
the actual verification.verify route is like this
email/verify/{id}/{hash}
The verification.verify route calls verify method which is located in vendor\laravel\ui\auth-backend\VerifiesEmails.php In this method there are two if statements which throws AuthorizationException which causes 403 This action is unauthorized page. The first if checks the route user id with the current authenticated user id, and the second if checks the route hash with the current authenticated user hash.
And make soure to configure your trusted proxies correctly.
check in laravel documentation click here.
I hope this help you.
I my case I was clicking the wrong 'verify email url' Because my all verfication emails in google were accumulating under same 'Subject' so instead of clicking the last email, I was clicking the first email

Is this a proper Laravel Passport use case?

So think of my application as a CMS (laravel 5.7). I'm slowly adding in more javascript to make it more reactive. So I had the usual validation logic that makes sure the user is logged in and all that. But now when I use Vue to submit a comment payload it looks a little like this:
So looking at this, anyone could just change/mock the this.user.id to any number, I would like to also send a login token with the payload which then gets validated in the backend once the server receives the post request.
In the backend, ideally I'd want to have some kind of safe guard that it checks whether the api_token of the user matches with this.user.id to ensure the user.id wasn't mocked on the front end.
I read this portion: https://laravel.com/docs/5.7/passport#consuming-your-api-with-javascript
Part of it says:
This Passport middleware will attach a laravel_token cookie to your outgoing responses. This cookie contains an encrypted JWT that Passport will use to authenticate API requests from your JavaScript application. Now, you may make requests to your application's API without explicitly passing an access token:
But I'm still a bit unsure how that JWT gets generated in the first place. I don't have the vue components for the create token crud added because I want it to be done automatically. I think I'm slightly overthinking this..
Is this a good use case for Laravel Passport? I was looking through the tutorial and right now I don't have a need for custom oauth token creations and all the crud. I just want a unique token to be saved on the user side, that can expire, but also be used to validate requests. Am I on the right track here with Passport or should I use a different approach?
postComment(){
axios.post('/api/view/' + this.query.id+'/comment',{
id: this.user.id,
body: this.commentBox
})
.then((response) =>{
//Unshift places data to top of array, shifts everything else down.
this.comments.unshift(response.data);
this.commentBox = '';
document.getElementById("commentBox").value = "";
flash
('Comment posted successfully');
})
.catch((error) => {
console.log(error);
})
},
Update - Reply to Jeff
Hi! Thanks for your answer. It's not an SPA (might be in the future), but the comment box and the comment section is also integrated with websockets and there's a laravel Echo instance on it.
I guess where I'm feeling uncertain is the security of it.
I pass a user prop with :user="{{Auth::check() ? Auth::user()->toJson() : 'null'}}" into the vue component that contains the postComment() function.
This is where the id: this.user.id comes from. The route is defined in the api.php in a route middleware group for ['api'] like so:
Route::group(['middleware' => ['api']], function(){
Route::post('/view/{query}/comment','CommentController#store');
});
In my controller which calls a service to create the comment, the $request
public function makejson(createNewCommentRequest $request, Query $query){
$comment = $query->comments()->create([
'body' => $request->get('body'),
])->user()->associate(User::find($request->id));
$id = $comment->id;
$comment->save();
}
The createNewCommentRequest is a FormRequest class.
For now the authorize() function just checks whether the request()->id is an int:
public function authorize()
{
if(is_int(request()->id)){
return true;
}
return false;
}
From within there if I log the request(), all it outputs is:
array ( 'id' => 1, 'body' => 'gg', )
I thought I would need to add logic to authorize the request based on whether the user token and the request() yield the same user id? I'd want to avoid the scenario where someone can modify the post request and comment using another users id.
In the Network section of devtools, in the Request headers, i see it pushed a laravel_token cookie. I'm assuming that laravel_token is what stores the user session? If so, how would one validate based on that token?
I was playing around and added the route:
Route::get('/token', function() {
return Auth::user()->createToken('test');
});
When I went to it i got the following:
{
"accessToken": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImp0aSI6ImE4NDE2NGVkM2NkODc5NDY3MzAxYzUyNmVkN2MyMGViZTllNzJlMGMzMjRiMmExNWYzZDgwZGNmMzEzMDk1MTRmNTY1NGMxYWUwMTE2ZGRkIn0.eyJhdWQiOiIxIiwianRpIjoiYTg0MTY0ZWQzY2Q4Nzk0NjczMDFjNTI2ZWQ3YzIwZWJlOWU3MmUwYzMyNGIyYTE1ZjNkODBkY2YzMTMwOTUxNGY1NjU0YzFhZTAxMTZkZGQiLCJpYXQiOjE1NDY1NTQzNDEsIm5iZiI6MTU0NjU1NDM0MSwiZXhwIjoxNTc4MDkwMzQwLCJzdWIiOiIxIiwic2NvcGVzIjpbXX0.NMETCBkOrMQGUsXlcas6CvTFJ0xRC8v4AJzC5GtWANdl8YsPBGlyCozMe1OGc8Fnq8GC_GZFkKmMT27umeVcSyaWriZB139kvtWzY6ylZ300vfa5iI-4XC_tJKoyuwDEofqMLDA4nyrtMrp_9YGqPcg6ddR61BLqdvfr0y3Nm5WWkyMqBzjKV-HFyuR0PyPQbnLtQGCzRFUQWbV4XWvH2rDgeI71S6EwmjP7J1aDA2UBVprGqNXdTbxWpSINMkZcgrDvl4hdqNzet-OwB2lu2453R-xKiJkl8ezwEqkURwMj70G-t9NjQGIBInoZ-d3gM2C3J9mEWMB5lyfSMaKzhrsnObgEHcotORw6jWNsDgRUxIipJrSJJ0OLx29LHBjkZWIWIrtsMClCGtLXURBzkP-Oc-O9Xa38m8m6O9z-P8i6craikAIckv9YutmYHIXCAFQN2cAe2mmKp7ds1--HWN_P5qqw6ytuR268_MbexxGDTyq8KzUYRBjtkgVyhuVsS7lDgUHgXvJfHNmdCulpiPhmbtviPfWaZM19likSjKHLTpIn2PpfTflddfhB9Eb4X24wGH7Y5hwxASe7gDs_R707LphS1EH4cTE8p2XW_lLv0jo89ep9IUPUO27pWLsqabt8uTr5OoKQeNZmXT6XiJ9tK3HhRgvIt7DYt8vqlRw",
"token": {
"id": "a84164ed3cd879467301c526ed7c20ebe9e72e0c324b2a15f3d80dcf31309514f5654c1ae0116ddd",
"user_id": 1,
"client_id": 1,
"name": "lol",
"scopes": [],
"revoked": false,
"created_at": "2019-01-03 22:25:40",
"updated_at": "2019-01-03 22:25:40",
"expires_at": "2020-01-03 22:25:40"
}
}
Now in Postman, when I send a get request to:
Route::middleware('auth:api')->get('/user', function (Request $request){return $request->user();});
I added a authorization header of type Bearer Token for the string captured in the variable: accessToken. In return I get the user, no issue. However where and how is the accessToken generated? It's not saved in the database?
Take the user ID that Laravel gives you from the token, rather than sending it from the front end. You can also check the scopes assigned to the token:
Route::post('/api/view/{query}/comment', function (Request $request, Query $query) {
if ($request->user()->tokenCan('comment-on-queries')) {
$query->comments()->create([
'body' => $request->get('body'),
'user_id' => $request->user()->id,
]);
}
});
If this isn't a single page app, and only the comment box is handled by ajax, the default Laravel scaffolding should handle this by adding a CSRF token to axios config. In that case you don't need Passport, because the user is stored in the session. Still though, don't take the user ID from the front end, get it from \Auth::id()
Here's the key difference: If they login using PHP, your server has a session stored and knows who is logged in.
If you are creating a single-page app separate from your Laravel app, you have to rely on Passport and tokens to ensure the user has the authority to do what they're trying to do.
Figured it out, was overthinking it. Basically didn't need a whole lot to get it working.
Added the CreateFreshApiToken middleware to the web group in app\Http\Kernel.php.
The axios responses attach that cookie on the outgoing responses
The api middleware group had to be 'auth:api'.
The user instance can be then called via request()->user() which is awesome.

axios get passing parameter to laravel route

I am trying to pass an id through axios.get in vue.js to laravel route.
my axios code plus parameter are as following,
axios.get('http://localhost/laravel_back/public/api/bpaper',{
params: {
id:12
}
and my laravel route is as follows,
Route::get('bpaper/{id}', function($id)
{
return 'Paper '.$id;
});
when executing this code i get a 404 error on my browser console. and the request url is,
Request URL:http://localhost/laravel_back/public/api/bpaper?id=12
I have already given the access-control allow methods to allow communication through axios. And the code runs when not providing a parameter. any one know a fix.
Considerind server-side is Route::get('bpaper/{id}', function($id) { ..., the id is part of the path, not a parameter. Add it to the URL. Do:
var myId = 12;
axios.get('http://localhost/laravel_back/public/api/bpaper/' + myId)
Added it to a myId variable for clarity, you don't have to do it. Using:
axios.get('http://localhost/laravel_back/public/api/bpaper/12')
would work just as well.
Also, if you have access to newer versions of JavaScript, you can profit from template strings:
var myId = 12;
axios.get(`http://localhost/laravel_back/public/api/bpaper/${myId}`)

Merge URL parameters and generate a encrypted URL - Laravel 5.3

I have a URL like http://localhost:8000/assessment/3/199
Where 3 represents assignment id and 199 represents assessor id, in short they both represents two models.
I'm sending such URL into email. I want to first encrypt the URL and then send it to the email.
I want URL like http://localhost:8000/assessment/{some-long-random-string}
So, I want to merge both the parameters, make an encrypted string, send into email, upon accessing the URL decrypt it and get both actual parameters.
I would like a solution which uses Laravel to implement that.
May be using these:
https://laravel.com/docs/5.3/encryption
https://laravel.com/docs/5.3/hashing
The way I would tackle this, is by manually implementing it in a controller.
Generate the URL like this:
$url = URL::action('AssessmentController#decrypt', json_encode([
'assessment' => $assessment_id,
'assessor' => $assessor_id
]);
//now, use $url in your email
Create a route like:
Route::get('/assessment/{ciphertext}', 'AssessmentController#decrypt');
In the controller:
public function decrypt($ciphertext){
$params = json_decode(decrypt($ciphertext));
return $this->getAssessment($params['assessment'], $params['assessor']);
}
Of course, you will need more integrity-checks and some error handling, but you probably get the idea.

Resources