Push Messages⚓︎
This scenario describes how to set up Firebase Cloud Messaging (FCM) for your Android apps and Apple Push Notification Service (APNS) for your iOS apps, required to receive the push notifications from the server (e.g., push login, logout, account removal... ). We will also describe a fallback method to pull the server directly for any outstanding push notifications. The Mobile SDK will take care of deduplication of push messages it receives multiple times.
Important
Setting up push messages for your app, also requires a setup at the message center. Follow the steps described here to set up push messaging from the server side.
Info
Messages that are sent over FCM/APNS (or being pulled directly by the app) are encrypted for your app instance. The Mobile SDK takes care of the decryption and processing of those.
Receive Push Messages⚓︎
Android⚓︎
On Android, you will need to add a google-services.json
file to your app, that you can generate in the Firebase Console under Settings > General > Your Apps
. Make sure that it has an entry for the application id(s), e.g., com.nextauth.authenticator, of your app, including all build flavours, e.g., com.nextauth.authenticator.beta.
When starting the app, register the FCM token. You can do this by adding the following code to the onCreate()
method of MyApplication
.
FirebaseMessaging.getInstance().getToken().addOnSuccessListener(FCMToken -> {
NextAuth.getNextAuth().updatePushToken(FCMToken);
});
Next, create a service that extends the FireBaseMessagingService
to receive the FCM messages and handle updates of the FCM token. Here you should also handle notifications to be shown to the user, see NextAuthPushMessagingService.java
and NextAuthNotification
of the Android ACME app for a complete example.
import com.google.firebase.messaging.FirebaseMessagingService;
import com.google.firebase.messaging.RemoteMessage;
import com.nextauth.android.NextAuth;
import com.nextauth.android.PushMessage;
public class MyPushMessagingService extends FirebaseMessagingService {
@Override
public void onMessageReceived(RemoteMessage message) {
Map<String, String> data = message.getData();
// pass the encrypted push message to the Mobile SDK
PushMessage decryptedPushMessage = NextAuth.getNextAuth().processPushMessage(data);
// TODO implement notifications based on decryptedPushMessage
}
@Override
public void onNewToken(@NonNull String token) {
// update the FCM push token
NextAuth.getNextAuth().updatePushToken(token);
}
}
Tip
From nextAuth Android SDK 2.3.0 onwards, you can have more control over the processing of pushMessages. Where the deprecated method processPushMessage(data)
immediately starts a login flow in case of a LOGIN
push message, the new method decryptPushMessage(data)
does not. Instead you will need to call the processPushMessage()
method to start the login flow from the last received LOGIN
push message, if any. This way, you can start the login flow at the time that the user is present and has your app in the foreground. We recommend adding a call to the processPushMessage()
method:
- inside your code to handle notifications, after checking that your app is in the foreground; and
- at the app level when the app enters back into the foreground.
Optionally, create a receiver that extends BroadcastReceiver
to start up application directly after the device has booted.
public class BootReceiver extends BroadcastReceiver {
@Override
public void onReceive(Context context, Intent intent) {
if (Intent.ACTION_BOOT_COMPLETED.equals(intent.getAction())) {
NextAuth.getNextAuth().retrieveMessages();
}
}
}
Finally, add the following to your project's build.gradle
:
buildscript {
dependencies {
...
classpath 'com.google.gms:google-services:4.3.15'
}
}
to your app's build.gradle
:
dependencies {
...
// Play services
implementation 'com.google.android.gms:play-services-base:18.2.0'
// FCM
implementation 'com.google.firebase:firebase-messaging:23.1.2'
}
apply plugin: 'com.google.gms.google-services'
and AndroidManifest
:
<!--optional-->
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED" />
<application
...
<service
android:name="MyPushMessagingService"
android:exported="false"
android:stopWithTask="false">
<intent-filter>
<action android:name="com.google.firebase.MESSAGING_EVENT" />
</intent-filter>
</service>
<!--optional-->
<receiver
android:name="BootReceiver"
android:enabled="true"
android:exported="true"
android:permission="android.permission.RECEIVE_BOOT_COMPLETED">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED" />
<category android:name="android.intent.category.DEFAULT" />
</intent-filter>
</receiver>
</application>
iOS⚓︎
Notification Service Extension⚓︎
Apple enables updating a notification's content before it is displayed to the user even when the device is locked. To this end, you will need to create a new Notification Service app extension. Additionally, the extension and your app need to be members of an App Group so that they can share data.
First, navigation to Apple's Developer portal to create an App Group and an App ID for the app extension. The App Group's identifier will typically have the form group.com.nextauth.Authenticator. Once it has been created, don't forget to grant the app's Bundle ID the capability to access this group. Next, create a new App ID for the Notification Service extension. We recommend setting it to com.nextauth.Authenticator.NSExtension, but you are of course free to choose this value. Ensure that you also grant this ID the App Group entitlements with the relevant group.
Once you've completed these steps, open Xcode and choose File > New > Target
and choose Notification Service Extension in the dialog. Fill out the following screen as required by your setup, where we typically set the target's name to AuthenticatorNSExtension
. We will not list the required source files for the extension here, but instead refer to the Acme sample app for more details.
Handling Push Notifications⚓︎
In contrast to Android, iOS will invoke a couple of different methods of your app's AppDelegate
class when you receive push notifications. You should therefore ensure that it conforms to both UIApplicationDelegate
and UNUserNotificationCenterDelegate
. Furthermore, don't forget to request authorisation from the user to send push notifications (e.g., immediately when the app is started for the first time or during an onboarding flow).
Once a device push token has been requested from and granted by iOS, it will call application(_ :didRegisterForRemoteNotificationsWithDeviceToken:)
. The generated token should simply be passed to the nextAuth Mobile SDK, which will take care of registering it with the Message Center.
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
// MARK: - UIApplicationDelegate
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]? = nil) -> Bool {
UNUserNotificationCenter.current().delegate = self
UNUserNotificationCenter.current().getNotificationSettings() { settings in
guard settings.authorizationStatus == .authorized else {
return
}
DispatchQueue.main.async {
UIApplication.shared.registerForRemoteNotifications()
}
}
}
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
NextAuth.default.updatePushToken(deviceToken)
}
}
Finally, push notifications can be received by the app in a number of ways. In order to ensure proper handling of both silent and presented notifications when the app can be in the foreground or background, you should also add the following methods to your AppDelegate
. Each of them will process the received notification(s) and pass then to the Mobile SDK by calling the dedicated NextAuth.default.start(notification:)
method.
@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
// MARK: - UIApplicationDelegate
func applicationWillEnterForeground(_ application: UIApplication) {
UNUserNotificationCenter.current().getDeliveredNotifications { (notifications) in
for notification in notifications {
NextAuth.default.start(notification: notification)
UNUserNotificationCenter.current().removeDeliveredNotifications(withIdentifiers: [notification.request.identifier])
}
}
// MARK: - UNUserNotificationCenterDelegate
func userNotificationCenter(_ center: UNUserNotificationCenter, didReceive response: UNNotificationResponse, withCompletionHandler completionHandler: @escaping () -> Void) {
NextAuth.default.start(notification: response.notification)
completionHandler()
}
func userNotificationCenter(_ center: UNUserNotificationCenter, willPresent notification: UNNotification, withCompletionHandler completionHandler: @escaping (UNNotificationPresentationOptions) -> Void) {
NextAuth.default.start(notification: notification)
completionHandler([])
}
}
Message Retrieval⚓︎
In addition to pushing notification through FCM and APNs, the Message Center also supports message retrieval. We recommend regularly polling it to improve reliability or as a fallback if the user has not authorised push notifications. Due to the inherent limitations of iOS, this will only work for silent notifications when the application is in the foreground.
NextAuth.getNextAuth().retrieveMessages()
NextAuth.default.retrieveMessages()
Note that the retrieveMessages()
method on both platforms does not have any return values. If the Message Center returns any messages, they will be processed immediately by the Mobile SDK and the relevant callbacks will be issued. Furthermore, both push and pull mechanisms can be used simultaneously, as the Mobile SDK will deduplicate any incoming messages.