How do you identify the Expo project that an Expo Push Notification Token belongs to? - react-native

Premise
For better or for worse, I use two Expo accounts for my production and development environments.
Production Expo Account: prod-proj
Development Expo Account: dev-proj
I use Expo's push notification service to send push notifications to my users. I store each user's Expo Push Notification Token on their user document. i.e.:
User
id: 1
name: Jimothy
token: ExponentPushToken[di3ja!-lk2^(24af]
Through an unfortunate series of events, most users in my database have a push notification created using the prod-proj Expo project, but a few users have a push notification created using dev-proj.
Problem
When I try to chunk and send push notifications to all my users, I get an error from Expo:
Error: All push notification messages in the same request must be for the same project; separate your push notifications by project.
But my tokens are all mixed up!
How can I separate the Expo Push Notification Tokens by project?

Figured out the answer to this one!
In the expo-server-sdk Node client for Expo, the details field of the error returned by Expo contains all the information about which tokens belong to which projects.
Example Code
const { Expo } = require('expo-server-sdk');
const expo = new Expo();
const sendNotifications = async () => {
const notifications = [ ... ];
const chunks = expo.chunkPushNotifications(notifications);
for (const chunk of chunks) {
try {
await expo.sendPushNotificationsAsync(chunk)
}
catch (error) {
console.log(JSON.stringify(error));
// The important part:
// error.details = { EXPO_PROJECT_NAME: [ ExponentPushTokens ] }
}
}
}

I'm facing the same issue right now and probably the best thing we can do is to send the expo push token along with the experience id to the backend. From there, group tokens by experience and send those groups as we'd normally do.
import Constants from "expo-constants";
const experienceId = Constants.manifest.id; // #user/project-slug

Related

Unable to receive push notification send via Firebase console in iOS using React-Native-Firebase

I have tried to follow this guide https://rnfirebase.io/messaging/usage/ios-setup to set up push notifications for my React-Native application. In particular, I have done the following:
Adding Push Notification & Background Mode capabilities(with Remote fetch and background activities)
Register a key(with APNs enabled) in Apple developer account and upload it to firebase console settings with the correct KeyID(obtained when registering the key) and TeamID(obtained from developer's membership detail)
Register the App Identifier(with APNs capability). Since there are two bundle Identifiers for my project - org.reactjs.native.example.AppName and org.test.AppName, I have tried both but none works.
Register a Provisioning profile. I believe this wil automatically sync with my Xcode.
I note that I can further configure the APNs capability after registering the App Identifier, but this is not mentioned in the guide and I didn't do that:
To configure push notifications for this App ID, a Client SSL Certificate that allows your notification server to connect to the Apple Push Notification Service is required. Each App ID requires its own Client SSL Certificate. Manage and generate your certificates below.
In my React-Native application, I have the following code:
const App => {
useEffect(() => {
async function requestUserPermission() {
const authStatus = await messaging().requestPermission();
const enabled =
authStatus === messaging.AuthorizationStatus.AUTHORIZED ||
authStatus === messaging.AuthorizationStatus.PROVISIONAL;
if (enabled) {
console.log('Authorization status:', authStatus);
}
}
requestPermission();
});
useEffect(() => {
async function getToken() {
await messaging().registerDeviceForRemoteMessages();
const token = await messaging().getToken();
console.log(token);
}
getToken();
});
...
}
After accepting the notification permission request when the app launch. This will output the FCMs token, which I use to send a test message in the Firebase console.
Did I miss any steps? Is it possible to send push notifications in React-Native debug built running under metro in the first place? Thank you in advance.
I figured out the problem. It is because I used a different bundle ID when building the product in XCode and when registering the identifier in Apple Developer Account. The steps by steps does work as of writing.

Duplicate fcm push notification in react native android backgroundhandler

I developed react native application with rnfirebase and notifee for sending the push notification. foreground is working properly, message is displayed only once. but the background notification is displaying twice like one is from messaging().setBackgroundMessageHandler and another one is android's default push notification. First message is from default push notification and next one is from firebase messaging. So how do I remove android's default push notification. I'm also checked that first default notification is not using the firebase messaging and notifee. It's comes from outside of react native like android's native push notification
The notifications that you are seeing are most likely one from firebase and another from Notifee.
In my project I was handling the notifications that were coming from firebase via firebase.messaging().onMessage and inside this listener I was showing a local notification using Notifee so that the notification shows in the foreground.
async showNotificationInForeground(message: FirebaseMessagingTypes.RemoteMessage) {
const { messageId, notification, data } = message
const channelId = await Notifee.createChannel({
id: messageId,
name: 'Pressable Channel',
importance: AndroidImportance.HIGH,
})
await Notifee.displayNotification({
title: notification?.title || '',
body: notification?.body || '',
data,
android: {
channelId,
importance: AndroidImportance.HIGH,
pressAction: {
id: messageId,
},
smallIcon: 'ic_stat_name',
localOnly: true,
},
})
}
However what was happening was that I was calling this showNotificationInForeground method to show the local Notifee notification on both firebase's background and messaging listeners ie: firebase.messaging().onMessage and firebase.messaging().setBackgroundMessageHandler
So what I ended up doing was only calling the showNotificationInForeground method in onMessage listener and not in setBackgroundMessageHandler, which resulted in showing the local notification in the foreground, and the firebase notification in the background.
If this is not the case for you, you are most likely registering an extra notification receiver inside your AndroidManifest.xml file which is causing the duplication
I had the same problem, where there were two notifications a) 1 from notifee (which I wanted to keep) b) from FCM (which I didnt want).
Sending a data only message from my custom server solved the issue. Below is a snippet of the serverside for sending data only message :
const admin = require("firebase-admin");
// Do other stuff like create an express server to listen and trigger sending message
// Note dont forget to to initialize the app
await admin.messaging().sendToDevice(
tokens, // ['token_1', 'token_2', ...]
{
data: {
owner: "Me",
user: "My Friend",
},
},
{
// Required for background/quit data-only messages on iOS
contentAvailable: true,
// Required for background/quit data-only messages on Android
priority: "high",
}
);
You can use other methods as well, just make sure that FCM doesnt include notification and is data only.

