PostgREST / postgrest

REST API for any Postgres database
https://postgrest.org
MIT License
23.41k stars 1.03k forks source link

JWT invalid with Auth0 tokens #660

Closed damienbry closed 8 years ago

damienbry commented 8 years ago

Hi !

I'm using the postgres binary build from ubuntu, and I'm trying to plug the external authentication Auth0.

They use a JWT whose sub claim is in the form

{
    ...
    sub: "{connection}|{user_id}",
    ...
}

The pipe | makes postgrest returns a

{
  "message": "Invalid JWT"
}

I don't know if it comes from postgrest or even higher in the Web.JWT library.

ruslantalpa commented 8 years ago

i think is from Web.JWT, PostgREST does not look as sub

darkyen commented 8 years ago

Adding support for this would be wonderful, Auth0 and Postgrest look like a match made in heaven.

damienbry commented 8 years ago

The sub claim must be in StringOrURI format according to the RFC-7519 However, there is no mention of particular forbidden character for the String.

ruslantalpa commented 8 years ago

Have not tested this a all, just glanced how the JWT lib works, the main logic for this issue i think is here https://bitbucket.org/ssaasen/haskell-jwt/src/c8b79eda623fb16061a19fa0196a9a528dcb523f/src/Web/JWT.hs?at=master&fileviewer=file-view-default#JWT.hs-495

instance FromJSON StringOrURI where
    parseJSON (String s) | URI.isURI $ T.unpack s = return $ U $ fromMaybe URI.nullURI $ URI.parseURI $ T.unpack s
    parseJSON (String s)                          = return $ S s
    parseJSON _                                   = mzero

so if URI.isURI $ T.unpack s would return true but URI.parseURI $ T.unpack s would actually fail, then i think the problem will surface.

@Orbmancer care to make a small test and check if the logic is this line works? parseJSON (String s) | URI.isURI $ T.unpack s = return $ U $ fromMaybe URI.nullURI $ URI.parseURI

or maybe make a small test and see if Web.JWT fails to parse your token (maybe somehow i missed it and the problem is in postgrest)? i.e. a) test web.jwt fails to parse your token b) if a is true (it fails), then test if the logic in isURI / parseUri is correct

damienbry commented 8 years ago

I'm on it 👍

darkyen commented 8 years ago

Would it be possible to switch to https://github.com/frasertweedale/hs-jose its listed on jwt.io and seems much more mature also works better with #567 .

damienbry commented 8 years ago

I tested a minimalistic token with a pipe character and it passes the tests, so my first analysis was wrong. Apparently, it's the Auth0 tokens which are specifically refused, I don't know yet where is the problem.

ruslantalpa commented 8 years ago

Ca you post the jwt and payload her?

ruslantalpa commented 8 years ago

Here*

darkyen commented 8 years ago

Here you go

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3NvbWUtZ2FtZS5hdXRoMC5jb20vIiwic3ViIjoiZ2l0aHVifDEwNDEzMTUiLCJhdWQiOiJmMGFUYU5LUjlUNTRpTWVzU2xNcG5xZDdQZkVqWEpIbyIsImV4cCI6MTQ2Nzc2NjQ1MSwiaWF0IjoxNDY3NzMwNDUxfQ.U48xp6FOcRlolpoWnp5ACbjdb6-y-aHcaXfjL67QToM

{
  "iss": "https://some-game.auth0.com/",
  "sub": "github|1041315",
  "aud": "f0aTaNKR9T54iMesSlMpnqd7PfEjXJHo",
  "exp": 1467766451,
  "iat": 1467730451
}
ruslantalpa commented 8 years ago

Ok, this looks ok it's a token generated by Auth0, right? and it fails to validate in with web.jwt lib?

ruslantalpa commented 8 years ago

stupid question, have you used the same secret when starting postgrest with the one used in Auth0?

damienbry commented 8 years ago

Yup. When I pass the token generated by Auth0 in the haskell web.jwt test suite, it fails to decode it.

ruslantalpa commented 8 years ago

this is the line where the jwt gets decoded and we only look at exp https://github.com/begriffs/postgrest/blob/master/src/PostgREST/Auth.hs#L66 but since you get invalid jwt, it means it failed to validate it so either the secret/algorithm does not match or there it a bug in web.jwt what type of algorithm have you set for jwt in auth0 and does the secret match

try

{
  "alg": "HS256",
  "typ": "JWT"
}

sample token eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJyb2xlIjoiYWRtaW5pc3RyYXRvciIsInVzZXJfaWQiOjEsImNvbXBhbnlfaWQiOjF9.ate5mETtGRu-mfGF4jFt7pP1b4W85r2uEXt603D7obc

secret = secret

ruslantalpa commented 8 years ago

ok, that's one step towards figuring this out check that you use a supported algorithm by web.jwt in auth0 and that the secret metches

(maybe post here the token and the short haskell test)

damienbry commented 8 years ago

I checked the signature algorithm (HS256) and the secret key (secret).

Here is my test with my token that fails:

case_decodeAndVerifyJWTAuth0 = do
    -- Auth0 token
    let input = "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3NvbWUtYXBwLmV1LmF1dGgwLmNvbS8iLCJzdWIiOiJ0d2l0dGVyfDMwMjM5MzMwNTEiLCJhdWQiOiI4SjNtQWx6Q1BvRzZtZ3JUcVYybEFQVHlXZmNuUjd3RiIsImV4cCI6MTQ2Nzc3NDI4OCwiaWF0IjoxNDY3NzM4Mjg4fQ.E8l3vJELio7nTzeWxqJYogFIbtcRL6IjdPsolu04WCY"
        mJwt = decodeAndVerifySignature (secret "secret") input
    True @=? isJust mJwt
