Google email offline access using react native expo app - react-native

I am creating one app using react native expo, which allow end user to login by their google account , and then applicaton try to save the access_token so that server based applicatin can use this to send the email on their behalf ,
But when using google sing in , i am not getting refresh token and not able to send the email ,
Here is code example which i am using
I tried below method to get the access request
const [request, response, promptAsync] = Google.useIdTokenAuthRequest({
clientId: "XXXXXXX",
androidClientId:"XXXXXXX",
iosClientId:"XXXXXXX"
});
const [initializing, setInitializing] = useState(true);
const [user, setUser] = useState();
const sendNotification=useNotification()
//console.log(sendNotification)
useEffect(() => {
if (response?.type === "success") {
const { id_token } = response.params;
const auth = getAuth();
const credential = GoogleAuthProvider.credential(id_token);
signInWithCredential(auth, credential);
let decoded = jwt_decode(id_token);
socialLogin(decoded)
}
}, [response]);
And on server using this code to sending email
const { google } = require('googleapis');
const path = require('path');
const fs = require('fs');
const credentials = require('./credentials.json');
// Replace with the code you received from Google
const code = 'XXXXXXX';
//const code="XXXXXXX"
const { client_secret, client_id, redirect_uris } = credentials.installed;
const oAuth2Client = new google.auth.OAuth2(client_id, client_secret, redirect_uris[0]);
oAuth2Client.getToken(code).then(({ tokens }) => {
console.log('first')
const tokenPath = path.join(__dirname, 'token.json');
fs.writeFileSync(tokenPath, JSON.stringify(tokens));
console.log('Access token and refresh token stored to token.json');
}).catch(err=>console.log(err));

async function signInWithGoogleAsync() {
try {
const result = await Google.logInAsync({
androidClientId: YOUR_CLIENT_ID_HERE,
scopes: ["profile", "email"],
});
if (result.type === "success") {
onSignIn(result);
return result.accessToken;
} else {
return { cancelled: true };
}
} catch (e) {
return { error: true };
}
}

Well, I tried to create an application with Google login. To use the Google Sign-In method in a React Native Expo app, you will need to perform the following steps:
Set up a project in the Google Cloud Console and obtain a configuration file for your app.
Install the expo-google-sign-in package in your React Native app.
Import the GoogleSignIn object from the expo-google-sign-in package and use the initAsync method to initialize the Google Sign-In process.
Use the GoogleSignIn.askForPlayServicesAsync method to check if the device has the required Google Play Services installed.
Use the GoogleSignIn.signInAsync method to prompt the user to sign in with their Google account.
Once the user has signed in, you can use the accessToken and refreshToken properties of the returned object to make authorized requests to the Google APIs.
The code lines for the above steps are:
import { GoogleSignIn } from 'expo-google-sign-in';
// Initialize the Google Sign-In process
await GoogleSignIn.initAsync({
// Your config. values
});
// Check if the device has the required Google Play Services installed
const isPlayServicesAvailable = await GoogleSignIn.askForPlayServicesAsync();
if (!isPlayServicesAvailable) {
console.error('Google Play services are not available on this device.');
return;
}
// Prompt the user to sign in with their Google account
const { accessToken, refreshToken } = await GoogleSignIn.signInAsync();

Related

auth/popup-closed-by-user error 2 seconds after signInWithPopup

I am using firebase auth to authenticate users and do things on their behalf. I had this working before, and am having trouble figuring out what's going on now.
I'm using the example from the documentation almost unchanged:
import { getAuth, signInWithPopup, GoogleAuthProvider } from "firebase/auth";
import { initializeApp } from "firebase/app";
const firebaseConfig = {
// ...
};
const app = initializeApp(firebaseConfig);
const auth = getAuth(app);
export function signIn() {
const provider = new GoogleAuthProvider();
provider.addScope('https://www.googleapis.com/auth/contacts.readonly');
console.log('before sign in');
signInWithPopup(auth, provider)
.then((result) => {
console.log('success!');
const credential = GoogleAuthProvider.credentialFromResult(result);
const token = credential.accessToken;
const user = result.user;
console.log(token, user);
}).catch((error) => {
console.error(error.code);
});
}
document.addEventListener('click', ()=>signIn());
When I click in the page, this is what happens:
The authentication popup opens and the 'before sign in' log is triggered
Wait for ~2 seconds
signInWithPopup throws a auth/popup-closed-by-user error
The issue was related to these HTTP headers I configured on my server:
Cross-Origin-Opener-Policy: same-origin
Cross-Origin-Embedder-Policy: require-corp
I added these when I was experimenting with SharedArrayBuffer. Removing them fixed the issue.
For me, I had this error on iOS Emulator if I tried to put this into the Info.plist
<key>FirebaseAppDelegateProxyEnabled</key>
<false/>

