Call an api within .subcribe next in Angular - javascript

Essentially I am trying to edit a user, and calling the editUser method in my authService. Edit user in the auth service is just a HTTP post request.
this._authService.editUser(this.edit).subscribe(res=>{this.getUsers(this.params)}, (err)=>{alert(err)})
Now after editing the user, I wanna refresh the entire User list again. therefore I am calling the getUser method inside the .subscribe next field. The getUser method in the authservice is also a HTTP POST request.
getUsers(search){
this._authService.getUser(search).subscribe(
res => {
this.persons = res
},
error => {
if(error.status == 0) {
alert("The servers are down right now, try again later")
}
else{
alert(error.error.msg)
}
}
);
}
The problem with this is that it doesnt always refresh the persons list. Sometimes it does and sometimes it doesnt

Related

axios interceptor - hold off the request until cookie API call is finished, and only then continue

I am trying to get the axios to wait until one extra call in the interceptor finishes. So I am using NuxtJS as a frontend SPA and API in Laravel 8.
I've tried a lot of different things over the course of last ~ 4 days but nothing seems to be working.
THE GOAL
I need my axios REQUEST interceptor to check for existence of the cookie. If cookie is not present I need to make an API call first to grab the cookie and then we can continue with any other request.
WHAT I AM DOING?
So basically I have Axios interceptor for the requests that will call cookie endpoint if the cookie doesn't exist.
I am also saving cookie request promise to be reused in case there are multiple calls and the cookie still is not there.
PROBLEM
While it was supposed to just call cookie API first and everything else after I am mostly getting two results in different variations of the attached code.
A) I am making an extra cookie call but it is not in the required order so I still end up hitting laravel endpoint multiple times without cookies which causes extra sessions to spawn.
B) It is not making any calls at all (attached example).
Does anyone know what in the world I am confusing here?
export default function ({$axios, redirect, $cookiz, store}) {
$axios.onRequest(async request => {
// make sure that XSRF cookie exists before we make aby calls to prevent backend from
// creating multiple session when page on load calls more than one endpoint, if we don't have
// that cookie we will first have to get it and then call the rest of the endpoints
const xsrfCookie = $cookiz.get('XSRF-TOKEN')
if (xsrfCookie === undefined || xsrfCookie === null || xsrfCookie === '') {
await store.dispatch('login/getXsrfCookie')
$axios.request(request)
}
$axios.request(request)
})
}
getXsrfCookie(context) {
if (context.state.xsrfCookiePromise instanceof Promise) {
return context.state.xsrfCookiePromise
}
const xsrfCookiePromise = this.$axios.get('/csrf-cookie').then(response => {
context.commit('setXsrfCookiePromise', null)
console.log('This is the cookie response', response)
})
context.commit('setXsrfCookiePromise', xsrfCookiePromise)
return context.state.xsrfCookiePromise
}
I don't know anything about nuxt, and have only a vague idea about axios interceptors, but just looking at the code...
I think you want to persist a cookie, not the promise for a cookie.
I don't think you need to involve the store.
I think you can do that with your cookie plugin. If I'm right about that, using the set method is what you need. (you might need an options param, described here)
async getXsrfCookie() {
if (!$cookiz.get('XSRF-TOKEN')) {
// the op should double check which part of the response to persist, whether to stringify it, etc.
const response = await this.$axios.get('/csrf-cookie');
$cookiz.set('XSRF-TOKEN', response.data);
}
}
export default function ({$axios, redirect, $cookiz, store}) {
$axios.onRequest(async request => {
await getXsrfCookie();
return $axios.request(request)
})
}

Laravel + Angular - Get 401 unauthenticated on 1 GET method