Firebase Google Sign In not working in React Native

import firebase from 'firebase';
import React from 'react';
import { useAuthState } from 'react-firebase-hooks/auth';
import App from './testlogin';
import { View, Text, Button } from 'react-native';
import { firebaseConfig } from './firebaseConfig';
const app = firebase.initializeApp(firebaseConfig);
const auth = app.auth();
const db = app.firestore();
const googleProvider = new firebase.auth.GoogleAuthProvider()
export const signInWithGoogle = async () => {
try {
const res = await auth.signInWithPopup(googleProvider);
const user = res.user;
const query = await db
.collection("users")
.where("uid", "==", user.uid)
.get();
if (query.docs.length === 0) {
await db.collection("users").add({
uid: user.uid,
name: user.displayName,
authProvider: "google",
email: user.email,
});
}
} catch (err) {
console.error(err);
alert(err.message);
}
};
This code accesses the google sign in method and should pop up a window, but I don't get anything when I click the button. I have been having difficulty with implementing Firebase in React Native and this is one of the examples. I need an example of easy Google sign in button in React Native.
I recommend you to use https://rnfirebase.io/ library (their docs are quite helpful).
Here are their instructions for Google Sign-in: https://rnfirebase.io/auth/social-auth#google.
According to them, you need to
ensure the "Google" sign-in provider is enabled on the Firebase Console.
also install #react-native-google-signin/google-signin',
then "Before triggering a sign-in request, you must initialize the Google SDK using your any required scopes and the webClientId, which can be found in the android/app/google-services.json"
Finally, as because following:
Starting April 2020, all existing applications using external 3rd party login services (such as Facebook, Twitter, Google etc) must ensure that Apple Sign-In is also provided. - You would also need to support iOS / and Apple Sign-in too. (if are building app also for iOS).
NOTE: I also remember that I had to put both, SHA1 and SHA256 hashes in the Firebase Console as some Firebase services were not working without it.
How to find SHA hashes in Android for Firebase (signing with Keystore that will be used in production should be already configured):
The debug signing certificate is optional to use Firebase with your app, but is required for Dynamic Links, Invites and Phone Authentication. To generate a certificate run
cd android && ./gradlew signingReport
and copy the SHA1 and SHA256 from the debug key. This generates two variant keys. You can copy the 'SHA1' that belongs to the debugAndroidTest variant key option.
Again, I recommend you to put both SHA hashes in Firebase Console.

