Firebase throws "Cannot parse ID Token" after passing FacebookAuthProvider.credential(access_token) - javascript

I am attempting to integrate Facebook authentication into my app that interacts with firebase auth. I have followed the guide provided at the expo.dev docs for Facebook but am having a hard time getting it working. I am able to successfully log in to Facebook, and return an access_token. I am even successfully generating the firebase credential with FacebookAuthProvider.credential(access_token). But when trying to then authenticate with firebase I get a Cannot parse ID token. I was able to implement authentication with Google with no issues following the same methods but Facebook just will not work.
Here is the code in question:
import React, { useEffect, useState } from "react";
import { View } from "react-native";
import { FontAwesome5 } from "#expo/vector-icons";
import { useDispatch, useSelector } from "react-redux";
import * as WebBrowser from 'expo-web-browser';
import { ResponseType } from 'expo-auth-session';
import * as Facebook from 'expo-auth-session/providers/facebook';
import { addError } from "../../redux/actions/error.actions";
import { firebaseLogin } from "../../redux/actions/firebase.actions";
import { auth, facebookProvider } from '../../config';
WebBrowser.maybeCompleteAuthSession();
export default function FacebookLogin() {
const dispatch = useDispatch();
const [ request, response, promptAsync] = Facebook.useAuthRequest({
responseType: ResponseType.Token,
clientId: 'XXXXXXXXXXXXXXX',
});
useEffect(() => {
if (response?.type === 'success') {
const {access_token} = response.params;
console.warn(`access_token type: ${typeof access_token}`);
const credential = facebookProvider.credential(access_token);
auth.signInWithCredential(credential).then(
(userCredential) => {
if (userCredential.additionalUserInfo.isNewUser) {
console.warn('New Facebook User.');
} else {
dispatch(firebaseLogin(userCredential.user));
}
},
(error) => {
let email = error.email;
let credential = error.credential;
// TODO: Handle linking of account if credential exists.
if (error.code === "auth/popup-closed-by-user") {
console.log('user closed popup');
}
}
)
.catch((error) => {
setError(true);
setErrorMessage({ type: "Facebook Login", message: error });
dispatch(addError({ type: "Facebook Login", message: error }));
});
}
}, [response]);
return (
<View style={{ marginBottom: 10 }}>
<FontAwesome5.Button
name="facebook-f"
backgroundColor="#3B5998"
onPress={promptAsync}
borderRadius={24}
size={18}
style={{ marginLeft: 7, marginRight: 7 }}
>
Sign In with Facebook
</FontAwesome5.Button>
</View>
)
}
The only thing not visible in this component is how auth and facebookProvider are defined.
import firebase from 'firebase/app';
import 'firebase/auth';
if (!firebase.apps.length) {
firebase.initializeApp(firebaseConfig);
} else {
firebase.app();
}
export const auth = firebase.auth();
export const facebookProvider = new firebase.auth.FacebookAuthProvider();
export {firebase};

You have to enable 'Advanced Access' for 'public_profile'. Also any other fields you request at login, in my case I was grabbing the 'email' field as well. This is under App Review -> Permissions and Features in the Facebook Developer Console.
Img of FB Dev Console Location
Also make sure you are using the function without the nonce if you are using a custom button instead of the default Facebook one. That was also an issue I had with this.
let loginManager = LoginManager()
loginManager.logIn(permissions: ["public_profile", "email"],from: controller) { result, error in
if let error = error {
print(error.localizedDescription)
return
}
guard let idTokenString = result.token?.tokenString
else { throw NSError() }
let credential = OAuthProvider.credential(withProviderID: "facebook.com",
accessToken: idTokenString)
Auth.auth().signIn(with: credential) { (authResult, error) in }
}

Related

Firebase: Error (auth/network-request-failed) - React Native web

