Postgraphile: Use Passport instead of JWT

Created on 22 Aug 2017  路  29Comments  路  Source: graphile/postgraphile

Hey @benjie,

I was wondering if there was a possibility of using passport instead of jwt for postgraphql?

http://cryto.net/~joepie91/blog/2016/06/13/stop-using-jwt-for-sessions/

I saw that there's a JWTPlugin here: https://github.com/graphile/graphile-build/blob/master/packages/graphile-build-pg/src/plugins/PgJWTPlugin.js

But when reading it, at first glance is pretty cryptic. Would love to get your feedback on how to move forward.

Thanks!

Most helpful comment

  1. Provide role via pgSettings callback
  2. Do NOT provide pgDefaultRole
  3. Do NOT provide jwtSecret
  4. ...
  5. Profit

All 29 comments

You can just use pgSettings in the library version to get values from the request, e.g.

pgSettings: (req) => ({
  'jwt.claims.user_id': req.user ? req.user.id : undefined,
})

so in this scenario, you want to set jwt.claims.user_id, even if we're getting that information from a session, for example,

pgSettings: (req) => ({
  'jwt.claims.user_id': req.session ? req.session.user_id : undefined,
})

In this case, jwt.claims.user_id is just a key with a value of user id, but we wouldn't actually use jwt

Yes, though you can call it whatever you want:

pgSettings: (req) => ({
  'user.is_admin': req.session && req.session.user_is_admin ? "YES" : "NO",
})

so long as you match it with the relevant SQL:

CREATE FUNCTION current_user_is_admin() RETURNS boolean AS $$
  SELECT current_setting('user.is_admin', true) = 'YES';
$$ LANGUAGE sql STABLE;

ok that's perfect! So current_setting() will basically get whatever info you want, only during that request, then it is reset.

Correct 馃憤

(It must have at least one dot . in, otherwise it tries to override core postgres settings.)

and then just ignore jwtPgTypeIdentifier, jwtSecret when initializing? Or should these mechanisms be reimplemented?

Simply don't specify them - they're optional.

@benjie which version of postgraphql does pgSettings take a function? I'm currently using 3.4.0 but it doesn't seem to be getting called on a request or is it only on a graphql request? either way I'm not seeing a console log show up in graphiql from a graphql request or a page refresh from the express server.

pgSettings: req => {
            console.log(req);
            return {};
          }

I've just published v3.5.0 which includes this functionality. I'd encourage you to use postgraphql@next instead though!

@benjie cool, its all working now, just 1 note.

I had to write my code like this

          pgSettings: req => ({
            'user.user_id': req.ctx.session.user_id || ''
          })

because for some reason postgraph was turning both undefined and null into string representations of themselves. E.g. "undefined", "null" this to me seems like unexpected behavior.

馃憤 Please file it as a bug.

@benjie how do you specify the proper role once a user is logged in?

          pgSettings: req => ({
            'jwt.claims.user_id': req.ctx.session.user_id || '',
            ROLE: req.ctx.session.user_id ? 'known_user' : 'anonymous_user'
          }),

if this shouldn't be a concern of pgSettings, then there is a bug because ROLE is set to anonymous_user.

role is overridden after pgSettings is applied:

https://github.com/postgraphql/postgraphql/blob/85e630a9de530b5f141241ab62d2b6974cc6fbd4/src/postgraphql/withPostGraphQLContext.ts#L164-L174

But only if pgDefaultRole is set:

https://github.com/postgraphql/postgraphql/blob/85e630a9de530b5f141241ab62d2b6974cc6fbd4/src/postgraphql/withPostGraphQLContext.ts#L114

or there's a role claim in the JWT:

https://github.com/postgraphql/postgraphql/blob/85e630a9de530b5f141241ab62d2b6974cc6fbd4/src/postgraphql/withPostGraphQLContext.ts#L132-L140


Also, because settings go via set_config(...) they should be specified in lower case - PostgreSQL's case-insensitivity is exposed by lower-casing everything unless it is quoted.

I'd love a PR that throws an error if you set a config variable that contains only upper-case letters and underscores. 馃檹

@benjie I just tried to use the following:

          pgSettings: req => ({
            'jwt.claims.user_id': req.ctx.session.user_id || '',
            role: req.ctx.session.user_id ? 'known_user' : 'anonymous_user'
          }),

but still doesn't work. Any ideas?

FWIW, I also tried jwt.claims.role and also doesn't work

Ensure pgDefaultRole setting is not set.

Ensure jwtSecret is not set.

Add another configuration variable and ensure that is coming through.

If these don't solve it, then I'm going to need more context - e.g. exactly how you're calling it, which version of PostGraphQL you're using, how you are confirming that the role is not being set, etc.

Also be aware that currently this function does NOT change the pgRole on the GraphQL context, but as far as I know this is only used for one debugging message.

that helped to get the query through!

I dug into it more, and since we are using the graphiql tool, requests are not getting the bearer set in the authorization headers. @lifeiscontent

Also be aware that currently this function does NOT change the pgRole on the GraphQL context, but as far as I know this is only used for one debugging message.

so for clarification, this doesn't actually change roles in postgres?

so then if I understand it, effectively, we are logging in with the credentials built in the postgres://user:pass@host:port/db, so what's the purpose of the role in jwt?

No, that was not my meaning. The GraphQL context is the 4th argument to the graphql(...) function; we set it here:

https://github.com/postgraphql/postgraphql/blob/779a643898418bc732473025182710f2d5b843ef/src/postgraphql/withPostGraphQLContext.ts#L74-L77

What I'm saying is the pgRole there is stale (it does not show updates from pgSettings) however I think that is only used in one place:

https://github.com/postgraphql/postgraphql/blob/85e630a9de530b5f141241ab62d2b6974cc6fbd4/src/postgraphql/http/createPostGraphQLHttpRequestHandler.js#L447-L448

I.e. only for a debug message.

For the avoidance of doubt: the role in pgSettings / JWT does change the role setting in the PostgreSQL transaction.

For the avoidance of doubt: the role in pgSettings / JWT does change the role setting in the PostgreSQL transaction

In that case How can we change role setting in the PostgreSQL transaction, If we are using passportjs or facebook login on our site

  1. Provide role via pgSettings callback
  2. Do NOT provide pgDefaultRole
  3. Do NOT provide jwtSecret
  4. ...
  5. Profit

We're currently using a hand-rolled JWT library and are setting a private key with jwtSecret, but I'm not sure what/how to set jwtPgTypeIdentifier. The docs seem to indicate using the form "my.schema".[jwtType], but I do not know what this composite type is supposed to be, or how to determine it.

This is using postgraphql@next, and it looks like the function receiving the value is using pgCatalog which isn't currently implemented in v4. Is this something we should hold off on?

JwtPgTypeIdentifier is the fully qualified name of a type; for example if you had "create type myschema.myjwttypename as (name text, user_id int)" the type identifier would be "myschema.myjwttypename". This works in v4 as it did in v3 as far as I'm aware.

Ah. It looks like this type isn't being defined anywhere in our schema. Thanks!

I've extended the documentation on this generating JWTs, perhaps it helps:

https://www.graphile.org/postgraphile/security/#generating-jwts

(Code is untested)

I'll test it out and let you know, looks great though!! 馃檹

Should this be closed @benjie ? It is now documented.

Was this page helpful?
0 / 5 - 0 ratings

Related issues

tazsingh picture tazsingh  路  3Comments

ssomnoremac picture ssomnoremac  路  5Comments

safaiyeh picture safaiyeh  路  3Comments

kilianc picture kilianc  路  4Comments

angelosarto picture angelosarto  路  3Comments