web-ridge / gqlgen-sqlboiler

This is a plugin for gqlgen to generate converts + filter queries and resolvers for sqlboiler
MIT License
74 stars 13 forks source link

[Question] Supporting external record identifiers as strings that look up non primary key #54

Open orible opened 3 years ago

orible commented 3 years ago

I'll try to explain myself fully.

In my current code, I do the following when sending data to the client;

Here is the database model image

Here is the external model being constructed from the database model image

I use the unique UUID as the identifier when referring to it on the end client.

image

Example uID: '90c1939d-3414-11eb-8ffe-0242ac120005'

orible commented 3 years ago

So I suppose my question is;

  1. Am I able to change the schema and resolver to use the UID data as input
  2. Then use it as the first select argument to look up the record row.
orible commented 3 years ago

An example of how I do it manually.

image

Notice the use of ThreadWhere.UID rather than ThreadWhere.ID

RichardLindhout commented 3 years ago

Not sure. We construct the id based on the type of the model + the id of the model. e.g.

User_1

or User_2838932983298

this is to support the Node interface and Global Object Identification https://graphql.org/learn/global-object-identification/

RichardLindhout commented 3 years ago

So if I understand correctly you use internal id's and external ids?

orible commented 3 years ago

Yes, mainly for security measures and the aspect of ids unique to the whole database

When making a request, the client sends back the UUID in some valid way instead of a row number

For example

GET api/v1/thread?threadId=90c1939d-3414-11eb-8ffe-0242ac120005

The record is then fetched from the thread table by this unique id, and the model ID, which is the table primary key, is then used by sqlboiler as usual for joins and such.

RichardLindhout commented 3 years ago

Ah I see, we choose to encode the model because of the Node resolver so we know which table to fetch based on the id.

I think you could use the override converts for most of these use-cases and convert the graphql id to your internal id https://github.com/web-ridge/gqlgen-sqlboiler#overriding-converts

RichardLindhout commented 3 years ago

If you still have the chance I would choose between 1 id a uuid or auto increment id since gqlgen-sqlboiler does handle the unique ID across the schema . If you take security into consideration I don't know if it really would be more secure to not make your internal id's publicly https://en.wikipedia.org/wiki/Security_through_obscurity. I would recommend to put the user_id / tenant_id in your query to secure your data.

I think you will have most of the advantages if you use UUID for your id's everywhere since they are harder to guess than auto increment, so you'll still handle the 'Security through obscurity' and it's harder for users to scrape public data.

This is also interesting to read on this subject: https://softwareengineering.stackexchange.com/a/139453

We force the authentication if configured on all where's so if the user is not the owner of the object it won't do anything

dm.CommentWhere.UserID.EQ(auth.UserIDFromContext(ctx)),

https://github.com/web-ridge/gqlgen-sqlboiler-examples/blob/main/social-network/resolver.go#L71

See here more how to configure the AuthorizationScopes https://github.com/web-ridge/gqlgen-sqlboiler-examples/blob/main/social-network/convert_plugin.go#L66

orible commented 3 years ago

Aye, I do agree with the question around if it's really practical to obscure the row Ids

In terms of authentication, I'm loading a user context through information provided from access tokens, then passing the user object through the call context, and of course then asserting the user can modify the record in question in the biz logic packages.

orible commented 3 years ago

As you note, the biggest reason I chose to use external ids is to prevent users from inferring the number of posts, number of users, or other such records. And of course, generally make it slightly harder to attack the api when object identifiers are opaque.