Firebase Authentication - Retrieve Custom Claims key for iOS - ios

A custom claim luid has been added to the Firebase Authentication from the backend, I'm looking for a way to access this key from the front end for an iOS application.
First I need to check if the key exists and if it exists then get its value.
What have I tried? Everything under Auth.auth().currentUser
Attaching a picture of the decoded JWT data, which shows the key luid

You can check the custom claims this way:
user.getIDTokenResult(completion: { (result, error) in
guard let luid = result?.claims?["luid"] as? NSNumber else {
// luid absent
return
}
//Check for value here and use if-else
})
Detailed explanation can be found in documentation

This will display a Dictionary with all the keys available for the current user.
Auth.auth().currentUser?.getIDTokenResult(completion: { (authresult, error) in
print("CurrentUser Keys", authresult!.claims.keys)
})
This will return Bool value based on a particular key's availability
Auth.auth().currentUser?.getIDTokenResult(completion: { (authresult, error) in
print("CurrentUser Keys", authresult!.claims.keys.contains("luid"))
})

Related

How to get user specific data from firebase in iOS app?

Firstly I've reviewed Firebase documentation, they only have two separate explanations on login user and CRUD operations but not really how both they can be connected with security rules. Despite that I've worked on it here is the security rules. In iOS code I fetch the list and also add the item with user id
rules_version = '2';
service cloud.firestore {
match /databases/{database}/documents {
match /contacts/{creatorsID} {
allow read, write: if request.auth != null && request.auth.uid == creatorsID;
}
}
}
ERROR: Write at contacts/wv6fMMhBpw3ROEOyUMBe failed: Missing or insufficient permissions
{ creatorsID: "", name: "" }
func fetchData() {
db.collection("contacts").addSnapshotListener { (querySnapshot, error) in
guard let documents = querySnapshot?.documents else {
print("No documents")
return
}
self.contacts = documents.map { (queryDocumentSnapshot) -> Contact in
let data = queryDocumentSnapshot.data()
let name = data["name"] as? String ?? ""
let userID = data["creatorsID"] as? String ?? ""
return Contact(name: name)
}
}
}
func addData(name: String) {
do {
_ = try db.collection("contacts").addDocument(data: ["name": name, "creatorsID": Auth.auth().currentUser?.uid])
}
catch {
print(error.localizedDescription)
}
}
The problem here is that I still receive all the items from all users instead of specific user's list(FYI, CreatorsId is as same as UID). I can't figure out if the issue is in rules or in iOS code.
You're getting all users because you read the data with:
db.collection("contacts").addSnapshotListener...
So this gives you all documents from the contacts collection. If you want to only read a single document, you'll have to identify that document either by its ID or by using a query.
Since you use addDocument to add the document, Firestore generates a unique ID for you. That means that with your current data structure, the way to get the user document is by querying on the createdID field:
db.collection("contacts")
.whereField("createdID", isEqualTo: Auth.auth().currentUser?.uid)
.addSnapshotListener { (querySnapshot, error) in
...
A more idiomatic way to store user profile documents is to use the UID of each user as the ID of the document too. That both guarantees that each user can only have one document (as document IDs are by definition unique within a collection), and it means you can look up the profile document for a user without a query:
db.collection("contacts")
.document(Auth.auth().currentUser?.uid)
.addSnapshotListener { (documentSnapshot, error) in
....
Doing this will also solve your security errors, as this code matches exactly with what you're checking here:
match /contacts/{createdID} {
if request.auth != null && request.auth.uid == createdID
In your current approach the value of createdID is the random ID that Firestore generates when you call addDocument, and not the user's UID.

iOS/Swift/Firebase Authentication: Help Needed on Accessing "isNewUser"

In my iOS/Swift/Firebase app, I am trying to access the "isNewUser" parameter after a user successfully signs in via email/password so that I can pop a window to compel them to reset their password from the temporary one initially assigned upon user creation.
Any insights would be appreciated. Thanks.
The .isNewUser Bool is available from the FirebaseAuth AdditionalUserInfo class.
Here is the link. In order to utilize this code, please see a demo sign in function I wrote below.
Auth.auth().signIn(with: credential) { (result, error) in
if let error = error {
print("Error: \(error)");
return;
}
// Fetch the user's info
guard let uid = result?.user.uid else {return}
// Safely unwrap the boolean value rather than forcing it with "!" which could crash your app if a nil value is found
guard let newUserStatus = result?.additionalUserInfo?.isNewUser else {return}
// Test the value
print("\nIs new user? \(newUserStatus)\n")
if newUserStatus == true {
// Provide your alert prompt
}
else{
// Transition view to continue into the app
}
}

Update Parse ACL on queried object

In my iOS app I am making a PFObject and saving it to Parse. Later, a user's account is created (which didn't exist before), and tries to modify it but can't because the PFObjects's ACL wasn't set to allow that user to have permission. How can I modify the ACL of an existing object in Parse to allow this user to have access? I do not want to allow public write access.
The following code prints Success! if given the right code query parameter, but when I check the ACL in Parse it has not been updated at all.
let query = PFQuery(className: "Bike")
query.whereKey("bikeID", equalTo: code)
query.findObjectsInBackground { (objects: [PFObject]?, error: Error?) in
guard let obj = objects?[0], error == nil else {
print("Error")
return
}
obj.acl?.setWriteAccess(true, for: PFUser.current()!)
obj.saveInBackground { (success: Bool, error: Error?) in
if error != nil {
print(error!.localizedDescription)
}
else {
print("Success!")
}
}
}
This post seems to suggest that the ACL cannot be changed through my app's Swift code.
If you know the object you want to grant access to up-front you can change it's ACL in the CloudCode afterSave hook of the User class: in afterSave, test whether the user was just created (to avoid redoing this work for subsequent save requests), then look up the object and set the access rights using the master key.

Accessing Specific key on Firebase (Swift)

I am trying to update child values within firebase.
User first will create a new order, it creates two nodes, one in the main orders section and second under user to keep clean records. this seem to have worked but I am struggling to update values
then while he is on the form and makes updates, I want firebase to update simultaneously on firebase. How do I access to that specific key as nothing seem to have worked for me when I tried using observe method.
What will be the best way to access that key that the form is on and update values?
This is how you can update values in Firebase:
func updateDatabaseForEdits() {
let updates : [AnyHashable: Any] = ["variableName": value,
"variableName2": value]
ref.child("COrders").child("specificKeyYouWantToEdit").updateChildValues(updates, withCompletionBlock: { (error, success) in
if error != nil {
// upload failed
return
}
else {
// upload worked
// update your locally stored values
}
})
}
There are other issues with you app though. Specifically, how you're storing the data. How do you expect to know which key to access? I recommend you update your data store to be something like this:
desikhanapeena {
COrder {
key123 {
orderInformation
}
}
UserOrders {
uid {
key123
orderInformation
}
}
}
Let me know if you have questions:
If you want to get the key from a snapshot you can do that like this:
for child in snap.children {
let child = child as? DataSnapshot
if let key = child?.key {
// print(key)
}
}
If you want to get the key before you upload you can do that like this:
let key = ref.child("COrders").childByAutoId().key
in case if you are still looking for answer.
Answer for Prob #1.
you need to update order value for user, if it successfull then take parent key (in your case , its AutoID) from there , and using this key, update in "Corders".
here is the code
func updateBothValue(){
let value = ["key":"data","key1": "data2"]
REF.child("Users").child(UID).child("UserCorder").childByAutoId().setValue(value){
error, ref in
guard error == nil else { return }
let key = ref.key
REF.child("Coorders").child(key).setvalue(value)
}
}
for Prob #2, as you written "access that key that the form is on". for each form data , store the key which you get from firebase in previous steps. and use this key for updates.

Get providerData from Firebase User

I'm trying to add the unlink provider function in my app. I want to add a button that unlinks a user's Facebook account from their Firebase user account. The Firebase documentation says to use the providerData array, but I don't know how to extract the Facebook provider ID from it. If I just use let providerID = self.currentUser.providerID, it just defaults to the first provider in the array, regardless of what the type is (Facebook, Twitter, Firebase, etc..).
How can I extract the Facebook provider ID from providerData? This is how I tried to get it but it errors ("cannot subscript a value of type [String] with an index type of String"):
guard let providerData = self.currentUser?.providerData else {return}
var providerArray: [String] = []
for provider in providerData{
providerArray.append(provider as! String)
}
var providerID: String = providerArray["Facebook"]
FIRAuth.auth()?.currentUser?.unlink(fromProvider: providerID) { (user, error) in
EDIT:
Fixed by using
FIRAuth.auth()?.currentUser?.unlink(fromProvider: "facebook.com" ) { (user, error) in
You are trying to access providerArray like a Dictionary and that will not work. What you are looking for is the providerId specifically for Facebook. In order to retrieve this, you need to get the FIRAuthCredential for Facebook. In order to do this you should use FIRFacebookAuthProvider. This will get you the credential, from the credential you can grab the providerId, match it to the current user's array of providerIds like you are doing. Then unlink as you are attempting to do.
I fixed this by only showing the unlink button if a Facebook auth token is present. Then, when the button is pressed, just pass in "facebook.com" as the providerID:
FIRAuth.auth()?.currentUser?.unlink(fromProvider: "facebook.com" ) { (user, error) in

Resources