p2-inc / keycloak-orgs

Single realm, multi-tenancy for SaaS apps
Other
362 stars 65 forks source link

Client Credentials Grant / machine-to-machine auth in organizations #163

Open martivo opened 5 months ago

martivo commented 5 months ago

Hello!

I am currently using Organizations successfully with "human" users, but now I have a need to authenticate machine-to-machine communication. I am strugling to figure out how to achieve this with organizations.

As far as I know, we are supposed to use clients(CLient authentication: ON, Auth flow: Service accounts flow). How could I place this client into an Organization? Or the service account created/used by this Client?

I am also unable to assign an organization role to a Client. Only the regular KK roles are availabe.

Has anyone done something like this or can point me to the right direction?

xgp commented 5 months ago

Great question. 2 ideas:

  1. The client service account that gets generated is an actual User. You should be able to use the normal organization API methods to assign organization membership and roles. However, I don't think you'll be able to "see" the service account User in the admin UI in order to do this.
  2. There is an organization admin User created when an organization is created. The purpose of this account is to create a "machine" account with full organization permissions. You'd have to use grant_type=password, but it would work in a similar way.
martivo commented 5 months ago

Great question. 2 ideas:

  1. The client service account that gets generated is an actual User. You should be able to use the normal organization API methods to assign organization membership and roles. However, I don't think you'll be able to "see" the service account User in the admin UI in order to do this.

I can confirm, the user is not visible. I can get to the user details page (Clients->...->Service accounts roles->service-account-sample-...) but in the user details there is no option to add it to an organization. And from the Organizations -> ... -> Members -> Add Member it does not show the Service account users.

Would it be possible to display these users in the "Add member" menu somehow? Or make the search find it when entering specific user ID?

I will try to add this service account user through the API calls.

  1. There is an organization admin User created when an organization is created. The purpose of this account is to create a "machine" account with full organization permissions. You'd have to use grant_type=password, but it would work in a similar way.

I think this would grant too many permissions? Since it is the admin user? I require fine grained control and multiple macine-to-macine accounts, each with specific set of permissions(roles) in the organization. I can confirm the "org-admin" user is visible and already a member of the organization.

martivo commented 5 months ago

I used curl -X PUT -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" https://HOST/realms/master/orgs/ORG_ID/members/USER_ID It did not result in an error. When I use an invalid USER_ID then it does show an error. So I assume this worked. curl -X GET -H "Authorization: Bearer $ACCESS_TOKEN" -H "Content-Type: application/json" https://HOST/realms/master/orgs/ORG_ID/members However this request will not display it. Hard to predict if it works or not. I will try to test if the organization parameters are present when authenticating.

However it is rather difficult to administrate it when even the API does not show it as a member. I wonder if I am missing some api parameter to include the service accounts in the results?

xgp commented 5 months ago

@martivo thank you for doing the validations.

Regarding the "org-admin" user, you are correct. It is meant to be an organization "superuser" that has all permissions within an organization.

I'll tag this as a feature request, as it would be nice to have the ability to add service accounts to organizations, it's currently encumbered by how Keycloak itself treats / "hides" those Users.

ljani commented 4 months ago

I was just researching the same topic. Thanks for all the pointers! Managing/debugging this through the UI would be helpful, but I'm glad it is already supported.

Finding the USER_ID was a little hard for me initially though, although @martivo explained it correctly. This can be found from Realm > Clients > CLIENTNAME > Service account roles tab > "click on the user name service-account-CLIENTNAME" > ID

I will try to test if the organization parameters are present when authenticating.

I did try this and I can confirm the organization roles do come through when using the organization role mapper. This should work for assigning the role:

curl -X PUT -H "Authorization: Bearer $ACCESS_TOKEN" https://HOST/realms/master/orgs/ORG_ID/roles/ROLE_NAME/users/USER_ID
martivo commented 4 months ago

I can confirm that the organization is present for the client when it's service account is added to the organization. It is also needed to configure mappers for each of the added clients(machine accounts). In the client scope->(*-dedicated)->Add mapper->By configuration->Organization Role. Since in my case the service account uses it's own client to get the token. This behaviour/configuration seems to be the default for loki, prometheus etc using oauth2 configuraton block.

But administrating this is a nightmare - the API won't tell you if it is a member or not. Unless there is some API request that would show these hidden users.

xgp commented 4 months ago

This method in the Keycloak Admin API gets a service account user for a specific client: https://www.keycloak.org/docs-api/23.0.6/rest-api/index.html#_get_adminrealmsrealmclientsidservice_account_user

GET /admin/realms/{realm}/clients/{id}/service-account-user
martivo commented 4 months ago

This method in the Keycloak Admin API gets a service account user for a specific client: https://www.keycloak.org/docs-api/23.0.6/rest-api/index.html#_get_adminrealmsrealmclientsidservice_account_user

GET /admin/realms/{realm}/clients/{id}/service-account-user

This is great for finding the user ID of the clients service account user. How could I check what service account users are the members of an organization?

xgp commented 4 months ago

the GET /:realm/orgs/:orgId/members method needs to be updated. I'll keep this issue open if someone wants to do that.

kedare commented 2 months ago

Hello I guess in term of code change, for the implementation to work we only have to remove the && u.getServiceAccountClientLink() == null from this method ?

https://github.com/p2-inc/keycloak-orgs/blob/f863e96ad37d7248c726f2193a8bcc6b22eba0d6/src/main/java/io/phasetwo/service/model/jpa/OrganizationAdapter.java#L198-L203

Thanks