Supabase User Returning Null + Auth State Not Saved After Closing App - react-native

I am starting to learn React Native and using Supabase with React is pretty different than using it with Flutter. Right now, I have gotten email sign-in/up working and listening for auth changes to switch navigation stacks. I believe my two issues are related because the user/session might not be getting saved when updated.
The first issue is that every time the app is opened, the sign-in screen is shown, even though I signed in before closing it. I tried to set som options, including localStorage but that did not help. Below is my supabaseClient.tsx file.
const options = {
auth: {
localStorage: AsyncStorage as any,
autoRefreshToken: true,
persistSession: true,
detectSessionInUrl: true,
},
};
const supabase = createClient(SUPABASE_URL, SUPABASE_ANON_KEY, options);
export { supabase };
The second issue is that I am trying something simple, just showing the displayName that the signed-in user has set on the HomeScreen, but the getUser() and the user from getSession() both return null so I cannot get the user.id. This causes the displayName to be undefined and a blank screen. Below is the code I am using to get the displayName.
export function HomeScreen() {
const [displayName, setDisplayName] = useState();
useEffect(() => {
(async () => {
const user = (await supabase.auth.getSession()).data.session?.user;
console.log("User: " + user);
const { data, error } = await supabase
.from("profiles")
.select()
.eq("uid", user?.id);
if (error === null) {
data!.map((data) => {
const name = data.displayName;
console.log(name);
setDisplayName(name);
});
} else {
console.log(error.message);
}
console.log("Name: " + displayName);
})();
}, [setDisplayName]);
return (
<View>
<Text>{displayName}</Text>
</View>
);
}

I had defined localStorage as any because of a tutorial I saw.
I needed to change this:
const options = {
auth: {
localStorage: AsyncStorage as any,
autoRefreshToken: true,
persistSession: true,
detectSessionInUrl: true,
},
};
to this:
const options = {
auth: {
localStorage: AsyncStorage,
autoRefreshToken: true,
persistSession: true,
detectSessionInUrl: true,
},
};

Related

Vuex state property does not update

I'm developing a simple social media at the moment. I have a problem. token state property doesn't update at all, even when there is a token item in the localStorage. Here is my unfinished project on Github. And here is the store where the token property is stored (path: resources/js/store/modules/middleware.js):
const state = {
user: {
loggedIn: false,
isSubscribed: false,
token: localStorage.getItem('token') || ''
},
}
const actions = {}
const mutations = {}
const getters = {
auth(state) {
return state.user
}
}
export default {
namespaced: false,
state,
actions,
mutations,
getters
}
At first I thought that the state just updates before token item appears. So I decided to print the token in the console after 10 seconds (path of the file below: resources/js/middleware/auth.js):
export default function ({ next, store }) {
if (!store.getters.auth.token) {
console.log(store.getters.auth.token);
setTimeout(() => {
console.log(store.getters.auth.token);
}, 10000)
return next('login')
}
return next()
}
But the token was still an empty string. Here is how the console looks:
If you need something else to understand my question, feel free to ask!

Auth0 with react-native

I'm trying to Example repo. But when this call is executed: const { accessToken } = await getCredentials();, accessToken is undefined
I have created Auth0 Application and copy my domain and clientId
so plugins in app.json looks like this:
"plugins": [
[
"react-native-auth0",
{
"domain": "{MY_DOMAIN}"
}
]
],
Also have Auth0Provider who looks like this:
Auth0Provider domain={config.domain} clientId={config.clientId}
Callback and Logout urls are all set. Here is image literally from provided example:
and getCredentials keep throwing undefined after client login with Google
You can d two approaches here.
Using a loader and waiting until data loading.
Using useEffect() with Promise() or using only useEffect().
in the App.js
const { user, getCredentials} = useAuth0();
const [isLoading, setIsLoading] = useState(false);
const getUserMetadata = async () => {
setIsLoading(true);
const tokenData = await getIdTokenClaims();
if (tokenData) {
localStorage.setItem(USER_OBJECT_KEY, tokenData['__raw']);
setIsLoading(false);
}
};
useEffect(() => {
const storedAccessToken = localStorage.getItem(USER_OBJECT_KEY);
if (!storedAccessToken) {
getUserMetadata();
}
}, [getCredentials, user]);
if (isLoading) {
return <div>Loading...</div>;
}
return (
Your code.....
)

vuex unknown action type: login

