Closed FGRibreau closed 7 years ago
We could just remove the sub
requirement, or make it optional with a command line flag. I was thinking about this use case when I made that requirement in effect to defer the decision :blush:
In my experience of integrating Firebase auth with another authentication system, I would always have two tokens. One for Firebase, and one for the app’s auth system. However I’m sure we can come to a more efficient pattern here.
Do you know if Auth0 will work out of the box with PostGraphQL if this requirement is removed?
@calebmer it should work yes, because it's the last thing that is blocking me from using postgraphql on my project, everything else works well on postgrest (I will just have to change my stored function to handle both way to access to .sub
(postgrest use postgrest.claims.sub
while postgraphql is jwt.claims.sub
) and thus give an REST api and a GraphQL api to my project which would be purely awesome)
@calebmer Going to be implementing Auth0 + Postgraphql + Express very soon. I will be doing authentication as middleware and just passing the token to my Postgraphql endpoint. Would really appreciate the sub requirement to be removed 👍
I think we just need to make the { audience: 'postgraphql' }
optional in https://github.com/calebmer/postgraphql/blob/e72696de3d67f5d478c009f8be4c9b25fdb1e2ed/src/postgraphql/http/setupRequestPgClientTransaction.js#L39
As long the secret is verified, JWT should be pretty safe. May I ask, why the audience is being verified? Also, Auth0 uses your unique clientID as the aud
. According to some JWT docs, the aud
should identify the Resource Server, which technically is Postgraphql, but it seems that Auth0 would like to assign it's own unique id for the client.
I have been playing around with this as well and I have some observations/ideas. I am not quite seeing the same issue with sub, rather I saw it with aud(ience). I was able to work around this in auth0.
Getting the access token (this may be different depending on how you want to support auth, but if you are doing a Proof of concept using a username password database in auth 0 like I am this is how I set it up )
Set the Token Endpoint Authentication Method to None on your client
In account settings under API Authorization Settings set the Default Directory to the name of the password database.
you can now post to the endpoint https://{{auth0_domain}}/oauth/token with parameters:
The response from this post if successful will contain an access_token field.
I can probably find time to do a PR for any of these, but would appreciate your thought on if they are valid before I do the work (they aren't huge but I have to make sure I read up on your guidelines and do the tests and commits right).
Add a new parameter for JWT options and let the user pass in the json config object directly to jsonwebtoken's verify. The default could be { audience: 'postgraphql' } if you are concerned about backwards compatibility. This allows the user to set additional restrictions if needed without postgraphql trying to figure out what options are needed.
Make it an error to specify --secret without --default-role. I can't really see a reason why this should work. Its like the user is requiring auth but not setting a token gives then unlimited access? (This is a low priority, maybe just a doc update?)
Role setting. I think setting the role using set is a fantastic solution, but I think maybe it should come from somewhere other than a 'role' attribute.
Sorry for the rambling, and hijacking of the thread... been working on this a day or two and wanted to share somewhere. I have it basically working, except I need to rewrite the token to add 'role' otherwise I got everything to work with auth0.
@angelosarto Thank you for this all this great information! Could you expand on the role? Conceptually, I was thinking that I would run into issues, but I thought that would be resolved via role level security policies. Not sure this is the appropriate place to discuss, but I'm on the gitter too.
Wow @angelosarto thanks so much. Would you be willing to write a documentation article once your thoughts are more clear and some solutions have been implemented.
Here's what I'm thinking right now. We allow users to specify the verification config here as suggested by @angelosarto to give users maximum control. We can also let users specify the config to sign
. This should provide maximum control over the JWT process. This would only be available in the JS API and not the CLI. So far we've been able to maintain feature parity, and I'd like to maintain as much parity as possible, but this seems like a good exception. I agree with @ugiacoman though. Post secret, most use cases can disable the aud
requirement if need be. I think it's a nice default to prevent slip ups though.
We should also log an error when --secret
is used without --default-role
, it really doesn't make sense to not do that. I don't want an error so that we can maintain backwards compatibility, but a warning to the effects of: "Warning: running in INSECURE mode" should do the trick.
Im also open to see what role setting would look like, @angelosarto. My thought is allow users to define a function which gets the role off of a JWT object. I'm initially hesitant/opposed because that means it's a little harder to reach the one PostGraphQL/PostgREST dream for auth by adding another level of indirection. Before implementing this I want to see a really clear use case. It sounds currently like there are workarounds on the Auth0 side. Are these workarounds ideal, or hacky?
@FGRibreau as to the PostGraphQL/PostgREST dream, I think the PostgREST team is moving to a naming scheme we could be compatible with. Can't recall the status atm. The architecture of PostGraphQL is setup so that hypothetically we could serve a REST API alongside the GraphQL API. Or a Falcor API, or a SOAP API if that's your jam. Check out https://github.com/calebmer/postgraphql/issues/87 for more info. It still requires a lot of work, but if you're interested in funding we could make it happen.
@calebmer Currently, if you use one of Postgraphql default generated queries, does it look for the role inside of the JWT or will it always use the role the connection was created with? Or is this what @angelosarto was referring to with the secret
and default-role
. To define the role you wish to use do you need to define your own functions? I'm thinking about use cases with the JS API.
@FGRibreau @calebmer Small update, just implemented this with the JS API and got the same error with aud. I can try my luck at a PR if you guys would like.
EDIT:
So I got it working locally with auth0. Quick note, Auth0 has this thing called rules
by which you can add fields like role
to your JWT. Their default rule for adding role
returns an array of roles
instead of a singular role. I can't think of a good use case for multiple roles... If there is an array of roles, do we want to just take the first role? Let me know... I'm more than willing to make a PR for this, just unsure how we would support this from the JS API, but I can figure it out though :)
UPDATE 2:
Got the fix working with the JS API! Let me know how we want to handle the role
or roles
. I'll also update the library docs for the new optional param jwtAudience
.
I can write up so example docs of the setup in Auth0. I got it mostly working but not adding the role. (Maybe @ugiacoman can look below to see if we are doing the same thing)
The use case I was trying for was to allow users to post to auth0 to get a token and then turn around and use that token to call graphql api directly; hopefully without building any services myself.
When calling the auth0 resource owner password grant endpoint I haven't yet been able to get a response that includes the role either from app_metadata or on the root object. I opened a topic on the auth0 forums and am hoping to see some response over the next couple days.
Locally, I made it work by modifying this line in postgraphql and changing it to this:
if (jwtClaims.role != null)
role = jwtClaims.role
}
else if (jwtClaims.iss != null && jwtClaims.sub != null) {
role = jwtClaims.iss + jwtClaims.sub
}
I can work around the aud check as long as I name the "api" object in auth0 to be postgraphql, I haven't (yet) found a workaround for setting the role unless I do one of these:
(edit - fixed the code formatting)
@angelosarto Similar, but a bit different use case. My client has authentication. When a user logs in, the response JWT is stored on the client. When a user makes a request to my API, it is passing the JWT in each request. All my API endpoints are secured by JWTs. My JWT comes out with the following structure:
{
...
"roles": [
"user_role"
],
"clientID": "exampleClientID",
"created_at": "2016-12-27T17:50:30.013Z",
"iss": "exampleISS",
"sub": "exampleSUB",
"aud": "exampleAUD",
"exp": 1483016195,
"iat": 1482980195
}
I am defining my /graphql
endpoint as follows.
app.use('/', postgraphql(config.db_string, config.db_schema, {
jwtSecret: new Buffer(config.secret, 'base64'),
jwtAudience: config.audience,
graphiql: true,
}));
Inside of the JWT check, I have modified it to check for an optional jwtAudience
parameter when defining it through the JS API (which I have added inside of the createPostGraphQLHttpRequestHandler
). You'll also notice that Auth0 produces a roles
field instead of role
. Therefore I am checking if that array exists, if so use the first role found.
if (jwtToken) {
// Try to run `jwt.verify`. If it fails, capture the error and re-throw it
// as a 403 error because the token is not trustworthy.
try {
// Check optional JWT audience. if none provided, default to postgraphql.
if (jwtAudience == undefined)
jwtAudience = "postgraphql";
jwtClaims = jwt.verify(jwtToken, jwtSecret, { audience: jwtAudience });
// If there is a `role` property in the claims, use that instead of our
// default role.
if (jwtClaims.role != null)
role = jwtClaims.role;
else if (jwtClaims.roles != null)
role = jwtClaims.roles[0];
}
catch (error) {
error.statusCode = 403;
throw error;
}
}
The roles
solution successfully works with Auth0, but also allows for a role
field. I don't check for the issuer or subject fields. Not completely satisfied with this solution, but please let me know what you all think.
Made a PR but had some issues, will look back into when I get a chance.
So I read this issue with great interest. Any updates on the matter?
Haven't gotten a chance to makes the changes necessary. The PR fixes the issue, but doesn't pass the tests. I'll take another look this weekend, see if I can fix it.
Not going to get a chance this weekend but soon :)
@Levino @angelosarto @FGRibreau Created a new PR without the roles
object support. Passes all tests! #365
Here is a (ugly) workaround to change the audience
until the pull request lands in the repository.
import jwt = require('jsonwebtoken');
const verify = jwt.verify;
jwt.verify = function (token, secret, options?) {
if (options && options.audience === 'postgraphql') {
options.audience = 'my_custom_audience';
}
return verify.apply(jwt, arguments);
};
Hey guys, any update about the roles object support ? Or any other way to make it work with Auth0 ?
@AlineFrancois you can now use the jwtAudiences
parameter of the postgraphql middleware. This is also matched to the jwt-audiences
command line parameter. Just use the auth0 clientID as your audience.
@mlegenhausen Great stuff! Is there a complete example somewhere?
@Levino Only in one of my internal projects ;) but if you have further questions don't hesitate to ask. Just one note to get the role
parameter working you need to add a auth0 hook that exposes a role
parameter from the app_metadata
attribute on the root level of your JWT. You can use:
function addMetadataToUserRoot(user, context, callback) {
var scope = context.request && context.request.query && context.request.query.scope || '';
if (scope.indexOf('app_metadata') > -1) {
Object.assign(user, user.app_metadata);
delete user.app_metadata;
}
callback(null, user, context);
}
And then add app_metadata
to your auth0 scope
variable. This hook simply copies all attributes defined under app_metadata
to the main object which then get combined in your jwt.
If someone could write a wiki page or submit a pull request to our documetation detailing this, that would be excellent 😁
@benjie @calebmer I should be able to write up a small example and doc for this. perhaps a new sub folder or markdown document in in the examples directory for running with Auth-0.
Any chance there might be a release to npm that includes: https://github.com/postgraphql/postgraphql/pull/434 then I can write the doc to just use npm install as the simplest example.
@calebmer I think it would be great for the user to be able to define a function for extracting the roles, as really roles should be namespaced with a domain so that JWTs are OIDC compliant.
With the solutions proposed above, I think role is just in the JWT e.g. "role": "test"
instead of "https://admin.example.com/role": [ "test" ]
. If a function could be defined, the namespace could be any old thing.
Am I just imagining a problem here/creating unnecessary work?
Sorry - never mind. This #480 appears to have fixed the issue - just have to use PostgraphQL as a library rather than from the CLI.
--jwt-role <string> a comma seperated list of strings that create a path in the jwt from which to extract the postgres role. if none is provided it will use the key `role` on the root of the jwt.
I think you can just use the CLI option?
I get error: unknown option --jwt-role
at the CLI and it is not listed as an option in the help for the CLI.
Ah; maybe we haven't shipped that yet. Try the v4 alpha:
npm i postgraphql@next
Hm. Still doesn't seem to work - same error.
I don't think I need to do anything else to get the correct version of PostgraphQL, right? I still just have to run postgraphql
to start the latest v4 alpha?
Nope, should work. Try --jwtRole
but failing that I'll have to look into it later.
Still doesn't work.
No worries re. getting to it later, but it would be great for it to work at some point, though.
I've just released v3.3.0 which I believe should fix this issue; if it does not please re-open 👍
Also I think you might have been using the wrong CLI flag - you want --jwt-audiences
for overriding the audience.
Ah, no, I think this issue got a little muddled.
small correction, the command line flag is --jwt-audience
not jwt-audiences
, at least on version 4.0.1
Hello!
I'm experimenting around PostgraphQL for a new startup and it seems at the time being it requires clams.sub to be "postgraphql" while Auth0 gives
"sub": "auth0|70522e86-1fcf-415f-aeda-9325f3413adf"
(auth0|user_id), @calebmer I can't find a CLI parameter to change this, any idea to make PostgraphQL work with Auth0? :)