Skip to content

Cosmo Router incorrectly checks the wrong "scopes" claim #645

Description

@rickpasetto

Component(s)

router

Component version

0.74.0

wgc version

0.45.0

controlplane version

cloud

router version

0.74.0

What happened?

Description

The @requiresScopes feature appears to be looking for a JWT claim called scopes, but RFC 8693 specifies that the claim should be called scope (singular). This appears to perhaps be a typo in authentication.go, around line 55, on branch main.

Steps to Reproduce

I reproduced this by setting up a simple Keycloak server in Docker along with Wundergraph.

  • Create a JWT with the following claims (for example):
...
"scope": "email profile read:value",
...
  • Create a subgraph with the @requiresScopes parameter, e.g.
type Query {
  hello: String! @requiresScopes([["read:value"]])
}
  • Create a federated graph pointed at this subgraph
  • Start the Wundergraph Cosmo Router with the config pointed at the Keycloak instance.
  • Using the valid JWT as indicated above in the "Authorization" header, try doing a query:
query {
  hello
}

(assuming you have a federated query set up like this)

Expected Result

Normal results of the query

Actual Result

Incorrect "Unauthorized" error:

{
  "errors": [
    {
      "message": "Unauthorized"
    }
  ],
  "data": null,
  "extensions": {
    "authorization": {
      "missingScopes": [
        {
          "coordinate": {
            "typeName": "Query",
            "fieldName": "hello"
          },
          "required": [
            [
              "read:value"
            ]
          ]
        }
      ],
      "actualScopes": []
    }
  }
}

Environment information

Setting up for this issue requires a bit of work. You'll need (at least):

  • Docker
  • a wundergraph supergraph setup

I've attached a zip file with the following files:

wundergraph/
├── docker-compose.keycloak.yml
├── docker-compose.wundergraph.yml
├── hello
│   ├── Dockerfile
│   ├── hello.js
│   ├── package-lock.json
│   └── package.json
├── realm-export.json
├── test.graphql
└── wundergraph.config.yml

Hopefully this is enough config to set up keycloak and wundergraph in Docker. You'll need to start everything by running:

WUNDERGRAPH_API_TOKEN=<your_api_token> docker compose -f atp/docker-compose.keycloak.yml -f atp/docker-compose.wundergraph.yml -f atp/docker-compose.hello.yml up --build --wait

Then, open keycloak's dashboard: http://localhost:8181/
Go to Realm Settings, choose Action (drop-down) -> Partial Import, then pick the realm-export.json file.
Now, copy the client secret by going to Clients -> Credentials -> Client Secret. Copy this and save it for later.

Now, open up http://localhost:9001/, and try doing:

query Hello {
  hello
}

You should get:

  "errors": [
    {
      "message": "unauthorized"
    }
  ],
  "data": null

This is expected. However, obtain a token by doing this (from the client secret above):

curl --location 'http://localhost:8181/auth/realms/master/protocol/openid-connect/token' \
--header 'Content-Type: application/x-www-form-urlencoded' \
--data-urlencode 'grant_type=password' \
--data-urlencode 'client_id=hello-client' \
--data-urlencode 'username=admin' \
--data-urlencode 'password=admin' \
--data-urlencode 'client_secret=<client_secret>'

Hopefully, if you have everything set up right, you should see some output like this:

{"access_token":"<ACCESS_TOKEN>","expires_in":60,"refresh_expires_in":... }

Copy <ACCESS_TOKEN> and add this to your header in the GraphQL query:

"Authorization": "Bearer <ACCESS_TOKEN>"

Now, you see the result:

{
  "errors": [
    {
      "message": "Unauthorized"
    }
  ],
  "data": null,
  "extensions": {
    "authorization": {
      "missingScopes": [
        {
          "coordinate": {
            "typeName": "Query",
            "fieldName": "hello"
          },
          "required": [
            [
              "read:value"
            ]
          ]
        }
      ],
      "actualScopes": []
    }
  }
}

...which is wrong, because the admin user does have the read:value scope.

I hope this is enough detail.

wundergraph.zip

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions