Token Introspection does not work with a public OAuth2 Client

Hi,

I’m trying to call /oauth2/introspect to validate the access token without success.
I’m using Hydra in a container from oryd/hydra:v0.11.12-alpine.

Here the request:

POST /oauth2/introspect HTTP/1.1
Host: 127.0.0.1:4444
Content-Type: application/x-www-form-urlencoded
Authorization: Basic aXQuZmFiYnJpY2FkaWdpdGFsZS5saWZlLmFwcGxhdW5jaGVyOg==
Cache-Control: no-cache
Postman-Token: fb972f6f-7b8c-7b08-e76f-08f93636cdb2

token=nkBPOW8GdkgLFF7ZvqIahE3FwGoBoaNbwaBzB1IgAX0.2tB0o--PqfLT9vgCgwZbZE-R-MyT39t0V3Z0TAhUaSE

Here the response:

{
    "error": "request_unauthorized",
    "error_description": "The request could not be authorized",
    "error_hint": "Check that you provided valid credentials in the right format.",
    "status_code": 401
}

Here the Hydra log:

hydra_1            | time="2018-05-24T15:41:13Z" level=info msg="started handling request" method=POST remote="172.19.0.1:56914" request=/oauth2/introspect
hydra_1            | time="2018-05-24T15:41:13Z" level=info msg="Access allowed" reason="The policy decision point allowed the request" request="&{rn:hydra:oauth2:tokens introspect it.fabbricadigitale.life.applauncher map[]}" subject=it.fabbricadigitale.life.applauncher
hydra_1            | time="2018-05-24T15:41:13Z" level=error msg="An error occurred" debug="OAuth 2.0 Client credentials are invalid" error=request_unauthorized hint="Check that you provided valid credentials in the right format."
hydra_1            | time="2018-05-24T15:41:13Z" level=debug msg="Stack trace: \ngithub.com/ory/hydra/vendor/github.com/ory/fosite.(*Fosite).NewIntrospectionRequest\n\t/go/src/github.com/ory/hydra/vendor/github.com/ory/fosite/introspection_request_handler.go:155\ngithub.com/ory/hydra/oauth2.(*Handler).IntrospectHandler\n\t/go/src/github.com/ory/hydra/oauth2/handler.go:307\ngithub.com/ory/hydra/oauth2.(*Handler).IntrospectHandler-fm\n\t/go/src/github.com/ory/hydra/oauth2/handler.go:122\ngithub.com/ory/hydra/vendor/github.com/julienschmidt/httprouter.(*Router).ServeHTTP\n\t/go/src/github.com/ory/hydra/vendor/github.com/julienschmidt/httprouter/router.go:299\ngithub.com/ory/hydra/vendor/github.com/urfave/negroni.Wrap.func1\n\t/go/src/github.com/ory/hydra/vendor/github.com/urfave/negroni/negroni.go:41\ngithub.com/ory/hydra/vendor/github.com/urfave/negroni.HandlerFunc.ServeHTTP\n\t/go/src/github.com/ory/hydra/vendor/github.com/urfave/negroni/negroni.go:24\ngithub.com/ory/hydra/vendor/github.com/urfave/negroni.middleware.ServeHTTP\n\t/go/src/github.com/ory/hydra/vendor/github.com/urfave/negroni/negroni.go:33\ngithub.com/ory/hydra/vendor/github.com/urfave/negroni.(middleware).ServeHTTP-fm\n\t/go/src/github.com/ory/hydra/vendor/github.com/urfave/negroni/negroni.go:33\nnet/http.HandlerFunc.ServeHTTP\n\t/usr/local/go/src/net/http/server.go:1918\ngithub.com/ory/hydra/cmd/server.(*Handler).rejectInsecureRequests\n\t/go/src/github.com/ory/hydra/cmd/server/handler.go:200\ngithub.com/ory/hydra/cmd/server.(*Handler).(github.com/ory/hydra/cmd/server.rejectInsecureRequests)-fm\n\t/go/src/github.com/ory/hydra/cmd/server/handler.go:113\ngithub.com/ory/hydra/vendor/github.com/urfave/negroni.HandlerFunc.ServeHTTP\n\t/go/src/github.com/ory/hydra/vendor/github.com/urfave/negroni/negroni.go:24\ngithub.com/ory/hydra/vendor/github.com/urfave/negroni.middleware.ServeHTTP\n\t/go/src/github.com/ory/hydra/vendor/github.com/urfave/negroni/negroni.go:33\ngithub.com/ory/hydra/vendor/github.com/urfave/negroni.(middleware).ServeHTTP-fm\n\t/go/src/github.com/ory/hydra/vendor/github.com/urfave/negroni/negroni.go:33\ngithub.com/ory/hydra/vendor/github.com/meatballhat/negroni-logrus.(*Middleware).ServeHTTP\n\t/go/src/github.com/ory/hydra/vendor/github.com/meatballhat/negroni-logrus/middleware.go:136\ngithub.com/ory/hydra/vendor/github.com/urfave/negroni.middleware.ServeHTTP\n\t/go/src/github.com/ory/hydra/vendor/github.com/urfave/negroni/negroni.go:33\ngithub.com/ory/hydra/vendor/github.com/urfave/negroni.(*Negroni).ServeHTTP\n\t/go/src/github.com/ory/hydra/vendor/github.com/urfave/negroni/negroni.go:73\ngithub.com/ory/hydra/vendor/github.com/rs/cors.(*Cors).Handler.func1\n\t/go/src/github.com/ory/hydra/vendor/github.com/rs/cors/cors.go:200\nnet/http.HandlerFunc.ServeHTTP\n\t/usr/local/go/src/net/http/server.go:1918\ngithub.com/ory/hydra/vendor/github.com/gorilla/context.ClearHandler.func1\n\t/go/src/github.com/ory/hydra/vendor/github.com/gorilla/context/context.go:141\nnet/http.HandlerFunc.ServeHTTP\n\t/usr/local/go/src/net/http/server.go:1918\nnet/http.serverHandler.ServeHTTP\n\t/usr/local/go/src/net/http/server.go:2619\nnet/http.(*conn).serve\n\t/usr/local/go/src/net/http/server.go:1801\nruntime.goexit\n\t/usr/local/go/src/runtime/asm_amd64.s:2337"
hydra_1            | time="2018-05-24T15:41:13Z" level=info msg="completed handling request" measure#http://localhost:4444.latency=60937415 method=POST remote="172.19.0.1:56914" request=/oauth2/introspect status=401 text_status=Unauthorized took=60.937415ms

