3

I have looked all over and can't figure out how to notify a user when a new device signs into their account. I'm using Firebase Authentication. I'd like to setup some kind of notification system or email that tells the user someone has signed into their account from a different device so they can know for security reasons.

Ideas?

Also, how could you monitor information about what device (s) are signed into a specific account? For example, maybe the user is curious how many devices he has signed into his account, what type of device they are, what the name of those devices are, and where they are located (example: San Antonio, Texas).

Ideas?

Christian W
  • 353
  • 2
  • 12
  • 1
    Trying to see what's possible, in my opinion: 1) You can check when a new signin is performed by reading a `Bool` value in `UserDefaults.standard`, setting it to `true` after a first signin; 2) You can trigger an automatic e-mail forwarding by reading it with `Auth.auth().currentUser?.email` and sending it to an online API tat you can build in PHP, for example; 3) I don't think you can get all that sensitive information - if ever, the user will have to provide his consent. – HunterLion Feb 25 '22 at 21:37
  • 1
    For a device type, You can go with [Device on CocoaPods](https://cocoapods.org/pods/Device). For the location, You have to ask for permissions as said already, and then You can do some reverse-geocoding, to get human readable address (eg, state, city...). To get this possible, You will have to use MapKit and CoreLocation frameworks. If You want to go even with ip address, its a bit unreliable, but check this [Rob Napier's comment](https://stackoverflow.com/a/27709110/3402095) – Whirlwind Feb 26 '22 at 11:25
  • 1
    Super simple; have a field in your user document that stores, maybe in an array, devices that are logged in and their status `device_id: 1234` and `is_logged_in: true` - and that node or document is being observered by the users devices. When a device logs in, update that field, which will notify the uses other devices. Since the devices are notified of changes, you can detect if its an existing or new device. Pretty straightforward to implement and easy to maintain. – Jay Feb 26 '22 at 14:51
  • @Jay That sounds like a great idea. Would you be willing to provide a more detailed example of how to do this? When you say "user document," are you referring to the document containing the user's information on Firebase? Forgive me, I'm still learning and am relatively self-taught. – Christian W Feb 28 '22 at 06:59
  • Are you using the Firebase Realtime Database or Firestore? Update the tags with your database of choice. – Jay Feb 28 '22 at 18:00
  • I'm using Firebase Firestore for this app – Christian W Feb 28 '22 at 18:21

2 Answers2

2

yes, you can use keychain for this situation. First you should create an uniqueId. After you can save to the db uniqueId. This value will change if the user uses another device. I'm using third party framework for Keychain services. You can use framework. It's perfect.

https://github.com/kishikawakatsumi/KeychainAccess

final class DeviceManager {
    
    static let shared = DeviceManager()
    
    private init() { }
    
    var uniqueDeviceId: String {
        get {
            let keychain = Keychain(service: KeyManager.keychainServiceName).accessibility(.always)
            if let deviceId = keychain[KeyManager.keychainDeviceIdKey] {
                return deviceId
            }
            
            let vendorId = UIDevice.current.identifierForVendor!.uuidString
            keychain[KeyManager.keychainDeviceIdKey] = vendorId
            
            return vendorId
        }
    }
}
Erkam KUCET
  • 485
  • 8
  • 21
  • Do you know any videos that show this being utilized? I'm having a hard time understanding it for some reason. Thanks for your patience. – Christian W Feb 28 '22 at 07:04
1

Super simple; have a field in your user document that stores a device name along with its status.

You app will be observing this users document and when something changes, all of the users devices will be notified of that change.

Let me set this up; here's a basic Firestore structure

users
   uid_0
      userName: "Jay"
      devices:
         device_0: offline
         device_1: offline

When the app starts, it will add an observer to this users document (using the uid as the documentId)

func observeUser() {
    let usersCollection = self.db.collection("users")
    usersCollection.document("uid_0").addSnapshotListener { (documentSnapshot, err) in

        guard let document = documentSnapshot else {
            print("Error fetching document: \(err!)")
            return
        }

        let device = document.get("devices") as! [String: String]
        print(device)
    }
}

Now in the Firestore closure shown above, if a users device changes status, offline to online for example, it outputs all of the devices to console. You would take whatever action is needed when the device changes status.

Keep in mind that if a NEW device is added, that event will also fire so you could present a message in the UI "A new device was added!"

So then some testing code that toggles the device_0 status from offline to online. I have a button click that does self.status = !self.status and then calls the toggleStatus function

var status = false
func toggleStatus() {
    var isOnline = ""
    if self.status == false {
        isOnline = "online"
    } else {
        isOnline = "offline"

    }
    let userCollection = self.db.collection("users")
    let thisDevice = "device_0"

    let devicesDict = [
        "devices":
            [thisDevice: isOnline] //sets device_0 to offline or online
    ]

    let document = usersCollection.document("uid_0").setData(devicesDict, merge: true)
}

In a nutshell, when a user authenticates with a device for the first time, it would perhaps ask for a device name, or craft one from the devices mac address or something under the hood. That device name is stored in the users document/devices with it's online status.

The device name would be stored locally as well, in user defaults for example so it's automatically sent up to Firestore upon login.

The end result here is that if any users devices change status; offline to online or vice versa, or any device is added or removed all of the devices are notified of that event.

Jay
  • 34,438
  • 18
  • 52
  • 81
  • This is an excellent implementation that should work well! Thank you! – Christian W Mar 01 '22 at 03:00
  • I'm getting the following error for the line... let device = document.get("devices") as! [String: String] -- ERROR: Could not cast value of type '__NSCFString' (0x23df4c5b0) to 'NSDictionary' – Christian W Apr 22 '22 at 03:44
  • @ChristianW You will need to have the identical structure to the one in the question where the key: value pairs within `devices` are `String: String` e.g. `device_0: offline`. Any other structure will make that not work, like `device_0: 0` - so ether adjust the structure or adjust the code to match your structure. – Jay Apr 22 '22 at 16:43