Cookies not saved on mobile - javascript

I'm using next middleware to do protected routes and I'm using cookies to do the checking since you can use cookies on the server-side.
When a user logged in, I save the info on local storage(for all my frontend auth checks) and a cookie set to true if logged in(for server-side checking).
The issue is that when im on mobile, and a user logs in, the info is saved successfully. But if the user closes the browser(like safari) then opens it again and goes to the site, the user is still logged in but the Cookie doesnt seem to work(or is no longer there) but the localstorage still is there. Then this causes a case where the user is logged in but they cant access the logged in(protected) routes.
The following is my login in function
let loginUser = async (e) => {
e.preventDefault();
let response = await fetch(URL, {
method:'POST',
headers:{
'Content-Type': 'application/json'
},
body:JSON.stringify({'email':e.target.email.value, 'password':e.target.password.value})
})
if(response.ok){
let data = await response.json();
setAuthTokens(data);
localStorage.setItem("authTokens", JSON.stringify(data));
Cookies.set("auth", "true");
setUser(true);
setFailed(false);
router.push("/dashboard");
} else {
setFailed(true)
}
}
The following is my middleware
export default function middleware(req, event) {
let verify = req.cookies['auth']
const url = req.url
if (url.includes("/login") || url.includes("/signup")) {
if (verify) {
return NextResponse.redirect("some_url/dashboard");
}
}
if (url == "some_url" && verify) {
return NextResponse.redirect("some_url/dashboard");
}
if(url.includes("/dashboard")){
if(!verify){
return NextResponse.redirect("some_url/login");
}
}
}

This code may cause some security issues so I suggest you to set cookie from server also have an API for logout that clears cookie.
BTW if you don't set expire date in your cookie safari mobile will remove it.
You can do this by passing options to third argument of Cookies.set() .
It would be something like this:
Cookies.set('auth', 'true', { expires: 7 }); //expires 7 days from now

Related

logout function refreshes the page but it's still logged in