Here the policy:

{
    "description": "Allow everyone including anonymous users to do introspect requests",
    "subjects": [
      client_id
    ],
    "resources": [
        "rn:hydra:oauth2:tokens"
    ],
    "actions": ["introspect"],
    "effect": "allow"
  }

I’m using a public client without a password.

Anyone knows what is wrong with this configuration?

Thanks for any help!

It seems like you’re not supplying a password in the Authorization header: Authorization: basic it.fabbricadigitale.life.applauncher:

Hi, thanks for your answer.

Yes, that’s client is public:

hydra clients create --id client_id --is-public...

Can a public client, without a secret, validate a token?

I’m actually not sure if that’ possible. Several reasons:

  • The basic authorization scheme doesn’t actually support not sending a password. You can try to send a dummy password so something like it.fabbricadigitale.life.applauncher:no-one-cares but I’m not sure if it will work, because:
  • The issue is that the introspection endpoint should be protected against token scanning attacks. In the official spec

    To prevent token scanning attacks, the endpoint MUST also require
    some form of authorization to access this endpoint, such as client
    authentication as described in OAuth 2.0 [RFC6749] or a separate
    OAuth 2.0 access token such as the bearer token described in OAuth
    2.0 Bearer Token Usage [RFC6750]. The methods of managing and
    validating these authentication credentials are out of scope of this
    specification.

So a client ID is not proof of authorization (because it’s knowledge is public) and thus it’s unlikely that it’s possible to introspect using a public client id.

Keep in mind that the introspection endpoint is primarily targeted at resource providers (aka your API backends), not at 3rd party apps:

This specification defines a method for a protected resource to query
an OAuth 2.0 authorization server to determine the active state of an
OAuth 2.0 token and to determine meta-information about this token.

So I’m not sure what your exact use case is here, but keep that in mind. You might want to use the /userinfo endpoint instead for your public client as you can just use the access token you already have there to see if it’s:

  • valid
  • what data it cointains

@alelb I’m moving this discussion to a new thread because it might help people with the same problem easier if they can identify it from the topic’s name

Thanks!

With a fake password does not work.
Using a client with a valid password everything works fine.