Sharing Anvil app user login with Expo app

Hello,

I’ve got an Anvil app with a customer user auth implementation (a modified version of what’s created in the tutorial here), and I’d like to create an Expo app that will use my Anvil app’s Users table for registration and login (so that any users are shared between apps).
I don’t have much experience with React Native or Expo development, but my initial thought is to use Anvil’s Uplink feature to allow communication between the Expo app and my Users table.

Just curious whether anyone here has implemented something similar, like sharing an Anvil Users table between apps and platforms, and performing registration and authentication on it.

Thanks

Anvil comes with the user registration and authentication management, and I have dozen of Anvil apps in my company that share the authentication. You login in one and you are logged in on all of them.

I also have a few old (pre-Anvil) tools built on Cherrypy and Flask, only accessible from our internal network. I recently needed to add authentication to some of them, so I added something like this:

  1. if the cookie anvil-user does not exist, redirect to the Anvil app at flaskapp-authentication.mydomain.com, including the redirect URL
  2. the Anvil app manages the authentication (registration, login, password change, etc.) just like it does for the other apps (I have a custom component that does that for every app)
  3. when the user has successfully signed in, the Anvil app redirects to flaskapp.mydomain.com/userlogin, including the redirect URL received and some info about the user
  4. the userlogin page creates a cookie and redirects to the redirect URL

At this point the flask app has a cookie that identifies the user.

This is NOT safe!
I did this because the old tools are only accessible from the office or VPN.

If your non-Anvil app lives in a public URL, then you could do something similar:

  1. same as above, but sending also a unique id
  2. same as above
  3. when the user has successfully signed in, the Anvil app first calls the http endpoint flaskapp.mydomain.com/userlogin, including the unique id received and some info about the user, then redirects to the URL received
  4. the userlogin endpoint first validates the unique id, then stores the user info together with the unique id in a table
  5. the redirect URL uses the unique id to recover user info and, maybe, stores it in a cookie

I don’t know if your Expo app allows you to define http endpoints and store info in a table. If it does, this could be a starting point.

1 Like

You’ll have to create API endpoints to log people into an external app that doesn’t use Anvil.

One way to authenticate users is with a JSON web token (JWT) ( jwt · PyPI). These are time-limited authentication tokens. I’m not sure how it works on client apps or Expo, but on the Anvil side, you’d do the following:

  1. API endpoint that extracts a username and password from the payload
  2. Check that the password matches the one in the database
  3. If it matches, send a JWT back as a response

For logging in with Google, things are more complicated as you have to pass a google id token to the endpoint and go through an oauth2 process with some saved client ids.

With the amount of things that can go wrong, I would not even attempt this unless I was really confident about how secure authentication works, or I was against using third party auth providers like Auth0.

I never thought about using uplink to do this - you’d have to have a python script running on the client (I think?) and that’s a non-starter for native apps.

OK. I’ll bite…

If you don’t have much experience with React or Expo, why would you try to use it rather than just building another anvil app?

1 Like

Thank you for the replies.

@owen.campbell The reason for this is two-fold, but related to the fact that I need access to the native device hardware:

  1. I need to access the device camera, to take a picture of a QR code when the user presses a button.
  2. I need better push notification integration with the app, for example to send notifications only to X user when Y action is taken. I am using OneSignal to manage client engagement, and how I integrate this with my app is as follows:
    a) I wrap the Anvil PWA as a native app with PWABuilder, exporting the source code for both iOS and Android app.
    b) I integrate the OneSignal API with each “native” app according to the OneSignal guide, in XCode and Android Studio.
    c) I submit each “native” app the App Store.

The Android app is by default a TWA, which allows much tighter integration of push notifications between a PWA and the native app - for example, I could integrate web push into my Anvil app, and that would trigger a “native” ‘Grant permissions for this app to send notifications?’ popup in the native app, allowing me to send push notifications to the device.

The iOS side is more difficult, because in order to send true push notifications (that will work even when the app isn’t running, for example), you need to have an iOS developer account, create a key and certificate signed by you for the app, and this is not connected with the browser’s web push. (By default, a PWA wrapped as an Android app will run as a Trusted Web Activity, with more privileges than if it is a WebView. But iOS does not have something like a Trusted Web Activity, all PWAs wrapped as a native app would run in a WebView, and it is much more locked down than a TWA on Android - anyone with more knowledge than me is welcome to correct any inaccuracies I’ve stated here).

When I integrate OneSignal API with my wrapped “native” app, I am unable to engage with the user’s actions inside my Anvil-app-running-as-a-TWA/Webview. All I can do is make the native device prompt pop up when the user opens the app for the first time “Do you want to allow this app to send notifications?”. After that, if the user says “Yes”, I can send true native push notifications to that device from the OneSignal console, however they are very limited - I can only send a push notification with a message and image, and when the user clicks on it, the app opens. I cannot tell my Anvil PWA, “Hey, the user just clicked the push notification, so open up Form X, or display some text now.”… there is zero communication between the Anvil app and OneSignal. There are a lot of features in OneSignal that are/would be very useful to increase user engagement, like “in-app messages”, which are popups in the app that display some additional information, or suggest an action, like “Rate us in the app store” and then supply a Call To Action button that would open up the app store on that device, if available.

The other big problem is that I have no way of knowing which user of my app is which user in the OneSignal console. The OneSignal console shows me a list of all the devices/users that have opted in to receive push notifications, so I could send a mass push notification to everybody, “Hey, there’s a sale on, click here”, but I don’t know what registered user that is in my Anvil app (if it is a registered user) and thus I can’t target those devices appropriately.

So there’s a tremendous amount of opportunity here for increased user engagement, and there is a OneSignal SDK python library available in Anvil, but from what I can see it is only server-side calls, like programmatically sending push notifications to your users, which can already easily be done through the console. What we really need, I think, is a client-side OneSignal Python SDK, like the ones available for Android and iOS, that can communicate with the OneSignal service and respond to user actions and push notifications, do things like display their “in-app messages”, and be able to match the users in the Anvil PWA to the users in the OneSignal console. Then we’d get a really great native push notification integration with our Anvil apps and users who have installed wrapped PWAs on their devices.

Sorry for any inaccuracies in my description, this is the best of my understanding so far having worked with PWABuilder-wrapped Anvil apps and OneSignal for native push notification.

You could use the onesignal web sdk

That is a good point - this should work, however after integrating it with Anvil like the other users have done in this thread, I also get the same errors:

The FetchEvent for "https://cdn.onesignal.com/sdks/web/v16/OneSignalSDK.page.js" resulted in a network error response: the promise was rejected.Understand this warning
OneSignalSDK.page.js:1 
        
        
       Failed to load resource: net::ERR_FAILED
service-worker:1 
        
        
       Uncaught (in promise) TypeError: Failed to fetch
    at service-worker:1:48657
    at Generator.next (<anonymous>)
    at r (service-worker:1:47298)
    at a (service-worker:1:47501)
    at service-worker:1:47560
    at new Promise (<anonymous>)
    at service-worker:1:47441
    at h (service-worker:1:48325)
    at service-worker:1:49728
    at Generator.next (<anonymous>)

It appears the problem is due to there being multiple service workers. Since Anvil creates a PWA, it generates a service worker as well, however that conflicts with OneSignal’s. See here

Does anyone know how to modify Anvil’s service worker in order to merge these?