I have a React Native app that needs to be served on its web version. So, I need to implement the Google OAuth with the firebase js lib.
I'm following these steps.
If I use the same code from the docs I have a weird error (of course I put the proper config object in the initializeApp method):
import * as React from 'react';
import * as WebBrowser from 'expo-web-browser';
import { ResponseType } from 'expo-auth-session';
import * as Google from 'expo-auth-session/providers/google';
import { initializeApp } from 'firebase/app';
import { getAuth, GoogleAuthProvider, signInWithCredential } from 'firebase/auth';
import { Button } from 'react-native';
// Initialize Firebase
initializeApp({
/* Config */
});
WebBrowser.maybeCompleteAuthSession();
export default function App() {
const [request, response, promptAsync] = Google.useIdTokenAuthRequest(
{
clientId: 'Your-Web-Client-ID.apps.googleusercontent.com',
},
);
React.useEffect(() => {
if (response?.type === 'success') {
const { id_token } = response.params;
const auth = getAuth();
const credential = GoogleAuthProvider.credential(id_token);
signInWithCredential(auth, credential);
}
}, [response]);
return (
<Button
disabled={!request}
title="Login"
onPress={() => {
promptAsync();
}}
/>
);
}
The error is:
I get this error specifically when I call signInWithCredential()
The strange thing is that in the Network tab (Chrome DevTools) I have a successful response:
And the problem is: If I want to get the user information with onAuthStateChanged, it is null because the login attempt just failed (despite the dev-tools being all good)
Note: I've tried with a new starter project, moving our dependencies with the same version as they are here and it's working.
But I can't figure out what can be happening with this project.
EDIT: Adding images of the requests that are being made:

Navigating to a page on load in react JS

I am working on user registration setup and stuck on a problem where I am not able to redirect from a page 'localhost:3000/activate/tokenNo.'(Activation.jsx file) on load to my main page (App.jsx file) .
Here is my activation.jsx file :
import React, { useEffect } from 'react';
import { useNavigate } from "react-router-dom";
const Activate = () => {
const navigate = useNavigate();
useEffect(() => {
navigate('/')
}, [navigate])
return (
<div>Activation Page</div>
)
}
export default Activate;
Here is my App.jsx file :
import React from 'react';
export const App = () => {
return <div>Dashboard</div>;
};
export default App ;
My activationController.js file :
exports.activationController = (req,res) => {
const {token} = req.body
if(token){
//Verify the token is valid or not or expired
jwt.verify(token , process.env.JWT_ACCOUNT_ACTIVATION ,
(err , decoded) => {
if(err){
return res.status(401).json({
error: "Expired Token , Signup again"
})
}
else{
//if valid save to database
//Get name email password from token
const {name , email , password} = jwt.decode(token)
const user = new User({
name ,
email ,
passsword
})
user.save((err,user) => {
if(err){
return res.status(401).json({
error: errorHandler(err)
})
}
else{
return res.json({
success: true ,
message: "Signup successful",
user
})
}
})
}
})
}
else{
return res.json({
message: "error happening please try again"
})
}
}
In my auth.routes.js
router.post('/activation', activationController)
I recieved an error in my console --> index.tsx:25 No routes matched location "/activate/tokenNo."
My reference folder --> https://github.com/Mohammed-Abdelhady/FULL-MERN-AUTH-Boilerplate
You may need "Navigate( to="where to go") instead. Leaving the dependencies open in useEffect will make the code run only once at load time. If you include [navigate] it will run every time. Not sure what you are trying to achieve, but Router/Routes/Route may be a better mechanism. If you are doing login/registration, in your login you would have a Link to your registration page. Then you could setup 2 routes, one for Login and one for Registration.
import React, { useEffect } from 'react';
import { Navigate } from "react-router-dom";
const Activate = () => {
useEffect(() => {
Navigate( to='/');
}, [])
return (
<div>Activation Page</div>
)
}
export default Activate;
Often we require to perform some extra action on component or page render initially.
Like, Data fetching, etc.
To implement like this we can use the useEffect hook from react and state our execution into it.
I can't see the useNavigate hook in the latest version of react-router-dom so we can use the useHistory hook.
For Example:
import React, { useEffect } from "react"
import { useHistory } from 'react-router-dom'
const App = () => {
const history = useHistory();
useEffect(() => {
history.push('Page2URL')
}, [history])
return (
<div>Page1</div>
)
}

React Native cannot find module

