My problem is that the “realm user id”, the one that RealmSDK gives me on mobile client is different from the one I generate in the custom function.
And I have set the permissions to
READ
(can only read PUBLIC and your own data such as partitions are "user=myId123" or "PUBLIC")
{
"$or": [
{
"%%partition": "user=%%user.id"
},
{
"%%partition": "PUBLIC"
}
]
}
WRITE
(can only write your own data such as partitions are "user=myId123")
{
"%%partition": "user=%%user.id"
}
How am I supposed to retrieve this realm user id so that I can set up correctly my custom login function and populate correctly my collection with the right _partition and _id?
EDIT: The error I get from Realm Sync Backend is
Error: user does not have permission to sync on partition (ProtocolErrorCode=206)
Partition: user=607ac1b7687c4dca433c3c5e
Because my custom function has set the custom user id to 607ac1b7687c4dca433c3c5b but the actual realm user id is 607ac1b7687c4dca433c3c5e and I have no clue how to fetch this id from the custom login function…
Step 1.
I set up my “Custom Function Authentication” - by the way I use GameCenter (an iOS game framework which gives me a unique user id).
Here is the code.
exports = async function(loginPayload) {
const users = context.services
.get("mongodb-atlas")
.db("app")
.collection("UserInfo");
let findQuery;
if (loginPayload["gameCenterId"] != null) {
findQuery = { gameCenterId: loginPayload["gameCenterId"] };
}
// Check if user already exists using our own GameCenter_id
const user = await users.findOne(findQuery);
if (user !== null) {
return user._id.toString();
} else {
// Set the partition to a temporary value.
// A onCreate trigger will replace it with user={InternalRealmId} which we still don't have here because of custom-function internal logic
const temporaryId = new BSON.ObjectId();
if (loginPayload["gameCenterId"] != null) {
const result = await users.insertOne({
_id: temporaryId,
_partition: "temporaryId=" + temporaryId.toString(),
creationDate: new Date(),
gameCenterId: loginPayload["gameCenterId"]
});
return result.insertedId.toString();
}
}
};
Step 2.
Then I create a trigger that will always run for every created user with the custom function login. This trigger’s goal is to inject and override the realm_id into the partition of the previous document created.
Here is its configuration:
Trigger type: Authentication
Enabled: True
Action type: Create
Provider: Custom Function Authentication
Select an event type: Function
Function: {my function called onUserCreated}
And here is my onUserCreated function
exports = async function(authEvent) {
const user = authEvent.user;
const realmUserId = user.id; // Now we have the actual realm_id
const identities = user.identities;
// Update our user's Mongo document's partition with the actual realm_id
const customIdentity = identities.find(identity => identity.provider_type === "custom-function");
if (customIdentity !== undefined) {
const userId = new BSON.ObjectId(customIdentity.id);
const collection = context.services.get("mongodb-atlas").db("app").collection("UserInfo");
const findUser = await collection.findOne({ _id: userId });
if (findUser !== null) {
await collection.updateOne({ "_id": userId }, { $set: {
_partition: "user=" + realmUserId.toString()
} });
return;
} else {
return;
}
}
};
And voila… I only had a few problems (maybe once per month or so) with the shared cluster and according to Realm support, shared clusters can have very rare trigger failures apparently.