I am building an app using Nativescript-Vue that requires authentication of users in order to use the app. I have a RESTful backend that functions appropriately as tested with Postman.
JWT Tokens are implemented with a perpetual life but require refreshing every 5 minutes (refresh functionality - in a vaccuum -- working appropriately).
Using Axios.js for web calls.
I am stuck on how to implement basic logic for determining if the user is logged in. All Axios calls return a Promise. I have read the extended "Promise" responses a bunch, but it's not sinking into my head how to do what I want. In a nutshell, I need to pause code execution until the API can authenticate the user and this is not computing for me.
Code as follows:
app.js
// Import VUE library
import Vue from "nativescript-vue";
// This is my user handler
import {UserServices} from "./assets/js/UserServices.js"
// imported Components
import Login from "./components/Login";
import Home from "./components/Home.vue"
let user = new UserServices();
let loggedIn = user.checkAuthStatus();
new Vue({
render: h => h('Frame', [
h(
loggedIn ? Home : Login
)
])
}).$start();
This isn't working because user.checkAuthStatus() is an async function that returns a promise and thus I cannot get a boolean value returned. I know this is the problem, but I left that in the code so that the intended result can be understood. What I don't understand is how to rewrite the code so that the designed flow is feasible using Promises.
Core logic is designed to be:
Check the user's logged-in status via user.checkAuthStatus(). This routine checks for a valid token (valid meaning it exists and is not expired). If it is expired, the token is refreshed via a call to this.refresh() from the UserServices controller.
If a value of "true" is returned from user.checkAuthStatus() the Vue app should load the Home component (aka user is logged in), else the user should be required to login.
I can only imagine that this is a simple situation thousands of people have successfully overcome, but my brain isn't working thru it. I get why JS needs to continue running so as not to stop the progress of the code (and that's the point of a Promise, I think?), but sometimes the code just needs to stop and wait it seems, like in a user authentication scenario.
Any help drilling down on the specifics on how to address my challenge? Please and thank you.
I would do like this:
Check if a locally stored token exists, valid and not expired, if yes go to homepage else redirect to login.
Related
I am currently building an app using the following
Next.js
Express API
Redux-Toolkit & RTK Query
I have all of the authentication logic implemented, but have ran into an issue.
So, upon successful login, the express api returns 2 httponly cookies containing the access/refresh tokens.
I have an endpoint in my api to get the current user using the access token i.e /api/auth/me
This all works fine, but what I can't figure out is the best way to fetch the user on each page load and store them in the redux state.
Do I use RTK Query to hit the /api/auth/me endpoint and just call the query whenever I need it throughout the app?
Ideally I fetch and set the user in _app.tsx, but I cannot use redux dispatch since it's outside of the <Provider store={store}></Provider>
Also for example if I wanted to use the redux stored user in getServerSideProps I can't seem to do that either because it's not client side and doesn't have access to redux.
I just can't seem to find a good way to simply set the authenticated user and be able to use them globally throughout the app whether it's inside redux store or in something like getServerSideProps.
Any advice would be truly amazing, I am totally lost.
One way of persisting authentication state is storing the token in the local storage. And in the main App component you can check whether the token is present in the redux store. If it's not there you can set it manually in useEffect function.
As such:
useEffect(() => {
fetchAuthTokenFromLocalStorage().then((token) => {
dispatch(setAuthenticationResult(token));
});},[dispatch]);
Using Nuxt 2.15.3, with a Rails backend.
I'm trying to create a Google OAuth workflow in my app, and I'm having some trouble with the steps after getting the access code. Once the user authenticates with Google and gets redirected back with the access code in the URL params, I send a request to my own backend to save the access/refresh tokens in the User model.
NOTE: this google Auth flow is separate from my normal app sign-in/sign-up flow. I'm only using some Google APIs, so this has nothing to do with making a user account via Google OAuth, I'm only requesting access to some APIs in the user's Google account, namely the My Business API.
Now, my backend has a boolean google_authenticated field on the User table which gets set to true if the access/refresh token exists, which automatically gets sent to Nuxt as $auth.user.google_authenticated. This feature works fine, but my problem is that the page where the users get redirected to has a v-if that checks for this google_authenticated flag.
The template looks like this, obviously simplified for the sake of the question
<template>
<div v-if="googleAuthenticated">...</div>
<div v-else><a href="googleapis.com">Authenticate</button></div>
</template>
export default {
data() {
return {
googleAuthenticated: this.$auth.user.googleAuthorized,
};
},
async mounted() {
const accessCode = this.$route.query.code;
await this.$axios
.post("/users/google_oauth", {
access_code: accessCode,
})
.then((response) => {
this.$auth.fetchUser();
});
}
}
So as you can see, what I'm trying to do is to refresh the $auth.user.googleAuthorized automatically on mount when the user gets to the page with the code in the URL params. The problem is that $auth.user doesn't seem to be reactive, and the user needs to either navigate to another page or refresh the current page to get these changes to show up and for the v-if to trigger and to show the other div.
I can see from the dev console that the fetchUser() method does indeed get called, and I can see from the Vuex store that the auth/SET function has been called and that the $auth.user.googleAuthorized flag is set to true as well.
According to the Nuxt Docs, the $auth module is reactive, but I'm failing to see it. What can I do here to make these changes stick properly?
So it turns out it's a race condition (sorta).
What's happening is that once I tell my own API to get the access tokens from Google, that request obviously takes some non-zero amount of time. As such, Vue is waiting to get the 200 from my own API indicating that I've fetched the access token and can continue with using Google APIs.
So basically, by the time Nuxt is calling fetchUser() again, it's actually getting the user without the googleAuthorized flag set to true, because that just happens a tiny bit too slowly. It's not really something noticeable when you're just looking at dev tools though, since everything seems to be happening instantaneously more or less, but the timings are extremely close, which is the reason why it would sometimes work, and other times it wouldn't without a refresh.
I've tried following the sample code on the passport-saml site, and the advanced tutorial on the react-admin site for OAuth, but haven't been able to figure out what I need to do with the authProvider to get an authenticated session available in react-admin using SAML.
I can currently log into my app through OneLogin (clicking on the app in the panel) and write out the user's information (inside the passport.serializeUser function), so I know that piece is working, but I'm not sure how to get that information over to the authProvider.
The login function on authProvider is hit when you submit the form, so if I could replicate what OneLogin is sending over when I click on the app, I could probably make that call in authProvider.login and make a custom login page that submits on load rather than waiting for a submit, but that doesn't seem intentional.
What am I missing here, and is there a better option that I'm not considering?
I ended up writing a getUser function on the server and using the authProvider.login function to hit that endpoint, parse the user data off of the response, and store it in localStorage. Then logout removes the user from localStorage, and checkAuth just gets the user from localStorage.
This is working for my purposes at the moment, although eventually I will want to expire the users. Hope this helps anyone else trying to hook up OneLogin with React-admin.
I'm a beginner in react native and I'm creating an app. I've done some research about how to make a secured react native app, but I didn't found much information. I've come up with a "solution" myself, but I want to make sure this is the right way to do this. So I need the help of some react native/javascript/security experts if possible, to quickly check if my approach is OK or not?
I have included 3 questions in this text, but obviously they're related. I've put them in bold. Feel free to answer one or more questions, I appreciate every answer!
I'm creating an app in react native. For a user to be able to use the app, the user should create an account and sign in. I'm using an JSON web token as an access token to authorize the requests made from the app to the server, and to identify the user (I store the user ID in the JSON web token).
At my server, I first check if the access token is valid. If so, I get the user ID out of the access token and use this user ID to identify the user.
For extra security, I'm also using refresh tokens, because an access token is only valid for 10 minutes. When a user send a request with an expired access token, the server responds with a 401 not authorized status.
To make my code more "managable", I've created a wrapper function in react native. I wrap every "request function" (every function where I do a GET/POST/PUT/DELETE request to the server) with this wrapper function. This wrapper function checks the response of the request. If the response status is 200, the response is returned to the code. If the response status is 401, the refresh token is send to a specific endpoint to obtain a new access token. When the access token arrives at the app, the previous request is made again with the new access token. The wrapper function also stores the new access token in (temporary) redux (keychain or shared preferences). 1. Is a wrapper function a good idea? For me, it's more manageble because now I'm reusing the code.
Every time the user opens the app, a new access token is requested, and when a user closes the app, the current access token is deleted, even if it is not expired yet. That way, I want to make sure that every app "session" starts with a new access token. 2. Is this okay? Or should I prevent unnecessary requests to the server when I still have a (possibly) valid access token?
In my react native app, this wrapper function is located in a context component. This "authentication" context is wrapper around my other components in App.js like this:
<AuthenticationProvider>
<AppNavigator />
</AuthenticationProvider>
This way, my wrapper function is accessible to all my other components. My authentication context looks like this:
const AuthenticationContext = createContext({
accessToken: null,
wrapperFunction: () => {}
})
const AuthenticationProvider = (props) => {
let accessToken = null
const refreshToken = useSelector(state => state.auth.refreshToken)
const wrapperFunction = () => {
// wrapper function
// set the access token
// await fetch('server endpoint')...
}
return (
<AuthenticationContext.Provider value={{ accessToken, wrapperFunction }}>
{props.children}
</AuthenticationContext.Provider>
)
}
3. Is using a context a good practice to do stuff like this?
Server-side, I store every refresh token in a database. When a user requests a new access token, I check if the sent request token still exists in the database. If not, I have revoked access for this user and the user should be logged out. This way, I want to make sure I can "manage" users.
Yes, it makes sense. Actually I can't think of a better way to manage the scenario you mentioned. When you wanna temper the request before it's sent, you will need a single function to do so. You could also use some hooks e.g. onBeforeSend and onAfterReceive, but in your case I don't see any extra value for this.
I do not agree with the deletion of a valid token. You can still send request to server on every app start to get user's last data -might have changed on another device-. I don't understand the logic of starting the app with a new session -maybe more information?
I don't think you need to pass the wrapperFunction/token using context. It would be best if you could send user data by context. you wrapper function can access the token directly from asyncStorage. And each component can call the function directly by importing it.
I believe you are taking the approach of using a wrapper function since the relevant API requests are made directly in components. The best practice is to move such requests outside (E.g. Redux actions with a middleware like redux-thunk) the components.
It's better to check if the access token is expired (by decoding the token) before sending the API request and retrieve the new access token. This will reduce the amount of requests to server. You can implement a common request method which handle this check as well.
I think since your access token expires every 10 mins this is unnecessary. Is there a specific reason to start each session with a new access token?
You can pass in user access details using the context. I think it's matter of preference. Passing in the wrapper function is not needed if you're handing the requests through a common request method.
I'm figuring out the data-flow, and writing action oriented code to deal with Redux.
This is the flow:
I reach SplashScreen and check if accessToken exists. If no, I send the user to LoginScreen. Else, I take him to the HomeScreen.
The flow is rather simple but I'm not able to wrap my head around how to store the token, expire the token or just check if user is logged in. So, technically, when a user logs in, the state(access token) should change. I'm not asking for code, just some pseudo-code and a little explanation will help!
If you want to user the redux store to save token or any user credentials you will need to ensure the state is saved on localStorage, and for that you can use a redux middleware like this or this, or you can use middle-ware of your own :
export default store => next => action => {
let result = next(action)
localStorage.state = JSON.stringify(store.getState())
return result
}
I don't know if you are using react router, If you are, take a look at this example: authenticator I think it's really the best way to do it..
In my app I used react-native-keychain. When a user logs in, I store the accessToken in the redux store. From there, any time I'm making a request, I just get the token from the store, ensure that it's valid, and make the request.