I'm developing a Laravel + Angular app and i'm getting 401 Unauthorized in only 1 GET request.
Here I explain how I developed my authentication and how it work on Backend and Frontend. I wish you can help me.
I use Laravel Sanctum for manage authentication in my app. Here is how I program the backend.
I get users from my BD table:
Note: I have created a separate controller, to separate the authentication functions from the user functions, even so, I have tried to put this function in my AuthController and it has not given me any result.
<?php
namespace App\Http\Controllers;
use Illuminate\Http\Request;
use App\Models\User;
class UsersController extends Controller
{
public function getAllUsers()
{
return User::all();
}
}
As I want you to only be able to retrieve all the DB users if you are authenticated, in my api.php file I put the path inside the middleware:
Route::middleware('auth:sanctum')->group(function()
{
Route::post('logout', [\App\Http\Controllers\AuthController::class, 'logout']);
Route::get('getAuthUser', [\App\Http\Controllers\AuthController::class, 'getAuthUser']);
//Admin actions
Route::post('createUser', [\App\Http\Controllers\AuthController::class, 'createUser']);
Route::get('getAllUsers', [\App\Http\Controllers\UsersController::class, 'getAllUsers']);
});
If I make the request from the Postman everything works correctly, if I am not authenticated it gives me an error and if I have previously authenticated it returns all the DB users just as I expected. By the way, I am using cookies to send the jwt to the Frontend.
The problem is when in my Angular app I request my backend with the GET method to retrieve these users and display them in a table. In addition, the code to retrieve the users is within a condition in which it is looking at whether the user is authenticated or not. The truth is that I do not understand what may be happening.
getUsers(): void
{
//Check if user is authenticated
this.http.get('http://localhost:8000/api/getAuthUser', { withCredentials: true }). subscribe(
(res: any) =>
{
Emitters.authEmitter.emit(true);
Emitters.roleEmitter.emit(res.role);
//Get all users
this.http.get('http://127.0.0.1:8000/api/getAllUsers', { withCredentials: true }). subscribe(
res =>
{
this.users = res;
}
)
},
err =>
{
Emitters.authEmitter.emit(false);
Emitters.roleEmitter.emit("none");
alert("You should be authenticated for this.");
}
);
}
The first request that you see above getAuthUser, makes the request to the Backend in the same way as the second request getAllUsers and the first one works perfectly and the second one does not, it is in which I get an err. I call the getUsers() method in the ngInit().
I hope I have explained myself well. Any information you need to know let me know. Thank you.
The solution was in the request that gave the error to change the path of the api, instead of putting 127.0.0.1 putting localhost.

Keycloak js not executing THEN after init