Login.vue
<script setup>
import { useLayout } from '#/layout/composables/layout';
import { ref, computed } from 'vue';
import AppConfig from '#/layout/AppConfig.vue';
import { decodeCredential } from 'vue3-google-login'
import {auth} from '../../../store/modules/auth.module';
import { useStore } from "vuex";
const store = useStore()
const { layoutConfig, contextPath } = useLayout();
const email = ref('');
const password = ref('');
const checked = ref(false);
const logoUrl = computed(() => {
return `${contextPath}layout/images/${layoutConfig.darkTheme.value ? 'logo-white' : 'logo-dark'}.svg`;
});
const callback = (response) => {
const userData = decodeCredential(response.credential);
// const authStore = auth;
// console.log(authStore.login());
if (userData.email=='****#gmail.com') {
return store.dispatch('login')
}
}
</script>
auth.module.js
import AuthService from "../../services/auth.service";
const user = JSON.parse(localStorage.getItem('token'));
const initialState = user
? { status: { loggedIn: true }, user }
: { status: { loggedIn: false }, user: null };
export const auth = {
namespaced: true,
state: initialState,
actions: {
login({ commit }, user) {
return AuthService.login(user).then(
user => {
commit('loginSuccess', user);
return Promise.resolve(user);
},
error => {
commit('loginFailure');
return Promise.reject(error);
}
);
},
logout({ commit }) {
AuthService.logout();
commit('logout');
},
},
mutations: {
loginSuccess(state, user) {
state.status.loggedIn = true;
state.user = user;
},
loginFailure(state) {
state.status.loggedIn = false;
state.user = null;
},
logout(state) {
state.status.loggedIn = false;
state.user = null;
},
}
};
auth.service.js
import axios from 'axios';
const API_URL = 'http://localhostGetToken';
class AuthService {
async login(user) {
const response = await axios
.post(API_URL, {
username: user.username='admin',
password: user.password='password'
});
if (response.data.accessToken) {
localStorage.setItem('token', JSON.stringify(response.token));
}
console.log(response);
return response.data;
}
async logout() {
localStorage.removeItem('token');
}
}
export default new AuthService();
Here i trying to login if email true to trigger login vuex.but i get a error [vuex] unknown action type: login
how to solve this?
You haven't included in your question how the auth store is linked to your application.
I'm guessing you have a main store and the auth store is one of its modules.
If my guess is true, you should dispatch auth/login, not login, since the main store doesn't have a login action.
Side note: I suggest you carefully read How to Ask, to improve the quality of your future questions.
The problems with your current question:
you posted too much irrelevant code and, at the same time, you haven't posted all the relevant code. You should have included:
a) the action deemed unknown (everything else in that store is irrelevant for this question)
b) how the store is linked to the app (main store + how the store is instantiated in the app) - these bits are missing
c) how you're consuming the action in the component (everything else in the component is irrelevant for the question)
you started with the code. Always start by explaining the problem, so when people look at the code, they know what to look for (and skip the irrelevant parts). This is also helpful for future users with a similar problem: they'll be able to quickly understand if your question is relevant for their problem.
The more users find the question useful, the more chances for it to get upvoted.
Another side-note: the condition used to dispatch is, most likely, wrong. It is only true when the email is actually '****#gmail.com'.
You should probably use if (userData.email.endsWith('#gmail.com')).

Error: [vuex] Do not mutate vuex store state outside mutation handlers with Firebase Auth Object

I have been trying to solve this problem for a few hours now to no avail. Could someone help me spot the problem?
The error I am getting is:
Error: [vuex] Do not mutate vuex store state outside mutation handlers
Here is my login script section with the offending function in login()
<script>
import { auth, firestoreDB } from "#/firebase/init.js";
export default {
name: "login",
props: {
source: String
},
////////
layout: "login",
data() {
return {
show1: false,
password: "",
rules: {
required: value => !!value || "Required.",
min: v => v.length >= 8 || "Min 8 characters",
emailMatch: () => "The email and password you entered don't match"
},
email: null,
feedback: null
};
},
methods: {
login() {
if (this.email && this.password) {
auth
.signInWithEmailAndPassword(this.email, this.password)
.then(cred => {
//this.$router.push("/");
this.$store.dispatch("user/login", cred);
console.log()
this.$router.push("/forms")
console.log("DONE")
})
.catch(err => {
this.feedback = err.message;
});
} else {
this.feedback = "Please fill in both fields";
}
},
signup() {
this.$router.push("signup");
}
}
};
</script>
import { auth, firestoreDB } from "#/firebase/init.js";
export const state = () => ({
profile: null,
credentials: null,
userID: null
})
export const getters = {
getinfo:(state) =>{
return state.credentials
},
isAuthenticated:(state)=>{
if (state.credentials != null) {
return true
} else {
return false
}
}
}
export const mutations = {
commitCredentials(state, credentials) {
state.credentials = credentials
},
commitProfile(state, profile) {
state.profile = profile
},
logout(state){
state.credentials = null,
state.profile = null
}
}
export const actions = {
login({commit},credentials) {
return firestoreDB.collection("Users").where('email', '==', auth.currentUser.email).get()
.then(snapshot => {
snapshot.forEach(doc => {
let profile = {...doc.data()}
commit("commitCredentials", credentials)
commit("commitProfile", profile)
})
}).catch((e) => {
console.log(e)
})
},
credentials({ commit }, credentials) {
commit("commitCredentials",credentials)
},
logout() {
commit("logout")
},
}
I have checked that there is no where else that is directly calling the store state.
I have worked out that if I don't do the commitCredentials mutation which mutates credentials, the problem doesn't happen.
Another note to add, the error keeps printing to console as if it were on a for loop. So my console is flooded with this same message.
I am pretty sure this is to do with the firebase auth sign in and how the Credential object is being changed by it without me knowing, but I can't seem to narrow it down.
Any help would be very much welcomed.
Found the answer.
https://firebase.nuxtjs.org/guide/options/#auth
signInWithEmailAndPassword(this.email, this.password)
.then(cred)
"Do not save authUser directly to the store, since this will save an object reference to the state which gets directly updated by Firebase Auth periodically and therefore throws a vuex error if strict != false."
Credential object is constantly being changed by the firebase library and passing the credential object is just passing a reference not the actual values itself.
The solution is to just save the values within the object.

