Get a new csrf token, even if the current token has expired - laravel

Is there a way with Laravel to obtain a new CSRF-token, even when the current token might have expired? I'd like to be able to get a new token without updating the user interface.
I've been experimenting with the Visibility API to check if the browser (only mobile users) gets active again but I soon found out that's not really the issue. If the token has expired, so far I'm unable to get a new one, because I get the token mismatch exception. I want a new token, ignore any mismatch exception, put it in the DOM and let the user continue again.
My user might experience a long idle time but when he/she reactivates the mobile page I want the user to be able to proceed where he/she left off without the need to let them reload the page and provide initial data (like name) again.

You can use this script on pages needed a token refresh without refresh page :
<script type="text/javascript">
var csrfToken = $('[name="csrf_token"]').attr('content');
setInterval(refreshToken, 3600000); // 1 hour
function refreshToken(){
$.get('refresh-csrf').done(function(data){
csrfToken = data; // the new token
});
}
setInterval(refreshToken, 3600000); // 1 hour
</script>
And in your routes/web.php
Route::get('refresh-csrf', function(){
return csrf_token();
});
You can also disable csrf_token verifiction on certain routes, creating a middleware, example below :
https://laracasts.com/discuss/channels/general-discussion/l5-disable-csrf-middleware-on-certain-routes

Related

Angular: await for query string parameter but do something else if it doesn't appear