I am following a course on Udemy, the instructor's side is using Angular 2 and I am trying to build the app using the latest version. The main problem I have, is that the logout function is accessed but because I have to refresh the page to display the login form again, for some reason, after the refresh, I see the login form but then it goes back to the part where I'm logged in.
Logout method on the back-end side:
#RequestMapping(value="/loggedOut", method=RequestMethod.POST)
public ResponseEntity logout(){
SecurityContextHolder.clearContext();
return new ResponseEntity("Logout Successfully!", HttpStatus.OK);
}
Logout function from my login service:
logOut() {
const url = 'http://localhost:8181/loggedOut';
const basicHeader = 'Basic ' + localStorage.getItem('credentials');
const headers = new HttpHeaders({
'x-auth-token' : JSON.stringify(localStorage.getItem('xAuthToken')),
'Authorization' : basicHeader
});
return this.http.post(url, '', { headers, responseType: 'text'});
The button responsible for logging out:
logout() {
this.loginService.logOut().subscribe(
res => {
location.reload();
console.log("Logged out")
},
error => {
console.log(error)
}
);
Technically, it goes as follow: Logged in -> Login form -> Logged in
Logged in:
Log in form:
If I remove the reload method, I can see that the logout method is accessed and I get a 200 from the back-end.
Network tab before refreshing:
The server response before refreshing:
Try clearing out your localStorage when logging out:
localStorage.clear();
Basically, this removes any trace that the app left when logging in.
**Try This Approach **
logout() {
this.loginService.logOut().subscribe(
res => {
if(res) {
// clear localStorage
localStorage.clear();
//navigate to login component
this.router.navigate(['loginPagePathName']);
console.log("Logged out")
}
},
error => console.log(error));
}
We don't need to refresh the page
Note :- You can also clear the local storage whenever login component load into the browser simply put localStorage.clear() inside ngOnInit method of loginComponent

Authenticated requests after sign in with React Query and NextAuth

I'm having troubled sending an authenticated request to my API immediately after signing in to my Nextjs app using NextAuth. The request that is sent after signing in returns data for and unauthenticated user.
I believe the issue is that React Query is using a previous version of the query function with an undefined jwt (which means its unauthenticated). It makes sense because the query key is not changing so React Query does not think it's a new query, but, I was under the impression that signing in would cause loading to be set to true temporarily then back to false, which would cause React Query to send a fresh request.
I've tried invalidating all the queries in the app using queryClient, but that did not work. I've also used React Query Devtools to invalidate this specific query after signing in but it still returns the unauthenticated request. Only after refreshing the page does it actually send the authenticated request.
// useGetHome.js
const useGetHome = () => {
const [session, loading] = useSession();
console.log(`session?.jwt: ${session?.jwt}`);
return useQuery(
'home',
() => fetcher(`/home`, session?.jwt),
{
enabled: !loading,
},
);
}
// fetcher
const fetcher = (url, token) => {
console.log(`token: ${token}`);
let opts = {};
if (token) {
opts = {
headers: {
Authorization: `Bearer ${token}`,
},
};
}
const res = await fetch(`${process.env.NEXT_PUBLIC_BACKEND_URL}${url}`, opts);
if (!res.ok) {
const error = await res.json();
throw new Error(error.message);
}
return res.json();
}
// Home.js
const Home = () => {
const { data: home_data, isLoading, error } = useGetHome();
...
return(
...
)
}
Attached is the console immediately after signing in. You can see the the session object contains the jwt after signing in, but in the fetcher function it is undefined.
console after signing in
Any help here is appreciated. Is there a better way to handle authenticated requests using React Query and NextAuth? Thank you!
I have tried a similar situation here and struggled the same thing but the enabled property worked fine for me and it is good to go right now.
https://github.com/maxtsh/music
Just check my repo to see how it works, that might help.

Different headers used in Axios patch

I spent an hour looking in the Chrome console and I cannot see where this bug comes from.
I am finishing an update of OAuth implementation in my Vue app.
The story begins when socialLink.js finds out that a new user must be created. Vue component Vue-authentication depends on the presence of access_token in a response so I return some dummy text:
return api.sendResponse(res, { email, name, socialId, access_token: 'abcd' });
The library stores this value in localStorage:
After a redirect, the SignUp.vue is rendered and I complete the form. The first communication with the server is a Vuex call to create a new user:
response = await this.$store.dispatch('CREATE_USER_PROFILE', payload);
Which returns a real short lived JWT token:
const token = auth.createToken(userId, nickname, new Date(), null, false, '1m');
return api.sendCreated(res, api.createResponse(token));
Which I store in the Vue page afterwards:
const { data } = response;
const token = data.data;
if (token === undefined) {
this.error = this.$t('sign-up.something-went-wrong');
return false;
}
I checked that the token contains what the server returned:
Request URL: https://beta.mezinamiridici.cz/api/v1/users
Request Method: POST
Status Code: 201 Created
{"success":true,"data":"eyJhbGciOiJIUzI1NiIs...Tl8JFw2HZ3VMXJk"}
Then I call another Vuex method and pass the current JWT token:
await this.$store.dispatch('UPDATE_USER_PROFILE', {
I checked in the Vuex devtools that there really is the correct JWT token. I then pass it further to api.js.
Here I create an Axios configuration holding an Authorization header:
function getAuthHeader(context, jwt = undefined, upload) {
const config = { headers: { } };
if (jwt || (context && context.rootState.users.userToken)) {
config.headers.Authorization = `bearer ${jwt || context.rootState.users.userToken}`;
}
Again, I checked that the correct JWT token is used there.
Finally, I pass all data to Axios:
function patch(endpoint, url, body, context, jwt) {
const headers = getAuthHeader(context, jwt);
console.log(headers);
if (endpoint === 'BFF') {
return axios.patch(`${VUE_APP_BFF_ENDPOINT}${url}`, body, headers);
} else {
return axios.patch(`${VUE_APP_API_ENDPOINT}${url}`, body, headers);
}
}
Which I log and can confirm the correct JWT is still there:
bearer eyJhbGciOiJIUzI1N....8JFw2HZ3VMXJk
There is nothing that could change the header now to abcd, but, the 'Network' tab shows it:
And the server fails with a parse error.
Has anybody got an idea why Axios uses the Authorization header with a different value than I pass it?
Ok, mystery solved. vue-authenticate is the reason, because, it creates Axios interceptors and handles the Authorization header itself.
vue-authenticate.common.js:
var defaultOptions = {
bindRequestInterceptor: function ($auth) {
var tokenHeader = $auth.options.tokenHeader;
$auth.$http.interceptors.request.use(function (config) {
if ($auth.isAuthenticated()) {
config.headers[tokenHeader] = [
$auth.options.tokenType, $auth.getToken()
].join(' ');
} else {
delete config.headers[tokenHeader];
}
return config
});
},
My code is more complex and it supports internal accounts with email/password so this code is breaking mine. The interceptor must be present and be a function, so the solution was:
Vue.use(VueAuthenticate, {
tokenName: 'jwt',
baseUrl: process.env.VUE_APP_API_ENDPOINT,
storageType: 'localStorage',
bindRequestInterceptor() {},
bindResponseInterceptor() {},
providers: {
facebook: {
clientId: process.env.VUE_APP_FACEBOOK_CLIENT_ID,
redirectUri: process.env.VUE_APP_FACEBOOK_REDIRECT_URI,
},

JWT Token and how to use them after login?

I like to understand the JWT handling of token.
I have created a login page to check if user exist in DB? If yes, I used jwt sign a token and return jwt token.
jwt.sign({userdata}, secretKey, (err, token) => {
res.json({
token
After I get the token I understand I have store it in local storage.
localStorage.setItem("token", token);
After this I am lost! How can I redirect the login to a protected URL once the token is stored?
Then my next question is how can I make use of the local stored token in the protected route?
For example login.html will invoke a login function call and return the token then I want to go to /admin/admin.html. In /admin/admin.html, i have protected routes that need to use the token. How can I use it ? How can I know the user is the same user using the protected route since? I know the localstored token has the user information. Does that mean every protected route I have to post a user information and compare to local token?
Some examples of the code will be useful. Thanks
You can do something like that
login() {
try{
const tk = response.token; // from api response
if (tk) {
const expiresInDuration = response.expiresIn;
setAuthTimer(expiresInDuration); // setTimer to not send rest call everytime if user is visiting many times
const now = new Date();
const expirationDate = new Date(
now.getTime() + expiresInDuration * 1000
);
this.saveAuthData(this.token, expirationDate, role);
navigate(['/home']); // function which should redirect to your desired url
}
}, (err) => {
console.log(err)
});
}
// function to auto logout after specified time
setAuthTimer(duration: number) {
this.tokenTimer = setTimeout(() => {
this.logout();
}, duration * 1000);
}
saveAuthData(token, expirationDate, role) {
localStorage.setItem('token', token);
localStorage.setItem('expiration', expirationDate.toISOString());
}
// after delete and log out
clearAuthData() {
localStorage.removeItem('token');
localStorage.removeItem('expiration');
}
// function to login user if its data is already present in the localStorage
autoAuthUser() {
authInformation = getAuthData();
if (authInformation) {
const now = new Date();
const expiresIn = authInformation.expirationDate.getTime() - now.getTime();
if (expiresIn > 0) {
this.token = authInformation.token;
this.isAuthenticated = true;
this.setAuthTimer(expiresIn / 1000);
}
}
}
For your question regarding same user is accessing the protected route as local storage is storing token specific to user that should take care of the task
You have to use a library that verifies your stored JWT token. You can use https://www.npmjs.com/package/jsonwebtoken . This library includes a method that verifies your JWT jwt.verify(token, secretOrPublicKey, [options, callback]). To be able to verify a token, you must provide the secret key that is used to sign your tokens. If the token is verified successfully, you can redirect the user to its designated page. As long as the token is stored and not expired, the user is remembered in the browser.
This is an approach for JS apps, however, if you're using PHP/Laravel, the token is stored in a HTTP cookie and I recommend using jwt-auth library, it will handle the JWT processes for you.

How to handle token authentication in reactjs?

Suppose I have API for login, log out. I am using Token Authentication.
I have react component name Login. I can call API and I am getting token. But, I am not understanding how to save the token in a browser until log out or automatically destroy token after a moment.
You can create a storage module and check for available storage in client's browser.
import LocalStorage from "./localstorage"
import Cookie from "./cookie"
// Function to check availability of `localStorage`
function storageAvailable() {
try {
var storage = window["localStorage"],
x = '__storage_test__';
storage.setItem(x, x);
storage.removeItem(x);
return true;
}
catch(e) {
return false;
}
}
export default function Storage() {
return storageAvailable() ? LocalStorage : Cookie
}
Using above module:
function login(redirect = '/home') {
// set your session data
storage.set({email, id})
// redirection
window.location.href = redirect
}
function logout(redirect = "/") {
storage.clear()
// redirection
window.location.href = redirect
}
const Session = {
login,
logout
}
export default Session
Now you can simply use your Session module for login and logout as Session.login() and Session.logut() respectively.
How to use cookie:
How do I create and read a value from cookie?
How to use localStorage: Storing Objects in HTML5 localStorage
You can use universal-cookie package to set it in the cookie,
const cookies = new Cookies();
cookies.set('token', this.token, { path: '/' });
Don't forget to import(or require) Cookies from universal-cookie.
You can retrieve it back using :
cookies.get('token');
Refer to https://github.com/reactivestack/cookies/tree/master/packages/universal-cookie
You can save it in local storage as well :
localStorage.setItem('token', this.token);
But saving in the cookie would be a better idea, refer to :
Local Storage vs Cookies

Categories

Resources