In an effort to understand Fauna better I’m reconstruction the wonderful Fauna Auth Skeleton by hand. I’ve this slightly modified login
function:
{
name: "login_new",
role: null,
body: Query(
Lambda(
["email", "password"],
Let(
{
maxFailedLogins: Call("config_var", {
path: ["rate_limiting", "failed_logins", "max"]
}),
rateLimitFailedLogins: Call("config_var", {
path: ["rate_limiting", "failed_logins", "enabled"]
}),
logLogins: Call("config_var", { path: ["logging", "login"] })
},
Do(
If(
Var("rateLimitFailedLogins"),
Call("call_limit", [
"failed_login",
Var("email"),
Var("maxFailedLogins")
]),
false
),
Let(
{
loginResult: If(
Exists(Match(Index("accounts_by_email"), Var("email"))),
If(
Identify(
Select(
["ref"],
Get(Match(Index("accounts_by_email"), Var("email")))
),
Var("password")
),
Let(
{
account: Get(
Match(Index("accounts_by_email"), Var("email"))
),
accountRef: Select(["ref"], Var("account")),
tokens: Let(
{
refresh: Create(Tokens(), {
instance: Var("accountRef"),
data: {
type: "refresh",
used: false,
sessionId: If(
And(
HasCurrentToken(),
Equals(
Select(
["data", "type"],
Get(CurrentToken()),
false
),
"refresh"
)
),
Select(
["data", "sessionId"],
Get(CurrentToken())
),
NewId()
),
validUntil: TimeAdd(
Now(),
Call("config_var", {
path: [
"session",
"refresh_tokens",
"lifetime_seconds"
]
}),
"seconds"
),
loggedOut: false
},
ttl: TimeAdd(
Now(),
Call("config_var", {
path: [
"session",
"refresh_tokens",
"reclaimtime_seconds"
]
}),
"seconds"
)
}),
access: Create(Tokens(), {
instance: Var("accountRef"),
data: {
type: "access",
refresh: Select(["ref"], Var("refresh"))
},
ttl: TimeAdd(
Now(),
Call("config_var", {
path: [
"session",
"access_tokens",
"lifetime_seconds"
]
}),
"seconds"
)
})
},
{ refresh: Var("refresh"), access: Var("access") }
)
},
{
tokens: Var("tokens"),
account: Var("account"),
accessTokenLifetimeSeconds: Call("config_var", {
path: ["session", "access_tokens", "lifetime_seconds"]
})
}
),
{ errors: ["Wrong password."] }
),
{ errors: ["No account or incorrect email/password."] }
)
},
If(
Equals(Var("loginResult"), false),
Do(
If(
Var("logLogins"),
Call("log", ["auth", "login", Var("email")]),
false
),
false
),
Do(
If(
Var("logLogins"),
Call("log", ["auth", "login", Var("email")]),
false
),
If(
Var("rateLimitFailedLogins"),
Call("reset_access_logs", ["failed_login", Var("email")]),
false
),
Var("loginResult")
)
)
)
)
)
)
)
}
To which I assigned a role with the following permissions:
When I try to invoke it from the backend it yells at me:
{
"errors":
[
{
"position":[],
"code":"call error",
"description":"Calling the function resulted in an error.",
"cause":
[
{
"position":
[
"expr","in","do",1,"let","loginResult","then","then","let","tokens","let","refresh","params","object","data","object","sessionId","if","and",1,"equals",0,"from"
],
"code":"permission denied",
"description":"Insufficient privileges to perform the action."
}
]
}
]
}
The documentation states that:
User-defined functions (UDFs) can be created to have a role, such as
role: "admin"
. This allows UDFs to execute with specific privileges, which might be higher than those of the caller.
And when I assign the Admin
role everything works just fine. But I can’t figure out which exact permissions I need here without the elevated access that Admin
role grants to the function.
I would greatly appreciate couple of pointers on how to better understand the permission error messages and some debugging tips!