I am trying to build a basic notification app that uses the react-native-background-task module. For some reasons it gives this error: Cannot read properties of undefined (reading 'schedule')
Below is the code:
import { StatusBar } from 'expo-status-bar';
import { StyleSheet, Text, View } from 'react-native';
import BackgroundTask from 'react-native-background-task';
// import { Notifications, Permissions, Constants } from 'expo';
import * as Notifications from 'expo-notifications';
import * as Permissions from 'expo-permissions';
import React, { useEffect } from 'react'
console.log("object")
BackgroundTask.define(async () => {
console.log("bgtask")
// if time is 12pm, fire off a request with axios to fetch the pills info
// Notification configuration object
const localNotification = {
title: text,
body: 'msg',
data: data,
ios: {
sound: true
}
}
// trigger notification, note that on ios if the app is open(in foreground) the notification will not show so you will need to find some ways to handling it which is discribed here https://docs.expo.io/versions/latest/guides/push-notifications
Notifications
.presentLocalNotificationAsync(localNotification)
.catch((err) => {
console.log(err)
})
BackgroundTask.finish()
})
export default function App() {
useEffect(() => {
console.log("uef")
const componentDidMount = async () => {
// allows the app to recieve notifications (permission stuff)
console.log("cdm")
registerForPushNotificationsAsync().then(() => {
console.log("bg sche")
BackgroundTask.schedule()
}).catch((e) => {
console.log("err")
console.log(e)
});
}
componentDidMount()
}, []);
const registerForPushNotificationsAsync = async () => {
console.log("reg")
const { status } = await Permissions.askAsync(Permissions.NOTIFICATIONS);
if (status !== 'granted') {
console.log("permission not granted")
return;
}
console.log("permission granted")
let deviceToken = await Notifications.getExpoPushTokenAsync()
}
return (
<View style={styles.container}>
<Text>Open up App.js \o start working on your app!</Text>
<StatusBar style="auto" />
</View>
);
}
The problem comes when I do the BackgroundTask.schedule() function. It says in the error that BackgroundTask is undefined. I know for a fact that all the other functions worked fine because the console.logs all get printed right until "bg sche" and then it goes and print "err" in the catch block.
I also tried to use ctrl + click on the package name that should bring me to source where this BackgroundTask object is exported but it doesn't work like it usually does for other modules. So I think for some reasons the module can't be found but I have already installed it and it is in my package.json file and I see it in my node_modules folder.

Why does Okta-React Login redirect to blank page when using Django?

I am using Okta-React for authentication in my React project and when I run the React test server my login authenticates successfully and redirects to the account page. When I run the React build command and render the build files with Django, my login authenticates properly, but when it redirects back to my site I get a blank /implicit/callback page, no login token or user info, and the code & state gets stuck in the URL. Does anyone know why this is only happening when using Django, and what I can do to resolve this issue?
Here is my authConfig:
const config = {
issuer: 'https://dev-#######.okta.com/oauth2/default',
redirectUri: window.location.origin + '/implicit/callback',
clientId: '#################',
pkce: true
};
export default config;
Here is my accountAuth
import React, { useState, useEffect } from 'react';
import { useOktaAuth } from '#okta/okta-react';
import '../scss/sass.scss';
import "../../node_modules/bootstrap/scss/bootstrap.scss";
import 'react-bootstrap';
const AccountAuth = () => {
const { authState, authService } = useOktaAuth();
const [userInfo, setUserInfo] = useState(null);
useEffect(() => {
if (!authState.isAuthenticated) {
// When user isn't authenticated, forget any user info
setUserInfo(null);
} else {
authService.getUser().then((info) => {
setUserInfo(info);
});
}
}, [authState, authService]); // Update if authState changes
localStorage.setItem("username", userInfo && userInfo.given_name)
const login = async () => {
// Redirect to '/account_page' after login
localStorage.setItem("accountLink", "/account_page")
localStorage.setItem("loginPostingVisibilityStyle", { display: "none" })
localStorage.setItem("postingVisibleStyle", { display: 'block' })
authService.login('/auth_index');
}
const logout = async () => {
// Redirect to '/' after logout
localStorage.setItem("username", null)
localStorage.setItem("accountLink", "/auth_index")
localStorage.setItem("loginPostingVisibilityStyle", { display: "block" })
localStorage.setItem("postingVisibleStyle", { display: 'none' })
authService.logout('/');
}
return authState.isAuthenticated ?
<button className="settings-index" onClick={logout}>Logout</button> :
<button className="settings-index" onClick={login}>Login</button>;
};
export default AccountAuth;
Here is an example of the URL when it's stuck
http://localhost:8000/implicit/callback?code=-mRoU2jTR5HAFJeNVo_PVZsIj8qXuB1-aioFUiZBlWo&state=c9RXCvEgQ4okNgp7C7wPkI62ifzTakC0Ezwd8ffTEb29g5fNALj7aQ63fjFNGGhT
It doesn't look like you're handling the callback to exchange the authorization_code for tokens. You might want to check out Okta's React sample app to see how it works.

