edgedb-js icon indicating copy to clipboard operation
edgedb-js copied to clipboard

ext::auth::ClientTokenIdentity has wrong cardinality type and type does not exist error

Open voxspox opened this issue 8 months ago • 4 comments

Code

The code causing the error.

const query = 
    e.insert(e.User, {
      name: "user",
      identity: e.ext.auth.ClientTokenIdentity,
    })

Schema

Your application schema.

using extension auth;

module default {
  type User {
    required name: str;
    identity: ext::auth::Identity {
      constraint exclusive;
    };
  }
}

Error or desired behavior

For identity in the e.insert() statement, Typescript complains about:

Type 'Cardinality.Many' is not assignable to type 'Cardinality.AtMostOne | Cardinality.One | Cardinality.Empty

Expected behavior: According to its definition, I think ClientTokenIdentity should have Cardinality.AtMostOne instead of Many:

<project>/dbschema/edgeql-js/modules/ext/auth.ts

const ClientTokenIdentity: $.$expr_PathNode<$.TypeSet<$ClientTokenIdentity, $.Cardinality.Many>, null> = _.syntax.$PathNode($.$toSet($ClientTokenIdentity, $.Cardinality.Many), null);

<gel-source>/edb/lib/ext/auth.edgeql

    create single global ext::auth::ClientTokenIdentity := (
        with
            ...
        select
            ext::auth::Identity
        filter
            .id = <uuid>json_get(jwt.claims, "sub")
    );

Versions:

voxspox avatar Apr 07 '25 10:04 voxspox

query.toEdgeQL() returns:

INSERT default::User {
  name := "user",
  identity := DETACHED ext::auth::ClientTokenIdentity
}

and running the query throws error:

object type or alias 'ext::auth::ClientTokenIdentity' does not exist
identity := DETACHED ext::auth::ClientTokenIdentity
                       ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Hint: did you mean 'ext::auth::ClientTokenIdentity'?

voxspox avatar Apr 07 '25 10:04 voxspox

I run the query after a successful OpenIDConnect login flow: A corresponding entry ext::auth::Identity is created.

voxspox avatar Apr 07 '25 10:04 voxspox

A few things are off here, including a misleading error message on our part that I'll work in getting fixed with the compiler team:

Unless you are setting the ext::auth::client_token global, you will not have an ext::auth::ClientTokenIdentity, and depending on how you're working with this code, you're probably wanting to set it like this:

const query = 
    e.insert(e.User, {
      name: "user",
      identity: e.select(e.ext.auth.Identity, (i) => ({
        filter_single: e.op(i.id, "=", identityId),
      })),
    });
// or better yet with UUID -> object casting which will throw an error if the Identity does not exists
const query = 
    e.insert(e.User, {
      name: "user",
      identity: e.cast(e.ext.auth.Identity, e.uuid(identityId)),
    });

Where the identityId came from the token data you got when creating the identity.


The actual path to the ClientTokenIdentity global is: e.ext.auth.global.ClientTokenIdentity. This explains the query error, too: you were trying to address it as an object type, but it's a global. Two bugs there:

  1. we shouldn't expose module globals on the module namespace, only on the module_name.global namespace
  2. we should give you a better error message that implies that the syntax error is that you are missing global. The query should read:
INSERT default::User {
  name := "user",
  identity := global ext::auth::ClientTokenIdentity
}

scotttrinh avatar Apr 07 '25 14:04 scotttrinh

Thanks for the fast reply! Yes, I confirm that using e.ext.auth.global.ClientTokenIdentity fixes the insert query.

For anyone who stumbles across this, here are the docs for globals.

voxspox avatar Apr 09 '25 13:04 voxspox