how to handle failed silent auth error in auth0

I followed spa react quick start guide and it worked fine for more than a month. Recently i had this error and it is logged on auth0 as 'failed silent error' with no further information. I have been told that it is because of the browsers cookie updates and recommended to use new beta release of auth0-spa-js and change cache location to local storage. And it didn't work either.
The code is as follows:
auth_config.json:
{
"domain": "dev.........eu.auth0.com",
"clientId": "....eEKkQ.............",
"redirect_uri": "https://localhost:8080",
"audience": "https://.......herokuapp.com/v1/....",
"cacheLocation": "localstorage"
}
and
react-auth0-wrapper.js:
import React, { useState, useEffect, useContext } from "react";
import createAuth0Client from "#auth0/auth0-spa-js";
const DEFAULT_REDIRECT_CALLBACK = () =>
window.history.replaceState({}, document.title, window.location.pathname);
export const Auth0Context = React.createContext();
export const useAuth0 = () => useContext(Auth0Context);
export const Auth0Provider = ({
children,
onRedirectCallback = DEFAULT_REDIRECT_CALLBACK,
...initOptions
}) => {
const [isAuthenticated, setIsAuthenticated] = useState();
const [user, setUser] = useState();
const [auth0Client, setAuth0] = useState();
const [loading, setLoading] = useState(true);
const [popupOpen, setPopupOpen] = useState(false);
useEffect(() => {
const initAuth0 = async () => {
const auth0FromHook = await createAuth0Client(initOptions);
setAuth0(auth0FromHook);
if (window.location.search.includes("code=")) {
const { appState } = await auth0FromHook.handleRedirectCallback();
onRedirectCallback(appState);
}
const isAuthenticated = await auth0FromHook.isAuthenticated();
setIsAuthenticated(isAuthenticated);
if (isAuthenticated) {
const user = await auth0FromHook.getUser();
setUser(user);
}
setLoading(false);
};
initAuth0();
// eslint-disable-next-line
}, []);
const loginWithPopup = async (params = {}) => {
setPopupOpen(true);
try {
await auth0Client.loginWithPopup(params);
} catch (error) {
console.error(error);
} finally {
setPopupOpen(false);
}
const user = await auth0Client.getUser();
setUser(user);
setIsAuthenticated(true);
};
const handleRedirectCallback = async () => {
setLoading(true);
await auth0Client.handleRedirectCallback();
const user = await auth0Client.getUser();
setLoading(false);
setIsAuthenticated(true);
setUser(user);
};
return (
<Auth0Context.Provider
value={{
isAuthenticated,
user,
loading,
popupOpen,
loginWithPopup,
handleRedirectCallback,
getIdTokenClaims: (...p) => auth0Client.getIdTokenClaims(...p),
loginWithRedirect: (...p) => auth0Client.loginWithRedirect(...p),
getTokenSilently: (...p) => auth0Client.getTokenSilently(...p),
getTokenWithPopup: (...p) => auth0Client.getTokenWithPopup(...p),
logout: (...p) => auth0Client.logout(...p)
}}
>
{children}
</Auth0Context.Provider>
);
};
What is wrong with this code, any help appreciated. Or i can use a different method, i just followed the docs, it doesn't matter as long as it authenticates.
Thanks
I know this has been hanging around for a bit, but i was running into a similar issue.
As I understand it the createAuth0Client helper factory runs the getTokenSilently function by default as part of the set up to re-authenticate users every browser refresh. The problem i was having was that the call to getTokenSilently was erroring, meaning that auth0FromHook was never set and the auth0client never set in state. Because auth0client was undefined, it was then impossible to call loginwithredirect, which is the behaviour i wanted to achieve.
Basically i wanted it to auth silently, but if it failed, send to the log in screen, but that's impossible because the auth0client was undefined, resulting in a cannot call loginwithredirect of undefined error. It seems that (sadly) in the current stable version of the #auth0/auth0-spa-js library (1.6.5 at time of writing) there is no way to bypass getTokenSilently when initialising the client. However in the current beta (1.7.0-beta.5) (Here is a list of versions) they have exposed the Auth0Client class itself, so if you want to move to that version the code could be tweaked with something like....
initAuth0().catch( e => {
const newClient = new Auth0Client(initOptions);
setAuth(newClient);
})
and then in any protected components you can check the loading is finished and if isAuthenticated is still falsey, you should be able to redirect to login despite an error occurring during the getSilentToken.
== NON BETA OPTION
The alternative in the current api would be to perhaps set max_age to 0 or 1 in the initOptions, to force a re-login, and maybe setting prompt to "login" on the second attempt to initialize the authClient