I've searched on here already but it seems all the answers are very outdated or they are questions that haven't been answered.
I've got an expo app SDK 43 and I'm using their auth library to authorize a reddit login. I've followed the example here https://docs.expo.dev/guides/authentication/#reddit to produce this code
import React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { makeRedirectUri, ResponseType, useAuthRequest } from 'expo-auth-session';
import { Button } from 'react-native-paper';
import { useAppDispatch } from '../../common/hooks/redux';
import { setAuthCode } from '../../common/state/authSlice';
WebBrowser.maybeCompleteAuthSession();
// Endpoint
const discovery = {
authorizationEndpoint: 'https://www.reddit.com/api/v1/authorize.compact',
tokenEndpoint: 'https://www.reddit.com/api/v1/access_token',
};
const LoginScreen = () => {
const dispatch = useAppDispatch();
const [request, response, promptAsync] = useAuthRequest(
{
responseType: ResponseType.Token,
clientId: 'MY_CLIENT_ID',
scopes: ['identity'],
redirectUri: makeRedirectUri({
scheme: undefined,
}),
},
discovery
);
React.useEffect(() => {
console.log(`response is ${response}`);
if (response?.type === 'success') {
const { access_token } = response.params;
dispatch(setAuthCode(access_token));
console.log(access_token);
} else {
console.log(response);
}
}, [response]);
return (
<Button
disabled={!request}
onPress={() => {
promptAsync();
}}
>
Login
</Button>
);
};
export default LoginScreen;
But despite the fact that the login button correctly takes me to the login screen, I successfully log in and allow the app (and if I go onto the web separately I can see in my account that the app is there under the authorized apps.)
At this point on my device one of two things happens: 1. something causes the app to disconnect from metro and it hangs on a loading wheel belonging to the greater expo stuff, or 2. It successfully gets back to the app but it redownloads the bundle and the response is null.
What is screwing up here?
Related
I'm currently working on authentication of an application in the frontend. I don't have access to the backend.
User state is managed via redux and the authentication requires a token.
While testing I notice that when I'm logged in and via Postman (for example) I authenticate the user the app return an unidentified state. I am still able to navigate the private routes but I can not see the data I fetch from the backend.
Here is how I am managing the logged / !logged state for the private route.
I wonder if I am getting something wrong and if not, if its possible to overcome this issue of potentially "stealed" token... Imagine 2 person working using the same username/password... (wrong, but technically possible)
import { useState, useEffect } from 'react'
import store from '../store'
export const useAuthStatus = () => {
const [loggedIn, setLoggedIn] = useState(false)
const [checkingStatus, setCheckingStatus] = useState(true)
const user = store.getState().userLogin
const status = user.userDetails.status
useEffect(() => {
if (status === "ok") {
setLoggedIn(true)
} else {
setLoggedIn(false)
}
setCheckingStatus(false)
}, [status, user])
return { loggedIn, checkingStatus }
}
Solved
Although not exactly my solution to this problem, the answer given below from #Po Wen Chen was helpful but it doesn't work exactly as I want it to. data in the form of proxy continues to come, this is not important, the conditions are met.
The main problem was that every time the page was refreshed, the states were null, so their values were flying. After we searched, my states became permanent with the library named pinia-plugin-persistedstate.
Solved
I did as it is written in the documentation, but instead of receiving a user data, I am getting a proxy type data.
Documentation of pinia describing how to use store outside of component
The account store performs the registration and login of the users, that is, the auth processes.
import axios from "axios";
import { defineStore } from "pinia";
const useAccountStore = defineStore("account", {
state: () => ({
user: null,
returnUrl: null
}),
getters: {
isLoggedIn: state => (state.user ? true : false),
getUser: state => state.user,
},
actions: {
async init() {
console.log('run the init')
this.fetchUser()
},
async registerUser(user) {
await axios.post("/account/register", {user})
},
async login(credentials) {
const user = await axios.post("/account/session", credentials)
this.user = user.data
},
async logout() {
await axios.delete("/account/session")
this.user = null
},
async fetchUser() {
const user = await axios.get("/account")
this.user = user.data
},
},
})
export { useAccountStore };
In main.js
import antd from "ant-design-vue"
import "ant-design-vue/dist/antd.css"
import axios from "axios"
import { createPinia } from "pinia"
import { createApp } from "vue"
import App from "./app.vue"
import { router } from "./router"
import { useAccountStore } from "./store/account.store"
// import './assets/main.css'
axios.defaults.baseURL = import.meta.env.VITE_API_URL
axios.defaults.withCredentials = true
createApp(App)
.use(createPinia())
.use(router)
.use(antd)
.mount("#app")
useAccountStore().init()
router.beforeEach(async (to) => {
// redirect to login page if not logged in and trying to access a restricted page
const publicPages = ['/login', '/register'];
const authRequired = !publicPages.includes(to.path);
const authStore = useAccountStore();
console.log('authStore.isLoggedIn', authStore)
if (authRequired && !authStore.user) {
authStore.returnUrl = to.fullPath;
return '/login';
}
});
I press the authStore to console and it comes back to me as a proxy.
return the proxy
Although not exactly my solution to this problem, the answer given below from #Po Wen Chen was helpful but it doesn't work exactly as I want it to. data in the form of proxy continues to come, this is not important, the conditions are met.
The main problem was that every time the page was refreshed, the states were null, so their values were flying. After we searched, my states became permanent with the library named pinia-plugin-persistedstate.
I am using expo React Native for developing my app, Redux for state management, ExpressJS for backend, MongoDB as database.
I am trying to fetch logged-in user data, JWT tokens are not saved in cookies. I don't know if cookies work with react native or not? I have tested the API with postman, API is working and also JWT Tokens are saved in cookies when I test it with postman or thunder-client VS Code extension. Here is the code:
Backend:
exports.getUserDetails = catchAsyncErrors(async (req, res, next) => {
const user = await User.findById(req.user.id);
if (!user) {
return next(new ErrorHandler("User not found", 404));
}
res.status(200).json({
success: true,
user,
});
});
Redux State:
export const LoadUser = () => async (dispatch) => {
try {
dispatch({ type: "LOAD_USER_REQUEST" });
const { data } = await axios.get(`${API_URL}/api/auth/me`);
dispatch({
type: "LOAD_USER_SUCCESS",
payload: data.user, //Not loading data.
});
} catch (error) {
dispatch({
type: "LOAD_USER_FAIL",
payload: error.response.data.message,
});
}
};
Frontend App.js file:
import { Provider as RNP_Provider } from "react-native-paper";
import { Provider } from "react-redux";
import Index from "./Index";
import { LoadUser } from "./actions/authAction";
import store from './store'
import { useEffect } from "react";
export default function App() {
useEffect(() => {
store.dispatch(LoadUser());
}, [])
return (
<Provider store={store}>
<RNP_Provider>
<Index />
</RNP_Provider>
</Provider>
);
}
There is no such thing as cookies in mobile apps created in ReactNative.
What you may use are AsyncStorages for Android/iOS and EncryptedStorage, search for those in npm and read the documentation, I guarantee you'll find your answers there!
You can also use some kind of context if you want your tokens to be saved for one session only, stores are suggested to use for values that you want to save for a longer time.
EncryptedStorage saves values even after app unninstall, so be careful with that one :)
I am trying to use Firebase Authentication on my Next.js website. For some reason, when I press the sign up button, nothing happens and no errors are logged. It just refreshes the page. It doesn't even set any cookies or create a user.
Here are my two files related to authentication:
utils/authProvider.js:
import firebase from 'firebase/app';
import 'firebase/auth';
if(!firebase.apps.length) {
firebase.initializeApp({
// config
});
}
const auth = firebase.auth();
module.exports = { auth };
pages/signup.js:
import { useState } from 'react'
import { LockClosedIcon } from '#heroicons/react/solid'
import { auth } from '../utils/authProvider'
export default function CreateAccount() {
const [emailField, setEmailField] = useState('');
const [passwordField, setPasswordField] = useState('');
// emailField and passwordField are set correctly, I used console.log to test it
const createAccount = () => {
// this event does get triggered, I used console.log to test it
auth.createUserWithEmailAndPassword(emailField, passwordField)
.then((userCredential) => {
window.location.replace('/');
console.log('logged in!');
}).catch((error) => {
console.error(error);
});
}
Edit: After experimenting some more, I saw that the request to https://www.googleapis.com/identitytoolkit/v3/relyingparty/signupNewUser never gets completed. It just shows as red in the network traffic tab of Chrome dev tools. Any reason why this might happen?
Thanks in advance.
I am building a web application in which i need to verify the user's email sent via the client side (React.js and Next.js) and i'm following this youtube tutorial. However, the mentor is using create-react-app CLI and React-Router-Dom for the routing system which doesn't really go with my current needs.
Moreover, I found this method online using HOC :
import React from 'react';
import Router from 'next/router';
const login = '/register?redirected=true'; // Define your login route address.
const checkUserAuthentication = () => {
return { auth: null }; // change null to { isAdmin: true } for test it.
};
export default WrappedComponent => {
const hocComponent = ({ ...props }) => <WrappedComponent {...props} />;
hocComponent.getInitialProps = async (context) => {
const userAuth = await checkUserAuthentication();
// Are you an authorized user or not?
if (!userAuth?.auth) {
// Handle server-side and client-side rendering.
if (context.res) {
context.res?.writeHead(302, {
Location: login,
});
context.res?.end();
} else {
Router.replace(login);
}
} else if (WrappedComponent.getInitialProps) {
const wrappedProps = await WrappedComponent.getInitialProps({...context, auth: userAuth});
return { ...wrappedProps, userAuth };
}
return { userAuth };
};
return hocComponent;
};
The code above helps me to have a private route that the user cannot access unless he's authenticated (currently no programming included), but on the other hand i still need a page in the following route :
'pages/user/activate/[token].js' // the link sent via email from express back end.
What i need now is to create this page using Next routing system in order to get the token and decode it to move forward with the back end and save the user into MongoDB, and in order to accomplish that, i have created my [token].js page with the following code :
import React, {useState, useEffect} from 'react'
import { ToastContainer, toast } from 'react-toastify';
import axios from 'axios';
import jwt from 'jsonwebtoken';
import { authenticate, isAuth } from '../helpers/auth';
import { Link, Redirect } from 'react-router-dom';
const Activate = ({ match }) => {
const [formData, setFormData] = useState({
email: '',
token: '',
show: true
});
const { email, token, show } = formData;
useEffect(() => {
let token = match.params.token;
let { email } = jwt.decode(token);
if (token) {
setFormData({ ...formData, email, token });
}
console.log(token, email);
}, [match.params.token]);
return (
<>
{isAuth() ? <Redirect to="/" /> : null}
<p>Account activated, please log in</p>
</>
)
};
export default Activate;
However, i keep getting this error :
TypeError: Cannot read property 'params' of undefined
at Activate (C:\Users\Hp\Desktop\SMP\client\.next\server\pages\user\activate\[token].js:245:13)
at processChild (C:\Users\Hp\Desktop\SMP\client\node_modules\react-dom\cjs\react-dom-
server.node.development.js:3353:14)
at resolve (C:\Users\Hp\Desktop\SMP\client\node_modules\react-dom\cjs\react-dom-
server.node.development.js:3270:5)
at ReactDOMServerRenderer.render (C:\Users\Hp\Desktop\SMP\client\node_modules\react-dom\cjs\react-
dom-server.node.development.js:3753:22)
at ReactDOMServerRenderer.read (C:\Users\Hp\Desktop\SMP\client\node_modules\react-dom\cjs\react-dom-
server.node.development.js:3690:29)
at renderToString (C:\Users\Hp\Desktop\SMP\client\node_modules\react-dom\cjs\react-dom-
server.node.development.js:4298:27)
at Object.renderPage (C:\Users\Hp\Desktop\SMP\client\node_modules\next\dist\next-
server\server\render.js:53:851)
at Function.getInitialProps (C:\Users\Hp\Desktop\SMP\client\.next\server\pages\_document.js:293:19)
at loadGetInitialProps (C:\Users\Hp\Desktop\SMP\client\node_modules\next\dist\next-
server\lib\utils.js:5:101)
at renderToHTML (C:\Users\Hp\Desktop\SMP\client\node_modules\next\dist\next-
server\server\render.js:53:1142)
I couldn't find a solution because i believe that i'm doing something wrong whether in my code or in the logic implemented.
Is there any way that i can do this properly ?
Thank you in advance !