Stuck with IntrospectOAuth2Token

Hi all,
I am trying to set up a config with a client, hydra+consent and an api. The client gets a token with given scopes from hydra (after user consent), this works. Now I send the token to the api. The api needs to check if the token is active and valid for a certain scope. When using the SDK, this is done with IntrospectOAuth2Token as far as I can tell from the docs. I can’t get this to work and a little help would be great.

I have set up a client for the api with

 clients create --skip-tls-verify \
        --id protected-api \
        --secret $API_SECRET \
        --allowed-scopes hydra.clients, hydra.introspect \
        --grant-types client_credentials \
        --response-types token

and policy:

policies create --skip-tls-verify \
        --id check-token-policy \
        --description "Allow api to check token with /oauth2/introspect" \
        --allow \
        --actions introspect \
        --resources rn:hydra:oauth2:tokens \
        --subjects protected-api

The protected-api initializes the hydra SDK

 client, err = hydra.NewSDK(&hydra.Configuration{
		ClientID:     env.Getenv("CLIENT_ID", "demo"),
		ClientSecret: env.Getenv("CLIENT_SECRET", "demo"),
		EndpointURL:  env.Getenv("HYDRA_URL", "https://hydra:4444"),
		Scopes:       []string{"hydra.clients"},
	})

and then gets the token from the request with the function bearerTokenFromRequest® (copied from oathkeeper) and calls

introspection, response, err := client.IntrospectOAuth2Token(token, "")

When I check the logs, I see that both /oauth2/token and /oauth2/introspect are requested

time="2018-05-09T18:56:29Z" level=info msg="started handling request" method=POST remote="172.18.0.4:56826" request=/oauth2/token
time="2018-05-09T18:56:29Z" level=info msg="completed handling request" measure#https://localhost:9000.latency=80625203 method=POST remote="172.18.0.4:56826" request=/oauth2/token status=200 text_status=OK took=80.625203ms
time="2018-05-09T18:56:29Z" level=info msg="started handling request" method=POST remote="172.18.0.4:56826" request=/oauth2/introspect
time="2018-05-09T18:56:29Z" level=info msg="Access denied" error=invalid_scope reason="Token is expired, malformed or missing" request="&{rn:hydra:oauth2:tokens introspect map[]}" scopes="[hydra.introspect]"

i) Why is /oauth2/token requested before /oauth2/introspect?

ii) Why is access denied? I tried to add the scope “hydra.introspect” to the Scopes parameter in NewSDK(), but then I get the following error.

time="2018-05-09T19:19:01Z" level=info msg="started handling request" method=POST remote="172.18.0.4:57248" request=/oauth2/token
time="2018-05-09T19:19:02Z" level=error msg="An error occurred" debug="The client is not allowed to request scope hydra.introspect" error=invalid_scope

Anyone knows what I am missing? There are 2 posts related to this one, but they directly post to the /oauth2/introspect endpoint. I could try that to get it to work, but I would also like to understand why my approach with the SDK is failing.

Thanx for any help!

I had a similar issue. Use the credentials for the admin to get it to work.
The ones created by FORCE_ROOT_CLIENT_CREDENTIALS

docker run -d \
  --name ory-hydra-example--hydra \
  --network hydraguide \
  -p 9000:4444 \
  -e SYSTEM_SECRET=$SYSTEM_SECRET \
  -e DATABASE_URL=$DATABASE_URL \
  -e ISSUER=https://localhost:9000/ \
  -e CONSENT_URL=http://localhost:9020/consent \
  -e FORCE_ROOT_CLIENT_CREDENTIALS=admin:demo-password \
  oryd/hydra:v0.11.6

I do not know why adding the scope to a regular client does not work but maybe the maintainers could tell us.

Plus i see the client id and secret used to create the client with the hydra clients create command are different form the ones you use with the go SDK.

This does not make sense to me, the api should not need to know the admin credentials. Anyhow, I tried your suggestion, it did not work out:

time="2018-05-10T06:24:10Z" level=info msg="started handling request" method=POST remote="172.18.0.4:35034" request=/oauth2/token
time="2018-05-10T06:24:10Z" level=error msg="An error occurred" debug=": Not found" error=invalid_client

In the go SDK I use the same id’s and secrets as for client creation, they are passed by the CLIENT_ID and CLIENT_SECRET environment vars, the “demo” is not used. Thanx for sharing!

This will get easier with the 1.0.0 release, but to address your issue:

  1. Are you sure CLIENT_ID and CLIENT_SECRET are set up properly and do not fall back to demo?
  2. A token is requested because the SDK uses an access token to introspect your token. It’s possible to just use the client credentials but that causes Hydra to use BCrypt on every request which is really heavy on your CPU

ii) Why is access denied? I tried to add the scope “hydra.introspect” to the Scopes parameter in NewSDK(), but then I get the following error.

This indicates that you’re indeed not using the protected-api but some other client. Make sure that it’s indeed protected-api and that that client has the hydra.introspect scope by doing hydra clients get protected-api

Thanx for this hint! CLIENT_ID and CLIENT_SECRET were correct. However, the “client get” revealed that “hydra.introspect” was not set. The problem was the following syntax

--allowed-scopes hydra.clients, hydra.introspect \

it must read

--allowed-scopes hydra.clients,hydra.introspect \

Ooooh :joy:

Ahh, the spaces. Glad you got it working. Bash is a bit like yaml - add one extra space and everything breaks :wink:

Thanx again! A little suggestion: It would be helpful to have an option --wait-for-hydra (or similar, which would wait for the hydra port to be available) for the client/policy commands. Right now, when using docker-compose, I have only found a hacky solution for this. When running the setup of all the clients from within docker-compose, the client commands often are launched before the hydra host is ready to process them which leads to errors.

Or, as an alternative, include one of the available “wait-for” solutions into the ory/hydra dockerfile.

Hm, --retry <n> would make more sense IMO. But then again, what you want can easily be solved with native unix commands, for example:

Yep, this is true, I currently use a simple “sleep”, your solution is a nice onliner. But, it restricts to the …-alpine container. --retry would automatically be available in production, which can come handy when bootstrapping the system.

Ah yeah, restricting that to the alpine image is definitively a downside. Adding resilience to the CLI is something worth considering, but I think it won’t be tackled right away as there’s currently some major refactoring going on. But after that, it would make sense to check in with this again! It would be really helpful if you could open an issue on that topic so it’s tracked and doesn’t get lost!

1 Like

Perfect, thank you claus! :slight_smile: