Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can we throw a better error message for JWSError JWSInvalidSignature? #1840

Open
priyadarshiniguptan opened this issue May 3, 2021 · 14 comments
Labels
config related to the configuration options difficulty: beginner Pure Haskell task messages user-facing error/informative messages

Comments

@priyadarshiniguptan
Copy link

priyadarshiniguptan commented May 3, 2021

Hi,
I am using Postgrest on docker and the below is my docker compose -

postgREST:
    container_name: middleware-rdbms-auto-baas_postgREST
    image: postgrest/postgrest
    restart: always
    ports:
      - '${POSTGREST_PGDCP_EXPOSE_PORT:-3000}:3000'
    environment:
      PGRST_DB_URI: >-
        postgres://${POSTGRESQLENGINE_USER}:${POSTGRESQLENGINE_PASSWORD}@${POSTGRESQLENGINE_HOST}:${POSTGRESQLENGINE_PORT}/${POSTGRESQLENGINE_DB}
      PGRST_DB_SCHEMA: '${PGDCP_SCHEMA}'
      PGRST_DB_ANON_ROLE: '${POSTGRESQLENGINE_USER}'
      PGRST_JWT_SECRET: 'pgdcpsecurewebtoken'

The login user is the authenticator role and I have also specified a jwt-secret that is being used by a function in Postgres that uses the sign method of pgjwt extension to generate the jwt.

However, when I try to
GET http://10.10.11.124:4432/<api name> with the authorization header set to Bearer I get the error below:

{
    "message": "Server lacks JWT secret"
}

Please let me know where could I possibly be going wrong.

@wolfgangwalther
Copy link
Member

PGRST_JWT_SECRET: 'pgdcpsecurewebtoken'

We need a secret of at least 32 characters, as stated in the docs.

However, we should try to throw a better error message in that case - possibly already at startup.

@wolfgangwalther wolfgangwalther added bug config related to the configuration options labels May 3, 2021
@priyadarshiniguptan
Copy link
Author

Thanks for clarifying however I tried giving a secret longer than 32 characters and gave a restart. But still the issue persists.
The secret I am using now is something like 'pgdcpsecurewebtokenpgdcpsecurewebtoken'

Secondly , I also tried generating a token on jwt.io using this secret and I got a jwt as below:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoibWVkaWd5X2FkbWluX3JlYWRfb25seSIsImV4cCI6MTYyMDEwNDI4MH0.LmtOC1igJ1-ilu0ra3d99FnxlbJviJpFnNlLJ5UV1Ug

Using this in my get request fails with the same error
{
"message": "Server lacks JWT secret"
}

@wolfgangwalther
Copy link
Member

Thanks for clarifying however I tried giving a secret longer than 32 characters and gave a restart.

Did you just restart the container or did you recreate it? Restarting the container will not take changes to the docker-compose file in account - you need to re-create it, e.g. with docker-compose up.


Using the same secrets you posted and some JWTs that were signed with the same token, I get:

  • {"message":"JWSError JWSInvalidSignature"} with a token that is too short - even though the JWT is signed correctly
  • a successful response with the longer token

As stated earlier, we should try to throw a better error when the token is too short - and not just report an invalid signature for every jwt we receive.

But other than that, I think everything is OK, and you just need to make sure the environment variable you set really reaches PostgREST.

@bcb
Copy link

bcb commented Sep 7, 2021

I had this same issue. The docs show various ways to store the JWT secret, but Postgrest only searches for it in a few places when validating a token. Setting the env var PGRST_JWT_SECRET works.

@steve-chavez
Copy link
Member

We cannot throw a precise error "message" regarding the 32 chars min because we also accept JWKS in the jwt-secret.

However we can add a "hint" with different recommendations.

cc @laurenceisla

@Grkmus
Copy link

Grkmus commented Oct 23, 2021

I have the similar issue. When I set the PGRST_JWT_SECRET in the docker-compose it works. But, if I set the jwt-secret by query alter database mydb set app.jwt_secret to 'mysecretlongerthan3223123dfqsad1ewd12e1wd1d2qsd1dwqsxc1d'; It sseem to not setting the secret and shows "message": "Server lacks JWT secret" message.

@wolfgangwalther
Copy link
Member

But, if I set the jwt-secret by query alter database mydb set app.jwt_secret to 'mysecretlongerthan3223123dfqsad1ewd12e1wd1d2qsd1dwqsxc1d'; It sseem to not setting the secret and shows "message": "Server lacks JWT secret" message.

app.jwt_secret is not a configuration option for PostgREST. If you want to use in-database-configuration, you need to use pgrst.jwt_secret as noted in https://postgrest.org/en/v8.0/configuration.html#in-database-configuration.

@wolfgangwalther wolfgangwalther changed the title GET request failing with "message": "Server lacks JWT secret" error Can we throw a better error message for JWSError JWSInvalidSignature? Jan 11, 2022
@colearendt
Copy link
Contributor

colearendt commented Feb 10, 2022

Another case where I hit this, in case it helps others: running locally in dev mode and switching between two backends, I did not reload the webpage. The localStorage cache was getting set properly, but some other state/cache/memory had not been cleared.

It was not clear to me that the wrong token was being sent until I inspected the JWT in the request. (i.e. I was sending a token for user fromBackendOne to backendTwo). Always good to check the JWT in the request and not just the value in the cache that you think you are using 😄 I was beginning to question my sanity. Reloading the page resolved 😅

@wolfgangwalther
Copy link
Member

wolfgangwalther commented Feb 10, 2022

@colearendt:

It was not clear to me that the wrong token was being sent until I inspected the JWT in the request

I guess in that case, it could help to add the received token as a json representation to the error message? That should have lead you on the right track much quicker, correct?


@steve-chavez:

We cannot throw a precise error "message" regarding the 32 chars min because we also accept JWKS in the jwt-secret.

Can we not change the parser here ...

parseSecret :: ByteString -> JWKSet
parseSecret bytes =
fromMaybe (maybe secret (\jwk' -> JWT.JWKSet [jwk']) maybeJWK)
maybeJWKSet
where
maybeJWKSet = JSON.decodeStrict bytes :: Maybe JWKSet
maybeJWK = JSON.decodeStrict bytes :: Maybe JWK
secret = JWT.JWKSet [JWT.fromKeyMaterial keyMaterial]
keyMaterial = JWT.OctKeyMaterial . JWT.OctKeyParameters $ JOSE.Base64Octets bytes

... to something like this?

  • check for JWKS first
  • check for JWK next
  • check for length of secret now
  • if good, use secret

@colearendt
Copy link
Contributor

colearendt commented Feb 10, 2022

It definitely would have! But I'm also not sure whether everyone would be happy with that solution. I would think the user has access to the JWT so exposing the data shouldn't be a concern - just wanted to note that there may be people bothered by exposure of data where it was not before 🤷‍♂️ I don't know what standards around JWT behavior / exposure are though

Interestingly and as a small corollary that I forgot about - postgrest-js is where the problem surfaced, but it did not in Axios (so only some of my requests were failing). So it is possible that postgrest-js or whatever library it uses has some type of cache/memory in it. That said, I would love some improvement on the error message here!

@wolfgangwalther
Copy link
Member

wolfgangwalther commented Feb 10, 2022

I would think the user has access to the JWT so exposing the data shouldn't be a concern - just wanted to note that there may be people bothered by exposure of data where it was not before man_shrugging I don't know what standards around JWT behavior / exposure are though

Yeah. Whoever is bothered by decoding the byte64 string and displaying it for better error message - doesn't understand how JWTs work. "Encoding" something in the JWT is by no means "secret". Everything in the JWT is public and this is signed only.

So if at all, it would lead to more education for those who ask :)


Although there is one thing that we should not do: And that is returning the signature of the JWT in the error message. We should only return the payload.

This way ppl can easily copy the error message to somewhere else... e.g. to our issue tracker, without risk.

@taimoorzaeem
Copy link
Collaborator

Should we close this issue in favor of #3600 which also encompasses this issue? @develop7 Are you working on this?

@develop7
Copy link
Collaborator

@taimoorzaeem no, I'm not, close away

@wolfgangwalther
Copy link
Member

I think there's some more valuable details in here that are not part of #3600, so we shouldn't close it, yet.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
config related to the configuration options difficulty: beginner Pure Haskell task messages user-facing error/informative messages
Development

No branches or pull requests

8 participants