Hey @Konarium!
Yeah, I’ve solved the issue and after consultation with members of the Core Team, it is up to now the only possible solution.
Login
First things first. I’m not sure how you access your namespaces yet since only Tenant-0
is publicly exposed and thus reachable directly without submitting an X-Dgraph-AccessToken
. Since the only way of receiving the token for a given namespace is querying against the /admin
endpoint, hence requireing an admin key which I do not want to expose in my application bundle, I wrote a custom query to fetch tokens via Tenant-0
.
This is my schema on Tenant-0
:
type Tokens @generate(
query: {get: false, query: false, aggregate: false}
mutation: {add: false, update: false, delete: false}
subscription: false
){
accessJWT: String
refreshJWT: String
}
type LoginPayload @generate(
query: {get: false, query: false, aggregate: false}
mutation: {add: false, update: false, delete: false}
subscription: false
){
response: Tokens
}
type Mutation {
getTokenForNamespace(userId: String, password: String, namespace: Int, refreshToken: String): LoginPayload @custom(http: {
url: "https://old-meadow.eu-central-1.aws.cloud.dgraph.io/admin",
method: POST,
secretHeaders: ["DG-Auth:AdminKey"],
introspectionHeaders:["DG-Auth:AdminKey"],
graphql: "mutation($userId: String, $password: String, $namespace: Int, $refreshToken: String) { login(userId: $userId, password: $password, namespace: $namespace, refreshToken: $refreshToken) }"
})
}
# Dgraph.Secret AdminKey "YOUR-ADMIN-KEY-HERE"
DQL Mutations/Queries from Custom Lambda Resolver
As I have stated in my previous post, the Dgraph-Lambda package is indeed implemented in the cloud. Looking at the source reveals that there is no token forwarding for DQL operations and for GraphQL operations only one header will be submitted. I have not tested this, but I guess your GraphQL operations only work because you have no @auth
rules on the types which are subject to your queries. Under my understanding also GraphQL operations would fail if you require both tokens, the
X-Dgraph-AccessToken
→ to query against the right namespace, and the
X-Auth-Token
→ which includes the user claim for your @auth
rules
However, since all methods which are accessible as arguments from inside a custom lambda resolver, are simple wrappers for a fetch
query, I wrote my own wrapper with an additional login step to fetch an X-Dgraph-AccessToken
inside the resolver - I know! Sounds weird to login twice but this is how it is at the moment The only possible argument here is that:
You have to require an X-Dgrap-AccessToken
from inside the lambda resolver again so you have full control over what the lambda is allowed to do via ACL.
I have simply created a lambda user with special read/write permissions valid for my custom lambda operations. This user is the one I’m logging in with from inside the custom resolver.
Request access token
export const getAccessToken: GetAccessToken = async params => {
const { userId, password, namespace, refreshToken } = params;
if (!refreshToken && !(userId && password && namespace)) {
throw new Error("Not all paramteters for logging in to a namespace are provided. Either submit a refresh token or userId, password and namespace.");
}
const res = await fetch(`YOUR_CLUSTER_ENDPOINT/graphql`, {
headers: {
"Content-Type": "application/json",
},
method: "POST",
body: JSON.stringify({
query: `mutation GetToken($userId: String, $password: String, $namespace: Int, $refreshToken: String) {
getTokenForNamespace(userId: $userId, password: $password, namespace: $namespace, refreshToken: $refreshToken) {
response {
accessJWT
refreshJWT
}
}
}`,
variables: {
userId: userId,
password: password,
namespace: namespace,
refreshToken: refreshToken,
},
}),
});
const result = (await res.json()) as GetAccessTokenResult;
if (result.errors) {
throw new Error(result.errors[0].message);
}
return result.data.getTokenForNamespace.response;
};
DQL Request Wrapper
export const dqlRequest = async <T extends DqlRequest<any>>(params: T["params"]): Promise<T["result"] | never> => {
const { type, query, errorPos, commitNow, secretHeaders, user } = params;
// Get the lambda access token for the current namespace
const accessToken = await getAccessToken(user);
// Set the content header type according to submitted query data
const contentType =
typeof query === "string" ? (type === "mutate" ? "application/rdf" : "application/dql") : "application/json";
// Set the request url
const requestUrl = `YOUR_CLUSTER_ENDPOINT/${type}${commitNow ? "?commitNow=true" : ""}`;
// Perform fetch request
const request = await fetch(requestUrl, {
method: "POST",
headers: {
"Content-Type": contentType,
"X-Dgraph-AccessToken": accessToken.accessJWT,
...secretHeaders,
},
body: JSON.stringify(query),
});
const result = (await request.json()) as DqlRequest<any>["result"];
if (result.errors) {
throw new Error(result.errors[0].message);
}
return result.data;
};
The Secret Headers object MUST CONTAIN the DG-Auth
header value! In my case I have set this to the Client Key
!
Hope this helps!