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.....
)
Related
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,
},
};
unfortunately I am struggling with the hook lifecycle in react native.
I have two hooks at the top level.
const permission = useLocationPermission();
const location = useCurrentLocation(locationPermission);
The first one handles the location permission permission and asks the user to grand.
export default function useLocationPermission() {
const [hasLocationPermission, setHasLocationPermission] = useState(false);
const appState = useRef(AppState.currentState);
useEffect(() => {
hasPermission();
}, []);
const hasPermission = async () => {
const { granted } = await Location.getForegroundPermissionsAsync();
if (!granted) {
const { granted: request } =
await Location.requestForegroundPermissionsAsync();
if (!request) {
Alert.alert(
"Some Message"
);
setHasLocationPermission(false);
} else {
setHasLocationPermission(true);
}
} else {
setHasLocationPermission(true);
}
};
return hasLocationPermission;
}
The second one handles the current location.
export default function useCurrentLocation(hasPermission: boolean) {
const [currentLocation, setCurrentLocation] = useState<LatLng>(initLocation);
useEffect(() => {
console.log(hasPermission);
if (hasPermission) {
setWatcher();
getCurrentPosition().then((locationObject) => {
setCurrentLocation({
latitude: locationObject.coords.latitude,
longitude: locationObject.coords.longitude,
});
});
} else {
setCurrentLocation(initLocation);
}
}, []);
const getCurrentPosition = async () => {
return Location.getCurrentPositionAsync({});
};
const setWatcher = async () => {
await Location.watchPositionAsync({ distanceInterval: 5 }, (locaction) => {
setCurrentLocation({
latitude: locaction.coords.latitude,
longitude: locaction.coords.longitude,
});
});
};
return currentLocation;
}
My problem is that after the user has granted the permission. The location will not been updated anymore (still the initial location). It seems that the second hook is only called ones.
Is there any best practice to handle such situation. So the location will be updated and the watcher is set after the permission is set.
Thanks a lot.
Your list of dependencies for the effect that sets current location is empty. Since you are saying you want that effect to be dependant on hasPermission, set it to [hasPermission] instead.
I use react-native with graphql.
I have a query and tried to use refetch.
const { data: updatePhoto, refetch } = useQuery(SEE_PHOTO_QUERY, {
variables: {
id: photoId,
},
});
when after I edit my comment, I want to refetch this query and put it on setState in order to change UI.
const onEditValid = async ({ comments }) => {
const commentId = await AsyncStorage.getItem("#commentId");
await editCommentMutation({
variables: {
id: parseInt(commentId),
payload: comments,
},
update: updateEditComment,
});
};
const updateEditComment = async (cache, result) => {
const {
data: {
editComment: { error, ok, id },
},
} = result;
if (ok) {
const commentId = await AsyncStorage.getItem("#commentId");
const { comments } = getValues();
await textRef.current.clear();
await refetch();
setState(updatePhoto);
await cache.modify({
id: `Comment:${commentId}`,
fields: {
payload(prev) {
return comments;
},
},
});
}
};
But UI doesn't change.
I tried to change UI by modifying cache and refetching data. But both fails for a week.. :(
I also raised the question about fail of cache modify
=> React Native: `cache.modity` doesn't work
But no one answers.
I really need your help.. please help me
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
I am a subscription setup but onNext is not getting triggered I am not sure why since this is my first time implementing subscription and docs was not much help with the issue.
Here are the code implementations:
import {
graphql,
requestSubscription
} from 'react-relay'
import environment from '../network';
const subscription = graphql`
subscription chatCreatedSubscription{
chatCreated{
id
initiate_time
update_time
support_id
category_id
email
name
}
}
`;
function chatCreated(callback) {
const variables = {};
requestSubscription(environment, {
subscription,
variables,
onNext: () => {
console.log("onNext");
callback()
},
updater: () => {
console.log("updater");
}
});
}
module.exports = chatCreated;
and here is my network for the subscription
import { Environment, Network, RecordSource, Store } from "relay-runtime";
import Expo from "expo";
import { SubscriptionClient } from "subscriptions-transport-ws";
import { WebSocketLink } from 'apollo-link-ws';
import { execute } from 'apollo-link';
import accessHelper from "../helper/accessToken";
const networkSubscriptions = async (operation, variables) => {
let token = await accessHelper();
if (token != null || token != undefined) {
const subscriptionClient = new SubscriptionClient("ws://localhost:3000/graphql",
{
reconnect: true,
connectionParams: {
Authorization: token,
},
});
execute(new WebSocketLink(subscriptionClient), {
query: operation.text,
variables,
});
}
}
const network = Network.create(fetchQuery, networkSubscriptions);
const store = new Store(new RecordSource());
const environment = new Environment({
network,
store
});
export default environment;
the subscription is called in a componentDidMount method on a component it executes but the onNext method inside the subscription is never triggered when new information is added to what the subscription is listening to.
so i figured out that my issue was the network js not being setup properly and the version of subscription-transport-ws. i added version 0.8.3 of the package and made the following changes to my network file:
const networkSubscriptions = async (config, variables, cacheConfig, observer) => {
const query = config.text;
let token = await accessHelper();
if (token != null || token != undefined) {
const subscriptionClient = new SubscriptionClient(`ws://${api}/graphql`,
{
reconnect: true,
connectionParams: {
Authorization: token,
},
});
subscriptionClient.subscribe({ query, variables }, (error, result) => {
observer.onNext({ data: result })
})
return {
dispose: subscriptionClient.unsubscribe
};
}
}
i hope this helps you if you get stuck with the same issue as mine.