Scope strictness for client_credentials flow

I have client configured as

 hydra clients get foo
{
	"grant_types": [
		"implicit",
		"refresh_token",
		"authorization_code",
		"password",
		"client_credentials"
	],
	"id": "foo",
	"public": true,
	"response_types": [
		"code",
		"id_token",
		"token"
	],
	"scope": "hydra.* openid offline defined1 defined2"
}

When i issue for token

curl -X POST \
  http://localhost:4444/oauth2/token \
  -H 'authorization: Basic YWRtaW46ZGVtby1wYXNzd29yZA==' \
  -H 'content-type: application/x-www-form-urlencoded' \
  -d 'grant_type=client_credentials&scope=hydra.fooo.bar.baz.zraz*%20openid%20offline%20hydra&client_id=foo&client_secret=foo'

I’m able to issue for token for any scope which falls into hydra.*
But my custom scopes can not be optained if those are outside of the hydra.*

curl -X POST \
  http://localhost:4444/oauth2/token \
  -H 'authorization: Basic YWRtaW46ZGVtby1wYXNzd29yZA==' \
  -H 'content-type: application/x-www-form-urlencoded' \
  -d 'grant_type=client_credentials&scope=defined1&client_id=foo&client_secret=foo'

I receive

{
    "error": "invalid_scope",
    "error_description": "The requested scope is invalid, unknown, or malformed",
    "status_code": 400
}

Just to mention i run the hydra with 5 minutes start up guide

hydra host --dangerous-auto-logon --dangerous-force-http --disable-telemetry

You are using differen client ids

Sorry i have pasted wrong request but this happens for client ‘foo’

The scope feature is working 100% as it’s passing all unit tests. The most likely cause for the behavior you’re experiencing is some mix up with client ids or scopes or request parameters.

Recheck everything, check the client settings, check the request parameters (I recommend using a library to make oauth2 requests), rerun everything.

Strange because this hydra is completely bar installation on docker according to (5 minutes guide)
Library is not an option for me a need a pure CURL
What version are you referring to 0.11.6 ? or any previuos ?

All versions of Hydra have rigid unit and integration tests. 99,9% of the times where something like this doesn’t work it’s due to some mixup in the request parameters. As seen in the original post, it was an issue there as well. Mixing this up is easy because it is so much text. Try to create a new client, make sure to use it has the right scopes, and request a token. Try using hydra help token client first to make sure that its working. Then use curl to do the same.

I spotted the error, you are using admin:demo-password in the authorization header. This should be the foo client. Remove client_id and client_secret from the request body. And again, please use a library so stuff like this doesn’t happen and you save yourself a ton of time debugging it.

Ok the point is different I’m using admin’s basic auth credentials - which causes token request to be issued for admin user - i checked it with validate api for generated token

The point is that when remove authorization header for admin i get 400’s


I create a client as

....
OAuth2 client id: one
OAuth2 client secret: APkfMS-a~PQ3o.9sKW2vAFocl3

/ # hydra clients get  one
{
	"grant_types": [
		"implicit",
		"refresh_token",
		"authorization_code",
		"password",
		"client_credentials"
	],
	"id": "one",
	"public": true,
	"response_types": [
		"code",
		"id_token",
		"token"
	],
	"scope": "foo"
}
/ 

Then i issue for token

curl -X POST \
  http://localhost:4444/oauth2/token \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/x-www-form-urlencoded' \
  -d 'grant_type=client_credentials&scope=foo&client_id=one&client_secret=APkfMS-a~PQ3o.9sKW2vAFocl3'

or

curl -X POST \
  http://localhost:4444/oauth2/token \
  -H 'authorization: Basic b25lOkFQa2ZNUy1hflBRM28uOXNLVzJ2QUZvY2wz' \
  -H 'cache-control: no-cache' \
  -H 'content-type: application/x-www-form-urlencoded' \
  -d 'grant_type=client_credentials&scope=foo&client_id=one&client_secret=APkfMS-a~PQ3o.9sKW2vAFocl3'

Regardless if
I get the