I have tried to formulate a question title the most generic way that applies to my problem.
I have an Angular application where I have to handle authentication over an external requirement: either use a query string parameter token that has to be exchanged with the server for a JWT, or try to search for a JWT refresh token in the local storage.
It is:
First test the query string: if there is a queryString parameter token, grab the token, delete any JWT in the local storage, exchange the token via API for two JWTs (id_token and refresh_token)
Else go for the refresh token: if there is a refresh_token in the local storage available, exchange it for a JWT id_token via API
Else, if none of the two are available, the user is unauthenticated and a prompt should be displayed
I used Observables almost the correct way
this.queryParamMap$.unsubscribe();
this.queryParamMap$ = this.activatedRoute.queryParamMap
.subscribe(
params => {
let token = params.get('token');
........
if (!!token) {
doLoginWithToken();
else if (isJwtRefreshAvailable())
doLoginWithRefreshToken();
There is one problem with this approach: the very first time the application starts up, the query param map is empty, even if I follow a direct browser link http://localhost:4200?token=AAAAAAAA. I have to wait for the next element which contains the token.
This has two undesirable effects:
At the first attempt, being the token undefined, the application immediately tries to log in with the refresh token
If I filter the queryParamMap observable for a token being present, if a token is never present the observable will never emit, thus not activating the subscription.
My problem can be summarized/generalized as follows.
I have an Observable that I know for sure emits undefined the very first time, but either in no time it could be ready with a proper value, or it won't emit new values after the initial undefined.
Else said, while the observable emits undefined and my code starts reacting to it (e.g. by testing for token), a new value can be ready for emission right away.
How can I solve this in Rxjs? Note that accessing the JWT token from the local storage is a synchronous operation, but it's easy to create Observable.of(localStorage.get(KEY)) which emits immediately if a refresh token is present.
For that, I can't realistically use race operator, because the refresh token is always ready and always wins the race.
How can I write asynchronous code that performs like the steps I described earlier?
As the ultimate result of the authentication, an Observable<UserProfileDto | undefined> emits the information about the user, which is used to display personalized information.
You have 2 problems here:
On init you got "undefined" value (its because probably, under the hood, there is some BehaviourSubject emitting its default value). To overcome this, you can add RxJS operator (skip(1)) to skip that first value, but:
Problem here is if you do not have query values at all, you will not reach your subscribe function. Its because queryParamMap will emit a value only, if there is a change (and in that case there is not):
You can do it without Angular, to just parse url from:
const urlParams = new URLSearchParams(window.location.search);
const params = Object.fromEntries(urlParams.entries());
Or using router in Angular:
this.router.events
// Wait for the navigation end event (since component is initialized before angular router navigation is done)
.pipe(filter(event => event instanceof NavigationEnd))
.subscribe((event: NavigationStart) => {
// Take query params snaphot
const map = this.route.snapshot.queryParams;
});

Pagination in Gmail API (Previous Token)

I have implemented GMail API which gets Emails for me. Now I am trying to add pagination to it I have succeed in getting next records but now I also want to have Previous option(which required previous token).
I am unable to get into it below is what I tried so far.
public function paginate(Request $request){
$label = $request->input("label");
$nextToken = $request->input("next");
$prevToken = $request->input("prev");
$messages = LaravelGmail::message();
$msg = $messages->take(3)->in($label)->all($nextToken);
$nextToken_New = $messages->pageToken;
return view('gmail.load_mails', ['messages' => $msg, 'nextPageToken' => $nextToken_New,
'prevPageToken' => $nextToken]);
}
Now In the above function nextPageToken is passed in view as $nextToken_New
and for prevPageToken I am unable to set previous page token.(In code I have set last nextPageToken to prevPageToken which is not working)
Remember prevPageToken will be used to set on back key.
The Gmail api does not support prevous page token. Its not going to return the value to you.
Your first option would be to save these tokens on your server and then when ever you want to go back a page simply supply the token you want to the page token field
The second option and the one that i personally feel would be the most logical. Would be to cache the data returned by these requests on your server so that
you dont have to make extra http calls to the server.
you are not eating addictal quota making a call you have already made before.
APIs were not meant for you to use to implement pagination in your application. You should only be requesting data once its your job to then cache that data so that you wont need to make the same request twice.

Should a Laravel SPA still use a CSRF token for security?

I ran into multiple difficulties with CSRF when building a small SPA with Laravel and Vue.js:
I use index.html as the only view, the rest is handled by vue-router using single file components (i.e. .vue files)
Because I'm not using PHP or Blade on the front, I can't inject csrf_token() into my view. Even if I did, the token would eventually expire, yet because the app has no (or very few) page refresh(es), it wouldn't know if the token changed, and it would eventually fail to make AJAX requests with the old token
Some answers suggest to pass the token in a cookie and then retrieve it with JS. This approach suffers from the same problem as above -- the SPA is never notified when the token changes
I could dig in the internal workings of Laravel and throw an event every time the token changes; the front-end could use Laravel Echo to listen to the changes, but then the question raises, is it even worth to bother?
Lastly, I was suggested to use JWT; however, as I understand, JWT is used for authentication purposes, while CSRF -- for every single request regardless of the HTTP verb or intent.
With the last two points in mind, do you think it is necessary/advisable to use a CSRF token in a Laravel SPA? And if so, what would be the best implementation (cookie with the token, dedicated route returning the token, or other)? If not, what are the alternatives?
Comments don't have enough space, so I'm adding this as an answer, but this is just a concept as I have extremely low experience with Vue.
From the docs
// Add a request interceptor
axios.interceptors.request.use(function (config) {
// Do something before request is sent
return config;
}, function (error) {
// Do something with request error
return Promise.reject(error);
});
// Add a response interceptor
axios.interceptors.response.use(function (response) {
// Do something with response data
return response;
}, function (error) {
// Do something with response error
return Promise.reject(error);
});
So the concept would be something like this:
Set a custom header from Laravel.
When building/starting up your Vue application, get the custom header and set it somewhere global.
When making a request, intercept it and add the CSRF token from the global storage
axios.interceptors.request.use(function (config) {
// Get your token from the place you stored it and add to the request
});
Intercept the response and store a new token
axios.interceptors.response.use(function (response) {
// Store the new CSRF token in the same place you stored the 1st one.
});
Loop forever

OKTA Session API

Is there an API (we are using the OKTA Sign In Widget) to get the original res.idToken?
Reason I ask is that users might hit our site after logging in to a different site and we need the idToken. We can tell if the session exists of course..
oktaSignIn.session.exists((exists) => {
if (exists) { oktaSignIn.session.get((res) =>
But I don't see an idToken in there.
Thanks!
Can you use tokenManager to store the tokens?
After receiving the id token, you can add it a tokenManager. The token can later be retrieved from there.
Refer - https://github.com/okta/okta-signin-widget#oidc-tokenmanageraddkey-token
Well.. seems like I can get a new IDToken. The docs say this:
oktaSignIn.idToken.refresh(token, function (newToken) {
// New id_token with extended lifetime
});
My problem of course was that I did not have have a token to refresh. Turns out you can just do... (use null instead of a token)
oktaSignIn.idToken.refresh(null, function (newToken) {
// New id_token with extended lifetime
});
Hopefully this is not a bug but a feature :-)

How do I Reload Ajax Call Parameters without Reloading the webpage

I'm working with Extjs 2.2.1 with Alfresco 3.2 enterprise.
I would like to update the ticket that handles authentication to the alfresco server on components that have been loaded during login. This ticket expires after a set time and this is why I will need to update the ticket.
Options that do not seem viable for me(but please let me know if I'm wrong):
Reload the components to reload the call parameters - I can't do this because it resets whatever the user was previously working on (ie. Tree panel gets reloaded, grid filters reset, etc). The actual webpage never reloads as everything uses ajax calls to update things on the page.
Create a global variable that stores the ticket and attach it as a call parameter with any ajax calls - Any components that were loaded during login will still use the original ticket to make calls to the server.
Try something like this
Ext.onReady(function() {
var token = new Ext.util.MixedCollection();
token.add('id', 'THE_TOKEN_ID');
Ext.ComponentMgr.register('token', token);
});
Attach event listeners to the MixedCollection that updates any components that care about the token.
// now you need all the event listeners
var token = Ext.getCmp('token');
var component = Ext.getCmp('some_component_that_needs_ajax');
token.on('replace', function(key, value, original) {
if(key == 'id') {
component.params.token_id = value; // update the new value
}
});
Whenever the token needs updating, do
var token = Ext.getCmp('token');
token.replace('id', 'THE_NEW_TOKEN_ID');
What's going on here is:
You create a MixedCollection to contain information about your token.
Any components which need to be updated when there is a new token should be updated in the handler for the replace listener for the token MixedCollection.
When you get a new token id, use MixedCollection.replace to update the id key with the new token id.
The replace handler will fire, and the listeners which updates all dependent components.

Resources