damienbry commented 8 years ago

Token which translates into:

{
  "iss": "https://some-app.eu.auth0.com/",
  "sub": "twitter|3023933051",
  "aud": "8J3mAlzCPoG6mgrTqV2lAPTyWfcnR7wF",
  "exp": 1467774288,
  "iat": 1467738288
}
begriffs commented 8 years ago

When I put the encoded string into jwt.io and use "secret" for the secret it passes signature verification only when the checkbox "secret base64 encoded" is checked. If I wanted to encode that payload so that it passes the test with the checkbox unchecked then I would use this instead:

eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJodHRwczovL3NvbWUtYXBwLmV1LmF1dGgwLmNvbS8iLCJzdWIiOiJ0d2l0dGVyfDMwMjM5MzMwNTEiLCJhdWQiOiI4SjNtQWx6Q1BvRzZtZ3JUcVYybEFQVHlXZmNuUjd3RiIsImV4cCI6MTQ2Nzc3NDI4OCwiaWF0IjoxNDY3NzM4Mjg4fQ.Vxa0a2IKykWywWGu0Wnk3TuQnAaphprQK0k78OBfU0s

Does that payload fare any better in your test?

damienbry commented 8 years ago

Your token passes the tests. I'm confused about this 'secret base64 checkbox', maybe I misunderstood the Auth0 configuration on how to generate token using HS256

ruslantalpa commented 8 years ago

Try this, leave your auth0 config as is. When startin postgrest istead of providing -j parameter as plain text, encode it with base64. I. E. Istead of -j secret Do -j c2VjcmV0

cristomanuel commented 8 years ago

I have b64 encoded Secret on my auth0.com account (for example: YWJjMTIz) and I did launch postgrest with -j option and the secret as plain/text (for example -j abc123). Be sure to include role claim into the token. Otherwise you will receive an "Invalid JWT" message.

regards,

darkyen commented 8 years ago

In that scenario, Invalid JWT is a misleading error it should rather give a 401 Unauthorized, No?

On Jul 6, 2016, at 7:16 PM, Cristo Manuel notifications@github.com wrote:

I have b64 encoded Secret on my auth0.com account (for example: YWJjMTIz) and I did launch postgrest with -j option and the secret as plain/text (for example -j abc123). Be sure to include role claim into the token. Otherwise you will receive an "Invalid JWT" message.

regards,

— You are receiving this because you commented. Reply to this email directly, view it on GitHub https://github.com/begriffs/postgrest/issues/660#issuecomment-230775789, or mute the thread https://github.com/notifications/unsubscribe/AA_jo4fubvSxQ7AUHgca2QgGIaxzKZdRks5qS7G5gaJpZM4JDDde.

cristomanuel commented 8 years ago

You're right, because of this I did try the secret with b64 and plain/text on both sides (auth0.com and my postgrest server).

Finally, I did modify the json including the role claim, then I signed it and i runs perfect!

ruslantalpa commented 8 years ago

Jwt payload does NOT have to include the role. Your error was from the base64 thing

On 06 Jul 2016, at 16:56, Cristo Manuel notifications@github.com wrote:

You're right, because of this I did try the secret with b64 and plain/text on both sides (auth0.com and my postgrest server).

Finally, I did modify the json including the role claim, then I signed it and i runs perfect!

— You are receiving this because you commented. Reply to this email directly, view it on GitHub, or mute the thread.

ruslantalpa commented 8 years ago

Hence ... Invalid JWT. It seems that there is no bug here at all, just misconfigurations (should be closed).

begriffs commented 8 years ago

When I add docs about JWT/Auth0 what should I say about b64? Is there a setting on Auth0 that controls if they b64 encode the secret, or do they always expect it to be provided encoded?

dev10 commented 8 years ago

Auth0 always encode their "Client Secrets" with b64 because their secrets are binary. They are also non-standard b64. I had to make sure to replace any invalid characters like - with a + etc.

begriffs commented 8 years ago

Thanks for the note.

FGRibreau commented 7 years ago

@dev10 @cristomanuel could you give us more details about how you did this? I have the same issue as @Orbmancer and would really like to use postgrest with auth0 :)

begriffs commented 7 years ago

@FGRibreau the code on master (soon to be v0.4) has a configuration flag to indicate whether the jwt secret is encoded in b64. Here's a section in the new docs about Auth0 - https://postgrest.readthedocs.io/en/v0.4/auth.html#jwt-from-auth0

statik commented 7 years ago

Auth0 has also recently made a change (Dec 6 2016) to stop encoding new secrets as Base64, which makes them much easier to work with. If you have an existing endpoint you will need to rotate the secret in order to get a non-encoded one. https://auth0.com/forum/t/client-secret-stored-without-base64-encoding/4338/1

FGRibreau commented 7 years ago

@begriffs @statik thanks a lot I refreshed the auth0 secret and it now work perfectly :+1:

begriffs commented 7 years ago

I updated the postgrest docs about auth0 to reflect the change.

Sebastianbrg commented 6 years ago

Anyone that have created a go through on how to combine Postgrest with Auth0?