SoftInstigate / restheart

Rapid API Development with MongoDB
https://restheart.org
GNU Affero General Public License v3.0
807 stars 171 forks source link

JWT in restheart #492

Closed Denii-dot closed 10 months ago

Denii-dot commented 11 months ago
. After unlocking JWT tokens on restHeart, I cannot access resources. ## Expected behavior I don't know if I understand the operation of JWT tokens on restHeart correctly. But here's the thing: I have a certain collection and I have the ability to view its values through restHeart. I want that after enabling JWT tokens, only authenticated people can view the contents. ## Current behavior Querying by postman this collection, adding Authorization -> JWT Bearere Token to the query: ``` { "usernameClaim" : "sub", "roles": ["admin" ], "issuer": "myIssuer", "audience": "myAudience" } ``` 403- prohibited, if I select /tokens I get 401 ## Context Token authorization during the query (possibly how to do token assignment, but how to do it) I have a database, and in it userList collections. I want the TokenJWT to be assigned after successful authentication, and access to further resources. I initially wrote myself a server in express.js (with endpoints: /login, /auth but I don't know if it could be done with just restHeart) ## Environment RESTHeart: 6.3 MongoDB: 4.418 Linux Ubuntu 22.04 . ``` jwtAuthenticationMechanism: enabled: true algorithm: HS256 key: jwt_secret_key base64Encoded: false usernameClaim: sub rolesClaim: roles fixedRoles: # - admin issuer: myIssuer audience: myAudience ```
ujibang commented 11 months ago

Hi @Denii-dot ,

Your JWT is wrong, it does not contain the sub claim and the issuer claim shoulb be iss:

sub (subject): Subject of the JWT (the user)

See more on claims at https://auth0.com/docs/secure/tokens/json-web-tokens/json-web-token-claims

Should be something like:

{
  "sub" : "foo",
  "roles": ["admin"],
  "iss": "myIssuer"
}

You can generate a valid JWT at jwto.io

example (with HS256 and key jwt_secret_key:

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmb28iLCJyb2xlcyI6WyJhZG1pbiJdLCJpc3MiOiJteUlzc3VlciJ9.l9gjP47grnMQSEpqcTb0HzD4XoSr3spErwJb9Alx9EI

Here is a test:

create a conf override file called conf.yml

(please set audience: null since I found a bug in the configuration parser, will be fixed in 7.6.4)

/jwtAuthenticationMechanism:
  enabled: true
  algorithm: HS256
  key: jwt_secret_key
  base64Encoded: false
  usernameClaim: sub
  rolesClaim: roles
  fixedRoles:
    # - manager
  issuer: myIssuer
  audience: null

run RESTHeart with:

$ java -jar restheart.jar -o conf.yml

And the request (I use httpie here) is authenticated (and authorized due to the role admin)!

$ http :8080/coll/_size Authorization:"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiJmb28iLCJyb2xlcyI6WyJhZG1pbiJdLCJpc3MiOiJteUlzc3VlciJ9.l9gjP47grnMQSEpqcTb0HzD4XoSr3spErwJb9Alx9EI"

HTTP/1.1 200 OK

{
    "_size": 11415
}
ujibang commented 11 months ago

You can also have RESTHeart issue JWT by enabling the jwtTokenManager

Define the following conf override file:

/jwtAuthenticationMechanism:
  enabled: true
  algorithm: HS256
  key: jwt_secret_key
  base64Encoded: false
  usernameClaim: sub
  rolesClaim: roles
  fixedRoles: null
  issuer: myIssuer
  audience: null

/jwtTokenManager:
  enabled: true
  key: jwt_secret_key
  ttl: 15
  srv-uri: /tokens
  issuer: myIssuer
  audience: null
  # additional JWT claims from accounts properties
  account-properties-claims:
    # - foo # property name
    # - /nested/property # xpath expr for nested properties

/rndTokenManager/enabled: false

Now authenticate the request using the basic authentication:

$ http -a admin:secret :8080/ping
HTTP/1.1 200 OK
...
Auth-Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJteUlzc3VlciIsInN1YiI6ImFkbWluIiwiZXhwIjoxNzAyMDQzMzQzLCJyb2xlcyI6WyJhZG1pbiJdfQ.Z1KpCff9uYLo8yAZNT8V7vAaJcKCmY9Hm8BAaqtES-E
Auth-Token-Location: /tokens/admin
Auth-Token-Valid-Until: 2023-12-08T13:49:03.869Z
...

Greetings from RESTHeart!

The response contains the Auth-Token header with the JWT:

The decoded JWT is:

{
  "iss": "myIssuer",
  "sub": "admin",
  "exp": 1702043343,
  "roles": [ "admin" ]
}

Now you can use it for further requests:

$ http :8080/coll/_size Authorization:"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJteUlzc3VlciIsInN1YiI6ImFkbWluIiwiZXhwIjoxNzAyMDQzMzQzLCJyb2xlcyI6WyJhZG1pbiJdfQ.Z1KpCff9uYLo8yAZNT8V7vAaJcKCmY9Hm8BAaqtES-E"
HTTP/1.1 200 OK
...
Auth-Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJteUlzc3VlciIsInN1YiI6ImFkbWluIiwiZXhwIjoxNzAyMDQzMzQzLCJyb2xlcyI6WyJhZG1pbiJdfQ.Z1KpCff9uYLo8yAZNT8V7vAaJcKCmY9Hm8BAaqtES-E
Auth-Token-Location: /tokens/admin
Auth-Token-Valid-Until: 2023-12-08T13:49:03.869Z
...

{
    "_size": 11415
}

You can renew the JWT (extending the expiration) as follows:

$ http :8080/coll/_size\?renew-auth-token Authorization:"Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJteUlzc3VlciIsInN1YiI6ImFkbWluIiwiZXhwIjoxNzAyMDQzMzQzLCJyb2xlcyI6WyJhZG1pbiJdfQ.Z1KpCff9uYLo8yAZNT8V7vAaJcKCmY9Hm8BAaqtES-E"
HTTP/1.1 200 OK
....
Auth-Token: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJteUlzc3VlciIsInN1YiI6ImFkbWluIiwiZXhwIjoxNzAyMDQzNzUwLCJyb2xlcyI6WyJhZG1pbiJdfQ.mK3YJrD5jXOk4M64Lvg9E-HAf595pvP0MLSvCWc62jc
Auth-Token-Location: /tokens/admin
Auth-Token-Valid-Until: 2023-12-08T13:55:50.885Z

As you can notice the request is authenticated with a JWT that expires on 2023-12-08T13:49:03.869Z but the response header includes a new JWT that expires on 2023-12-08T13:55:50.885Z