{
    "error": "invalid_grant",
    "error_description": "The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client",
    "status_code": 400
}

the same i can reproduce with my JS client code

const Hydra = require('ory-hydra-sdk')
const OAuth2 = require('simple-oauth2')

console.log("start ...")
// Set this to Hydra's URL
Hydra.ApiClient.instance.basePath = 'http://localhost:4444'




const scope = 'foo'


const oauth2 = OAuth2.create({
  client: {
    id: 'one',
    secret: 'APkfMS-a~PQ3o.9sKW2vAFocl3'
  },
  auth: {
    tokenHost: endpoint = 'http://localhost:4444',
    authorizePath: authorizePath = '/oauth2/auth',
    tokenPath: tokenPath = '/oauth2/token'
  },
  // These are important for simple-oauth2 to work properly.
  options: {
    useBodyAuth: true,
    useBasicAuthorizationHeader: false
  }
})

// Configure basic authorization
//Hydra.ApiClient.instance.authentications.basic.username = 'admin'
//Hydra.ApiClient.instance.authentications.basic.password = 'demo-password'

console.log("oauth" + JSON.stringify(oauth2))


// Next we need to fetch a token, let's wrap that in a
// function called refreshToken
const refreshToken = () => oauth2.clientCredentials
  .getToken({ scope })
  .then((result) => {    console.log("getting token .... "+token)
    const token = oauth2.accessToken.create(result);
    console.log("token .... "+token)
    const hydraClient = Hydra.ApiClient.instance
    hydraClient.authentications.oauth2.accessToken = token.token.access_token
    return Promise.resolve(token)
  })


refreshToken().then(() => {
  // It is important to note that must not return `hydra.listOAuth2Clients`
  // directly inside a Promise, otherwise you will encounter the "superagent double callback bug".

  const hydra = new Hydra.OAuth2Api()

  // for example, let's fetch all OAuth2 clients
  hydra.listOAuth2Clients((error, data, response) => {
    if (error) {
      // a network error occurred.
      throw error
    } else if (response.statusCode < 200 || response.statusCode >= 400) {
      // an application error occurred.
      throw new Error('Consent endpoint gave status code ' + response.statusCode + ', but status code 200 was expected.')
    }

    console.log(response) // a list of OAuth2 clients.
  })
})
.catch( err => {
        console.log(err) // a list of OAuth2 clients.

})

result

{ [Error: Bad Request]
  name: 'Error',
  status: 400,
  message: 'Bad Request',
  context: 
   { error: 'invalid_grant',
     error_description: 'The provided authorization grant (e.g., authorization code, resource owner credentials) or refresh token is invalid, expired, revoked, does not match the redirection URI used in the authorization request, or was issued to another client',
     status_code: 400 } }

Please do the following:

  1. Create a client using hydra clients create --id fooclient --secret foosecret -a myscope -g client_credentials -r token
  2. Make a token request CLIENT_ID=fooclient CLIENT_SECRET=foosecret hydra token client --scopes myscope

This will work. It’s tested and I even tested it manually to check my sanity.

It even works with curl, you are definitely mixing something up. Maybe you’re running two containers or whatever. Please check your environment, that’s all the help I can give you for now.

$ curl -X POST \
>   http://localhost:4444/oauth2/token \
>   -H 'authorization: Basic Zm9vY2xpZW50OmZvb3NlY3JldA==' \
>   -H 'content-type: application/x-www-form-urlencoded' \
>   -d 'grant_type=client_credentials&scope=myscope'
{"access_token":"Nc2PGk4AfvOtY7MFBPhR5vAsbRyhcw7NgKi-O8S8vE8.P4otH5QMIiKhx0Kgsv8qwKaTWdUG8KSyj3B6ymtH9Kg","expires_in":3600,"scope":"myscope","token_type":"bearer"}

Works !!!

1 Like

Ok i somehow sorted that out

First does not work

hydra clients create --id one --is-public  -g client_credentials -r token -a foo

where as second does

hydra clients create --id one              -g client_credentials -r token -a foo

The difference is ’ –is-public