AuthSession proxy error when trying to implement Expo AuthSession

I'm trying to get my expo AuthSession to work (sign up with google) in my react native app. I have been following this clip (https://www.youtube.com/watch?v=YX7IWOQIKA0) on youtube but get an error as soon as i try to navigate to the login screen.
The error message i get is:
Cannot use the AuthSession proxy because the project full name is not defined. Prefer AuthRequest (with the useProxy option set to false) in combination with an Expo Development Client build of your application. To continue using the AuthSession proxy, specify the project full name (#owner/slug) using the projectNameForProxy option.
Notes:
The app is working overall but breaks completely when i try to navigate to the LoginScreen where the code below exists. I have checked that the "https://auth.expo.io/#owner/slug" in the Google Cloud Credentials is correct. Have i missed to add something that i should've? I should also add that i am aiming to get this to work on both IOS and Android devices.
My relevant code to the problem is in my LoginScreen.tsx and it looks like this:
Imports:
import * as WebBrowser from 'expo-web-browser';
import * as Google from 'expo-auth-session/providers/google';
WebBrowser.maybeCompleteAuthSession();
GoogleAuthRequest:
const [request, response, promptAsync] = Google.useAuthRequest({
expoClientId: '*THE CLIENT ID FROM GOOGLE CLOUD CREDENTIALS IS PASTED HERE*',
iosClientId: 'GOOGLE_GUID.apps.googleusercontent.com',
androidClientId: 'GOOGLE_GUID.apps.googleusercontent.com',
webClientId: 'GOOGLE_GUID.apps.googleusercontent.com',
});
Code that runs when pressing "continue with google":
const signUpGoogleHandler = async () => {
const response = await promptAsync();
if (response.type === 'success') {
const {access_token } = response.params;
console.log('res params ', access_token);
}
console.log('test123');
const provider = new GoogleAuthProvider();
await signInWithRedirect(auth, provider);
getRedirectResult(auth)
.then((result) => {
const credential = GoogleAuthProvider.credentialFromResult(result);
const token = credential.accessToken;
const user = result.user;
}).catch((error) => {
const errorCode = error.code;
const errorMessage = error.message;
const email = error.customData.email;
const credential = GoogleAuthProvider.credentialFromError(error);
})
}
How my Client ID for my Web application is setup in Google Cloud (the Authorized redirect URIs is written as https://auth.expo.io/#myusername/slugname):
Client ID for Web application

React Native Google Sign In User Birthday Access Problem

I am trying to get user birthday when user is registered with Google Sign In and want to display his/her info in the profile page.
Google Sign In was implemented via firebase.
And then, I went to google developer console and made
Added People Apis and then go to OAuth consent screen
Select External
Added App domain, Authorized domains, Developer contact information
Added birthday scope
Added test users
Save and Back to Dashboard
Birthday info is set as public
The problem is still my test users cannot login to Google. It says "Access Denied. your app did not complete the google verification process. The app is currently being tested. Only approved test users can access the app."
I can only login with my developer account.
And when I logged in, in the console, I can see the birthday scope is added in scopes array. However the birthday info is still not in my user object.
I use "#react-native-google-signin/google-signin": "^6.0.1" package.
Can someone help me please ?
Do I need to verify the domain/owner to be able to see birthday info ?
Or the package does not support this info ?
Why my test users cannot login even though I added them ?
My code is below
export const auth = {
initGoogleSignIn: function () {
GoogleSignin.configure({
scopes: [
'https://www.googleapis.com/auth/user.birthday.read',
'https://www.googleapis.com/auth/user.gender.read',
'https://www.googleapis.com/auth/plus.login',
],
// scopes: ['https://www.googleapis.com/auth/plus.login'],
webClientId: Config.GOOGLE_WEB_CLIENT_ID,
offlineAccess: false,
});
},
};
import auth from '#react-native-firebase/auth';
export const googleLogin = () => {
return async dispatch => {
try {
await GoogleSignin.hasPlayServices({showPlayServicesUpdateDialog: true});
const isSignedIn = await GoogleSignin.isSignedIn();
if (isSignedIn && Platform.OS === 'android') {
await GoogleSignin.revokeAccess();
}
const {idToken} = await GoogleSignin.signIn();
const token = await GoogleSignin.getTokens();
dispatch(handleSocialSignup(COMMON.GOOGLE, token?.accessToken));
// Create a Google credential with the token
const googleCredential = auth.GoogleAuthProvider.credential(idToken);
// Sign-in the user with the credential
const userSignIn = auth().signInWithCredential(googleCredential);
Alert.alert(JSON.stringify(userSignIn));
userSignIn.then(user => Alert.alert(user)).catch(err => console.log(err));
} catch (error) {
if (error.code === statusCodes.SIGN_IN_CANCELLED) {
console.log('Cancelled by user');
} else if (error.code === statusCodes.IN_PROGRESS) {
// operation (e.g. sign in) is in progress already
} else if (error.code === statusCodes.PLAY_SERVICES_NOT_AVAILABLE) {
// play services not available or outdated
} else {
Sentry.captureException(error);
Sentry.captureMessage(strings.common.undefinedError);
console.log('some other error happened', error);
dispatch(showSnackbar(strings.common.undefinedError));
}
return false;
}
const [googleUserName, setGoogleUserName] = useState('');
const getGoogleUserName = async () => {
const currentUser = await GoogleSignin.getCurrentUser();
setGoogleUserName(currentUser);
console.log('currentUser', currentUser);
};
useEffect(() => {
getGoogleUserName();
}, []);
console.log('googleUserName', googleUserName);

OctoKit with Auth0 (Github Login) in NextJS

I am building a Next JS app that has Github Login through Auth0 and uses the Octokit to fetch user info / repos.
In order to get the IDP I had to setup a management api in auth0. https://community.auth0.com/t/can-i-get-the-github-access-token/47237 which I have setup in my NodeJs server to hide the management api token as : GET /getaccesstoken endpoint
On the client side : /chooserepo page, I have the following code :
const chooserepo = (props) => {
const octokit = new Octokit({
auth: props.accessToken,
});
async function run() {
const res = await octokit.request("GET /user");
console.log("authenticated as ", res.data);
}
run();
And
export const getServerSideProps = withPageAuthRequired({
async getServerSideProps({ req, params }) {
let { user } = getSession(req);
console.log("user from get session ", user);
let url = "http://localhost:4000/getaccesstoken/" + user.sub;
let data = await fetch(url);
let resData = await data.text();
return {
props: { accessToken: resData }, // will be passed to the page component as props
};
},
});
However, I keep getting Bad credentials error. If I directly put the access token in the Octokit it seems to work well, but doesn't work when it's fetching the access token from the server.
It seems like Octokit instance is created before server side props are sent. How do I fix it ?
I figured out the error by comparing the difference between the request headers when hardcoding and fetching access token from server. Turns out quotes and backslashes need to be replaced (and aren't visible when just console logging)

React native IAP (Android) Validation receive Problem

I added react-native-iap library( https://github.com/dooboolab/react-native-iap ) for using subscriptions.
Work well on IOS but I have some issues on Android.
On Android side perfectly working subscription person. I need to check expired date for block expired users. For IOS side I use RNIap.validateReceiptIos(receiptBody, false) this give me expired date and expired_ms. I used these but on Android side I use
const result = await RNIap.validateReceiptAndroid(
'com.test.app',
'test_sku_1m_acc',
//below token comes from RNIap.getPurchaseHistory() -> purchaseToken
'faolcmekaiihplkgoaljnfjh.AO-J1OxykzAlHtDtn0-NRHGUBavVbhA_bBLVYPIlc4yUMkHTLXrA_V66lBsZYKknGt-gcCZgznCXKNww0BW9FLuf4ah23HhB_Q',
//below token comes from localhost node.js backend (nodejs Code *)
'ya29.c.Kp0B7QcDz53Bt4YeXHZ6lOjSm7NW9FW0pNwSgX78EQVYNlhSoY2ZM-lhug4mODUy8lzghDuJ8SVj9Lw1Dtvh8ZuB8YFEEv_phTc_YOwJ2PK1_WPyS3nRuF-B96wJADN0F6QBCShHl2uOxEAocNyoCKzb6k1YVPBZxENGS6kwBXfcnNR_Hu4-TKpnl3y0s6vX5_XE_viR46d9cO73cdXIjA',
true, // for subscriptions should pass "true"
).catch((err) => {
console.log('err', JSON.stringify(err));
})
the code returns {"statusCode":401,"line":126336,"column":53,"sourceURL":"http://192.168.2.27:8081/index.bundle?platform=android&dev=true&minify=false"}
This problem is from me or from library. Thank you for your response.
Note: node.js Code *:
const open = require("open");
const { google } = require("googleapis");
const account = require("./service-account.json");
const express = require("express");
const app = express();
const JWTClient = new google.auth.JWT(
account.client_email,
null,
account.private_key,
["https://www.googleapis.com/auth/androidpublisher"]
);
app.get("/", (req, res) => {
JWTClient.getAccessToken((err, token) => {
if (err) {
return res.status(404).send("get access token failed");
}
return res.status(200).send(token);
});
});
app.listen(8000);
open("http://localhost:8000");