I am trying to integrate Keycloak login into my React app and I'm trying to get the JWT from keycloak. Here is the code:
const [keycloakState, setKeycloakState] = useState<any>();
const login = () => {
const keycloak = Keycloak("/keycloak.json");
keycloak.init({onLoad: 'login-required'}).then(authenticated => {
console.log('kk', keycloak)
console.log('at', authenticated)
setKeycloakState({ keycloak: keycloak, authenticated: authenticated });
}).catch(err => {
alert(err);
});
console.log('log after')
}
The login function is triggered when a button is clicked. It redirects properly to keycloak, I can log in and I am properly redirected to the app. The problem is that after the redirect back to the app with proper login the code in the then part of the chain is not executed, and even the 'log after' does not appear in the logs. The catch error part works fine.
Why might this be happening? I have keycloak-js added to my project.
I used to face this problem before. The way that I passed is separating the "init" function and then invoke it later.
Here is my example on jsFiddle: 'https://jsfiddle.net/gzq6j3yu/1/'
Our solution was to use the functions onAuthSuccess and onAuthError avaliable on the KeycloakInstance keycloak-js provides. (The documentation around this is a little shaky but you can find them if you check out the source code.) As the names imply these functions get called when an auth attempt is successful or unsuccessful, respectively.
note: in the following snippets this._instance replaces OP's keycloak constant.
Basic code snippet:
import Keycloak from 'keycloak-js';
...
// pulled from a class's init function from a custom Keycloak helper class so won't translate one for one but you get the point.
this._instance = Keycloak(configObject);
this._instance.onAuthSuccess = () => {
// code to execute on auth success
};
this._instance.onAuthError = () => {
// code to execute on auth error
};
this._instance.init(initOptions)
...
We also had a getter to get the token on the KeycloakInstance (or empty string) on the same class. This is an easy way to refer to the token in your code to check if it actually exists, etc. Here's what that'd look like inside the class.
get token() {
return this._instance ? this._instance.token : '';
}
Hope this can help out some folks.
I think the reason your fulfilled callback is not executed is the way your app interacts with Keycloak. You initialize the Keycloak-Adapter with onLoad: 'login-required' which will redirect the user to Keycloak - which means the Javascript execution is interrupted at this point. Keycloak will redirect the user back to your app and since you wrapped the Keycloak-Adapter in a function which is only executed when a button is clicked, the promise callback is not executed.
Simple example:
// do this on page load
keycloak.init({onLoad: 'login-required'}).then((authenticated) => {
console.log('authenticated', authenticated)
})
You will not see a "authenticated", false in your console when you open up your app. If the user is not authenticated, he will be redirected (so no chance to execute that callback). If he then comes back and is authenticated, the callback will be executed and authenticated should be true.
If you want the user to click a button, a setup like this should work:
// do this somewhere early in your App or main.js and in a way that this code is executed on page load
const keycloak = new Keycloak(configuration);
keycloak.init({onLoad: 'check-sso'}).then((authenticated) => {
if (authenticated) {
// do what needs to be done if sign in was successful, for example store an access token
} else {
// handle failed authentication
}
}).catch(err => {
alert(err);
});
const login = () => { // this could be an on-click event handler
keycloak.login();
};
check-sso won't redirect the user to Keycloak if unauthenticated, so the user can trigger the login when needed.
Keep in mind that your JavaScript code will run twice and you have to cover both cases, first the user is not authenticated and needs to be redirected to Keycloak and a second time once the user comes back from Keycloak (then we should get the information that the user is authenticated in .then().

Firebase ID Token expiration in an hour

so I am using redux-saga in my react-native app and tried to use refresh token but didn't work, so my approach was the following in app.js in order to get the token specifically for each request and force refresh it:
handleResponse = async () => {
const {dispatch} = this.store;
await axios.interceptors.request.use(config => {
// Important: request interceptors **must** return the request.
console.log("refreshToken")
let user = firebase.auth().currentUser;
firebase.auth().onAuthStateChanged(function(user) { if (user) {
console.log("auth changed: ",user)
user.getIdToken(true).then((token) => {
setAccessToken(token);
config.headers.authorization = token;
}
);
} else { console.log("didn't auth change") } });
console.log("req in handle response: ",JSON.stringify(config));
return config;
});
axios.interceptors.response.use(config => config, (err) => {
if (err.response) {
const response = err.response;
const state = this.store.getState();
if (
response.status === 401
&& state.auth.isAuthenticated
) {
dispatch(logout());
}
}
return Promise.reject(err);
});
};
But it always ends up after an hour throwing me the following error::
Firebase ID token has expired. Get a fresh token from your client app and try again (auth/id-token-expired). See https://firebase.google.com/docs/auth/admin/verify-id-tokens for details on how to retrieve an ID token.
so I was wondering if there's another approach I can try to solve the issue from my side?
Thanks in advance.
Firebase auth tokens automatically refresh every hour. That's a function of the SDK, and you don't have to do anything to enable it.
You definitely do not want to call firebase.auth().onAuthStateChanged for each request. That starts a persistent listener, and adds a new callback every time it's used. Typically you add just one listener globally for your app, and use its updates as they happen.
If you need to know when the SDK refreshes the token in order to get a new one immediately, you should instead use onIdTokenChanged to set up a callback that will be invoked every time the user's token changes. Again, you should only set up one of these globally for your app, and use its current value at the time of the request.

Should action talk with store?

I am using react with flux architecture and I have a problem I face it.
I need to create an action that gets a user id and fetches the user. Here is the code:
var createAction = require('common/scripts/actions-helpers/create-action'),
resource = require('common/scripts/resources/conversation');
module.exports = createAction(fetchAction);
function fetchAction(context, payload, success, failure) {
resource.sync(context, payload.userId)
.then(function(user) {
context.dispatch('USER_FETCH', user);
success();
}, failure);
}
I want to use a store that will cache all users so in case the user fetched before, the action will not perform a backend call. The new action should look like that:
function getFetchedUser() {
// <--------- HOW TO KNOW WHETHER USER FETCHED?
}
function fetchAction(context, payload, success, failure) {
var user = getFetchedUser();
if (user) {
context.dispatch('USER_FETCH', user);
success();
} else {
resource.sync(context, payload.userId)
.then(function(user) {
context.dispatch('USER_FETCH', user);
success();
}, failure);
}
}
The issue is that I don't want to manage users data in the action so the only way come in my mind to implement getFetchedUser() is checking in the Users store.
Is this a good approach?
Can action access to store?
The common approach is to do the backend call from the action and then dispatch an action when the request either succeeds or fails. This way you can populate your store with an action. This means that the store keeps synchronous and can just emit changes as they come along.
var AppDispatcher = require(<Flux AppDispatcher>);
var UserActions = {
fetchUser: function(payload) {
fetchUserFromBackendApi(payload)
.then(function (error, user) {
if(error) {
AppDispatcher.dispatch({
actionType: "ERROR_CODE",
error: error
});
return;
}
AppDispatcher.dispatch({
actionType: "SUCCESSFUL_USER",
user: user
});
}
}
};
module.exports = UserActions;
Then you just handle different action types in the store and just populate your cache. When the cache is populated you emit a change event and the data will be rendered in.
This code ain't the prettiest so howl out if I misunderstood or if there are any questions.
So to answer the intial answer. The action component should not talk to the store. It goes against one of the key concepts of React, loose coupling.
With Flux and React the data flow works out because the components will fire actions when they need data. The actions just fetch the data.
I also had a similar problem. I have a PhotosStore that keep all photos from a user, server send it paginated.
User can reorder photos by more recently, more commented, more viewed. Is PhotosStore has all page loaded, it can reorder by itself, if not, need call server.
# actions
Actions.Photo = createActions(
'sortBy', {'load': asyncResult: true}
)
# store
Store.Photos = Reflux.createStore(
listenables: Actions.Photo
onSortBy: (order) ->
if #hasMorePages()
Actions.Photo.load({order: order})
else
# reorder and trigger
onLoadCompleted: (data) ->
# get data and trigger
)
# view
PhotoHeaderOrder = React.createClass
mixins: [
Reflux.connect(Store.Photos, 'model')
]
onSortNew: -> Actions.Photo.sortBy('news')
onSortCom: -> Actions.Photo.sortBy('comments')
onSortVie: -> Actions.Photo.sortBy('views')
render: ->
# render code

Categories

Resources