Stripe Connect Account - return_url - Link back to Expo application

I'm on-boarding users onto Stripe connect. My node server generates a temporary HTTPS URL so that customers can sign on. According to their docs I need to provide a URL for when they complete the application.
https://stripe.com/docs/api/account_links/create#create_account_link
I have an Expo application. The user will open up the URL in their browser. However when they complete their application I would like them to go back to Expo App. If I try to use expo://MYAPP/ as the return_url, Stripe does not recognize the URL schema.
Does anyone have an idea how i can return the user back into my application after completing their on-boarding done via the browser?
For anyone one out there who runs into this post, this is was my solution. Your app has to link to a website. I am using Expo, but this is the React code to generate the link.
import * as WebBrowser from 'expo-web-browser';
import * as Linking from 'expo-linking';
const openPage = async () => {
try {
const result = await WebBrowser.openAuthSessionAsync(
`${url}?linkingUri=${Linking.createURL('/?')}`,
);
let redirectData;
if (result.url) {
redirectData = Linking.parse(result.url);
}
setstate({ result, redirectData });
} catch (error) {
console.log(error);
}
};
When you load the site, make sure to pass the URL that was generated from your backend
Backend code:
stripe.accountLinks
.create({
type: 'account_onboarding',
account: accountID,
refresh_url: `https://website.com/refresh`,
return_url: `https://website.com/return`,
})
When the user has the site open, have a button that redirects to the stripe URL.This is how i thought it went first
App -> Stripe connect
instead you have to approach it like this
App -> Website -> Stripe connect

Expo App with Google Login is not redirecting to app

I having a problem with my Google Sign In, I'm Currently using EXPO app I wish at all cost not to eject / opt out of Expo, the problem is that when I click on the Button to log in with Google in my App it does take me to the login page for me inside the browser, but once I put my Google credentials, it just lands inside the Google.com page.
I checked a lot of posts but still I'm unable to get it to come back to the app.
My app Code to log in is:
//import de Google en Expo
import * as Google from 'expo-google-app-auth';
import * as AppAuth from 'expo-app-auth';
export const googleLogin = () => {
console.log('***Entro en Google***');
return async dispatch => {
try {
const result = await Google.logInAsync({
androidClientId: '***my ID Censored***',
scopes: ['profile', 'email'],
behavior: 'web',
redirectUrl: `${AppAuth.OAuthRedirect}:/oauthredirect`
});
console.log('***Hizo Consulta***');
if (result.type === 'success') {
console.log(result.accessToken);
} else {
return { cancelled: true };
}
} catch (e) {
return { error: true };
}
}
};
I checked on many posts that the issue was the redirect URL and I tried setting 4 options:
${AppAuth.OAuthRedirect}:/oauthredirect
${AppAuth.OAuthRedirect}:/oauthredirect/google
'host.exp.exponent:/oauth2redirect/google'
'host.exp.exponent:/oauth2redirect/'
None of them worked, I did the last 2 of host.exp.exponent as that is the Bundle Identifier used by Expo on their documentation:
https://docs.expo.io/versions/latest/sdk/google/
Create an Android OAuth Client ID
Select "Android Application" as the Application Type. Give it a name
if you want (maybe "Android Development").
Run openssl rand -base64
32 | openssl sha1 -c in your terminal, it will output a string that
looks like A1:B2:C3 but longer. Copy the output to your clipboard.
Paste the output from the previous step into the
"Signing-certificate fingerprint" text field.
Use host.exp.exponent as the "Package name".
4.Click "Create"
You will now see a modal with the Client ID.
The client ID is used in the androidClientId option for Google.loginAsync (see code example below).
I did just that, and now it always gets stuck in the google.com page, any Ideas or recommendations?
Kind Regards
What have you added to your bundle id (e.g "Apple bundle ID")?
Take into account that there is a difference between production and development.
In development, you should use the default expo apple bundle id so you will be allowed to use the google login (and you won't get redirect_fail).
The default apple bundle id for development using expo is: host.exp.exponent
In production, you should have your own bundle id.
Please write up a follow-up message if any extra help is needed.
In app.json,
package name as to be all in small letters like com.app.cloneapp