How to access a function from React Functional Component in a Normal Javascript File?

Greetings Javascript Developers. I'm stuck in a complex situation now where I need to access a function inside one of my functinal components outside in a normal js file.
Ok So here's what I'm doing: This is my Authorizer.js functional Component.
import React, { createContext, useState, useEffect, useContext } from "react";
import SplashScreen from "react-native-splash-screen";
import { useStore } from "../config/Store";
import { useDatabase } from "../config/Persistence";
import { getSessionCredentials } from "../config/Persistence";
import NavigationDrawer from "./NavigationDrawer";
import AuthStacks from "./AuthStacks";
const AuthContext = createContext();
export const useAuthorization = () => useContext(AuthContext);
export function Authorizer() {
//TODO check whether user is already signed in or not.
const realm = useDatabase();
const { state, dispatch } = useStore();
const [isAuthorized, setAuthorization] = useState(false);
useEffect(() => {
VerifyCredentials();
}, []);
async function VerifyCredentials() {
//TODO Check from Async Storage?
var session = await getSessionCredentials();
console.log("saved session", session);
if (session) {
await DispatchShopData();
await setAuthorization(true);
} else {
await setAuthorization(false);
}
sleep(1000).then(() => {
SplashScreen.hide();
});
}
async function DispatchShopData() {
try {
let shop = await realm.objects("Shop");
await dispatch({ type: "UPDATE_SHOP_DETAILS", payload: shop[0] });
} catch (error) {
console.log("failed to retrieve shop object", error);
}
}
function sleep(ms) {
return new Promise((resolve) => setTimeout(resolve, ms));
}
return (
<AuthContext.Provider value={{ setAuthorization }}>
{isAuthorized ? <NavigationDrawer /> : <AuthStacks />}
</AuthContext.Provider>
);
}
This component basically handles my Authentication Flow, whether to show the Navigation Drawer or the Login Screen. Now I have another simple javascript file ApiService.js which does not have any components, only simple js functions.
import Axios from "axios";
import { getAuthToken } from "../config/Persistence";
import { LogoutUser } from "../config/Persistence";
import { Alert } from "react-native";
const BASE_URL = "#########################";
/** Defined my Api Endpoints Here */
let service = Axios.create({
baseURL: BASE_URL,
timeout: 10000,
});
service.interceptors.response.use((response) => {
console.log("[API] response intercepted data", response.data.message);
if (!response.data.status && response.data.tokenExpired) {
//Auth token has Expired. Show user Alert for Session Expired & redirect to login screen.
Alert.alert(
"Your Session has Expired!",
"Don't worry though. You just need to login again & you're set.",
[
{
text: "Continue",
style: "default",
onPress: () => {
LogoutUser()
.then((success) => {
if (success) {
//TODO Find a way to Access this function from Authorizer.js Component.
//setAuthorization(false);
}
})
.catch((error) => {
console.log("failed to logout after session expiry", error);
});
},
},
]
);
}
return response;
});
/** Defined my other api functions called inside my other components */
function TestSampleApi() {
try {
return new Promise(async function (resolve, reject) {
const response = await service.get("https://jsonplaceholder.typicode.com/users");
if (response.data != null) {
resolve(response.data);
} else {
reject(response.status);
}
});
} catch (error) {
console.log("request error", error.message);
}
}
export {
TestSampleApi,
/** Exporting other api functions as well */
};
In my ApiService.js file, I've setup a response interceptors whose job is to catch the default auth token expired response and SignOut user immediately and take him to the Login Screen. Here's now where my issue comes.
In normal scenarios, where I need to access functions from one component inside another component, I can manage is using CreateContext() and useContent() hooks. However, how do I access the useState function setAuthorization in my Authorizer.js components in my ApiService.js file as a normal js function.
I only need to call setAuthorization(false) from my response interceptor block to make the user return to the Login Screen. Problem is idk how to access that state setter function. So any help would be greatly appreciated.

Categories

Resources