Chapter 8. Authorization services
Chapter 8. Authorization services Red Hat build of Keycloak Authorization Services are built on top of well-known standards such as the OAuth2 and User-Managed Access specifications. OAuth2 clients (such as front end applications) can obtain access tokens from the server using the token endpoint and use these same tokens to access resources protected by a resource server (such as back end services). In the same way, Red Hat build of Keycloak Authorization Services provide extensions to OAuth2 to allow access tokens to be issued based on the processing of all policies associated with the resource(s) or scope(s) being requested. This means that resource servers can enforce access to their protected resources based on the permissions granted by the server and held by an access token. In Red Hat build of Keycloak Authorization Services the access token with permissions is called a Requesting Party Token or RPT for short. In addition to the issuance of RPTs, Red Hat build of Keycloak Authorization Services also provides a set of RESTful endpoints that allow resources servers to manage their protected resources, scopes, permissions and policies, helping developers to extend or integrate these capabilities into their applications in order to support fine-grained authorization. 8.1. Discovering authorization services endpoints and metadata Red Hat build of Keycloak provides a discovery document from which clients can obtain all necessary information to interact with Red Hat build of Keycloak Authorization Services, including endpoint locations and capabilities. The discovery document can be obtained from: curl -X GET \ http://USD{host}:USD{port}/realms/USD{realm}/.well-known/uma2-configuration Where USD{host}:USD{port} is the hostname (or IP address) and port where Red Hat build of Keycloak is running and USD{realm} is the name of a realm in Red Hat build of Keycloak. As a result, you should get a response as follows: { // some claims are expected here // these are the main claims in the discovery document about Authorization Services endpoints location "token_endpoint": "http://USD{host}:USD{port}/realms/USD{realm}/protocol/openid-connect/token", "token_introspection_endpoint": "http://USD{host}:USD{port}/realms/USD{realm}/protocol/openid-connect/token/introspect", "resource_registration_endpoint": "http://USD{host}:USD{port}/realms/USD{realm}/authz/protection/resource_set", "permission_endpoint": "http://USD{host}:USD{port}/realms/USD{realm}/authz/protection/permission", "policy_endpoint": "http://USD{host}:USD{port}/realms/USD{realm}/authz/protection/uma-policy" } Each of these endpoints expose a specific set of capabilities: token_endpoint A OAuth2-compliant Token Endpoint that supports the urn:ietf:params:oauth:grant-type:uma-ticket grant type. Through this endpoint, clients can send authorization requests and obtain an RPT with all permissions granted by Red Hat build of Keycloak. token_introspection_endpoint A OAuth2-compliant Token Introspection Endpoint which clients can use to query the server to determine the active state of an RPT and to determine any other information associated with the token, such as the permissions granted by Red Hat build of Keycloak. resource_registration_endpoint A UMA-compliant Resource Registration Endpoint which resource servers can use to manage their protected resources and scopes. This endpoint provides operations create, read, update and delete resources and scopes in Red Hat build of Keycloak. permission_endpoint A UMA-compliant Permission Endpoint which resource servers can use to manage permission tickets. This endpoint provides operations create, read, update, and delete permission tickets in Red Hat build of Keycloak. 8.2. Obtaining permissions To obtain permissions from Red Hat build of Keycloak you send an authorization request to the token endpoint. As a result, Red Hat build of Keycloak will evaluate all policies associated with the resource(s) and scope(s) being requested and issue an RPT with all permissions granted by the server. Clients are allowed to send authorization requests to the token endpoint using the following parameters: grant_type This parameter is required . Must be urn:ietf:params:oauth:grant-type:uma-ticket . ticket This parameter is optional . The most recent permission ticket received by the client as part of the UMA authorization process. claim_token This parameter is optional . A string representing additional claims that should be considered by the server when evaluating permissions for the resource(s) and scope(s) being requested. This parameter allows clients to push claims to Red Hat build of Keycloak. For more details about all supported token formats see claim_token_format parameter. claim_token_format This parameter is optional . A string indicating the format of the token specified in the claim_token parameter. Red Hat build of Keycloak supports two token formats: urn:ietf:params:oauth:token-type:jwt and https://openid.net/specs/openid-connect-core-1_0.html#IDToken . The urn:ietf:params:oauth:token-type:jwt format indicates that the claim_token parameter references an access token. The https://openid.net/specs/openid-connect-core-1_0.html#IDToken indicates that the claim_token parameter references an OpenID Connect ID Token. rpt This parameter is optional . A previously issued RPT whose permissions should also be evaluated and added in a new one. This parameter allows clients in possession of an RPT to perform incremental authorization where permissions are added on demand. permission This parameter is optional . A string representing a set of one or more resources and scopes the client is seeking access. This parameter can be defined multiple times in order to request permission for multiple resources and scopes. This parameter is an extension to urn:ietf:params:oauth:grant-type:uma-ticket grant type in order to allow clients to send authorization requests without a permission ticket. The format of the string must be: RESOURCE_ID#SCOPE_ID . For instance: Resource A#Scope A , Resource A#Scope A, Scope B, Scope C , Resource A , #Scope A . permission_resource_format This parameter is optional . A string representing a format indicating the resource in the permission parameter. Possible values are id and uri . id indicates the format is RESOURCE_ID . uri indicates the format is URI . If not specified, the default is id . permission_resource_matching_uri This parameter is optional . A boolean value that indicates whether to use path matching when representing resources in URI format in the permission parameter. If not specified, the default is false. audience This parameter is optional . The client identifier of the resource server to which the client is seeking access. This parameter is mandatory in case the permission parameter is defined. It serves as a hint to Red Hat build of Keycloak to indicate the context in which permissions should be evaluated. response_include_resource_name This parameter is optional . A boolean value indicating to the server whether resource names should be included in the RPT's permissions. If false, only the resource identifier is included. response_permissions_limit This parameter is optional . An integer N that defines a limit for the amount of permissions an RPT can have. When used together with an rpt parameter, only the last N requested permissions will be kept in the RPT. submit_request This parameter is optional . A boolean value indicating whether the server should create permission requests to the resources and scopes referenced by a permission ticket. This parameter only has effect if used together with the ticket parameter as part of a UMA authorization process. response_mode This parameter is optional . A string value indicating how the server should respond to authorization requests. This parameter is specially useful when you are mainly interested in either the overall decision or the permissions granted by the server, instead of a standard OAuth2 response. Possible values are: decision Indicates that responses from the server should only represent the overall decision by returning a JSON with the following format: { 'result': true } If the authorization request does not map to any permission, a 403 HTTP status code is returned instead. permissions Indicates that responses from the server should contain any permission granted by the server by returning a JSON with the following format: [ { 'rsid': 'My Resource' 'scopes': ['view', 'update'] }, ... ] If the authorization request does not map to any permission, a 403 HTTP status code is returned instead. Example of an authorization request when a client is seeking access to two resources protected by a resource server. curl -X POST \ http://USD{host}:USD{port}/realms/USD{realm}/protocol/openid-connect/token \ -H "Authorization: Bearer USD{access_token}" \ --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \ --data "audience={resource_server_client_id}" \ --data "permission=Resource A#Scope A" \ --data "permission=Resource B#Scope B" Example of an authorization request when a client is seeking access to any resource and scope protected by a resource server. NOTE: This will not evaluate the permissions for all resources. Instead, the permissions for resources owned by the resource server, owned by the requesting user, and explicitly granted to the requesting user by other owners are evaluated. curl -X POST \ http://USD{host}:USD{port}/realms/USD{realm}/protocol/openid-connect/token \ -H "Authorization: Bearer USD{access_token}" \ --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \ --data "audience={resource_server_client_id}" Example of an authorization request when a client is seeking access to a UMA protected resource after receiving a permission ticket from the resource server as part of the authorization process: curl -X POST \ http://USD{host}:USD{port}/realms/USD{realm}/protocol/openid-connect/token \ -H "Authorization: Bearer USD{access_token}" \ --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \ --data "ticket=USD{permission_ticket} If Red Hat build of Keycloak assessment process results in issuance of permissions, it issues the RPT with which it has associated the permissions: Red Hat build of Keycloak responds to the client with the RPT HTTP/1.1 200 OK Content-Type: application/json ... { "access_token": "USD{rpt}", } The response from the server is just like any other response from the token endpoint when using some other grant type. The RPT can be obtained from the access_token response parameter. If the client is not authorized, Red Hat build of Keycloak responds with a 403 HTTP status code: Red Hat build of Keycloak denies the authorization request HTTP/1.1 403 Forbidden Content-Type: application/json ... { "error": "access_denied", "error_description": "request_denied" } 8.2.1. Client authentication methods Clients need to authenticate to the token endpoint in order to obtain an RPT. When using the urn:ietf:params:oauth:grant-type:uma-ticket grant type, clients can use any of these authentication methods: Bearer Token Clients should send an access token as a Bearer credential in an HTTP Authorization header to the token endpoint. Example: an authorization request using an access token to authenticate to the token endpoint curl -X POST \ http://USD{host}:USD{port}/realms/USD{realm}/protocol/openid-connect/token \ -H "Authorization: Bearer USD{access_token}" \ --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" This method is especially useful when the client is acting on behalf of a user. In this case, the bearer token is an access token previously issued by Red Hat build of Keycloak to some client acting on behalf of a user (or on behalf of itself). Permissions will be evaluated considering the access context represented by the access token. For instance, if the access token was issued to Client A acting on behalf of User A, permissions will be granted depending on the resources and scopes to which User A has access. Client Credentials Clients can use any of the client authentication methods supported by Red Hat build of Keycloak. For instance, client_id/client_secret or JWT. Example: an authorization request using client id and client secret to authenticate to the token endpoint curl -X POST \ http://USD{host}:USD{port}/realms/USD{realm}/protocol/openid-connect/token \ -H "Authorization: Basic cGhvdGg6L7Jl13RmfWgtkk==pOnNlY3JldA==" \ --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" 8.2.2. Pushing claims When obtaining permissions from the server you can push arbitrary claims in order to have these claims available to your policies when evaluating permissions. If you are obtaining permissions from the server without using a permission ticket (UMA flow), you can send an authorization request to the token endpoint as follows: curl -X POST \ http://USD{host}:USD{port}/realms/USD{realm}/protocol/openid-connect/token \ --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \ --data "claim_token=ewogICAib3JnYW5pemF0aW9uIjogWyJhY21lIl0KfQ==" \ --data "claim_token_format=urn:ietf:params:oauth:token-type:jwt" \ --data "client_id={resource_server_client_id}" \ --data "client_secret={resource_server_client_secret}" \ --data "audience={resource_server_client_id}" The claim_token parameter expects a BASE64 encoded JSON with a format similar to the example below: { "organization" : ["acme"] } The format expects one or more claims where the value for each claim must be an array of strings. 8.2.2.1. Pushing claims Using UMA For more details about how to push claims when using UMA and permission tickets, please take a look at Permission API 8.3. User-managed access Red Hat build of Keycloak Authorization Services is based on User-Managed Access or UMA for short. UMA is a specification that enhances OAuth2 capabilities in the following ways: Privacy Nowadays, user privacy is becoming a huge concern, as more and more data and devices are available and connected to the cloud. With UMA and Red Hat build of Keycloak, resource servers can enhance their capabilities in order to improve how their resources are protected in respect to user privacy where permissions are granted based on policies defined by the user. Party-to-Party Authorization Resource owners (e.g.: regular end-users) can manage access to their resources and authorize other parties (e.g: regular end-users) to access these resources. This is different from OAuth2 where consent is given to a client application acting on behalf of a user, with UMA resource owners are allowed to consent access to other users, in a completely asynchronous manner. Resource Sharing Resource owners are allowed to manage permissions to their resources and decide who can access a particular resource and how. Red Hat build of Keycloak can then act as a sharing management service from which resource owners can manage their resources. Red Hat build of Keycloak is a UMA 2.0 compliant authorization server that provides most UMA capabilities. As an example, consider a user Alice (resource owner) using an Internet Banking Service (resource server) to manage her Bank Account (resource). One day, Alice decides to open her bank account to Bob (requesting party), an accounting professional. However, Bob should only have access to view (scope) Alice's account. As a resource server, the Internet Banking Service must be able to protect Alice's Bank Account. For that, it relies on Red Hat build of Keycloak Resource Registration Endpoint to create a resource in the server representing Alice's Bank Account. At this moment, if Bob tries to access Alice's Bank Account, access will be denied. The Internet Banking Service defines a few default policies for banking accounts. One of them is that only the owner, in this case Alice, is allowed to access her bank account. However, Internet Banking Service in respect to Alice's privacy also allows her to change specific policies for the banking account. One of these policies that she can change is to define which people are allowed to view her bank account. For that, Internet Banking Service relies on Red Hat build of Keycloak to provide to Alice a space where she can select individuals and the operations (or data) they are allowed to access. At any time, Alice can revoke access or grant additional permissions to Bob. 8.3.1. Authorization process In UMA, the authorization process starts when a client tries to access a UMA protected resource server. A UMA protected resource server expects a bearer token in the request where the token is an RPT. When a client requests a resource at the resource server without an RPT: Client requests a protected resource without sending an RPT curl -X GET \ http://USD{host}:USD{port}/my-resource-server/resource/1bfdfe78-a4e1-4c2d-b142-fc92b75b986f The resource server sends a response back to the client with a permission ticket and an as_uri parameter with the location of a Red Hat build of Keycloak server to where the ticket should be sent in order to obtain an RPT. Resource server responds with a permission ticket HTTP/1.1 401 Unauthorized WWW-Authenticate: UMA realm="USD{realm}", as_uri="https://USD{host}:USD{port}/realms/USD{realm}", ticket="016f84e8-f9b9-11e0-bd6f-0021cc6004de" The permission ticket is a special type of token issued by Red Hat build of Keycloak Permission API. They represent the permissions being requested (e.g.: resources and scopes) as well any other information associated with the request. Only resource servers are allowed to create those tokens. Now that the client has a permission ticket and also the location of a Red Hat build of Keycloak server, the client can use the discovery document to obtain the location of the token endpoint and send an authorization request. Client sends an authorization request to the token endpoint to obtain an RPT curl -X POST \ http://USD{host}:USD{port}/realms/USD{realm}/protocol/openid-connect/token \ -H "Authorization: Bearer USD{access_token}" \ --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \ --data "ticket=USD{permission_ticket} If Red Hat build of Keycloak assessment process results in issuance of permissions, it issues the RPT with which it has associated the permissions: Red Hat build of Keycloak responds to the client with the RPT HTTP/1.1 200 OK Content-Type: application/json ... { "access_token": "USD{rpt}", } The response from the server is just like any other response from the token endpoint when using some other grant type. The RPT can be obtained from the access_token response parameter. In case the client is not authorized to have permissions Red Hat build of Keycloak responds with a 403 HTTP status code: Red Hat build of Keycloak denies the authorization request HTTP/1.1 403 Forbidden Content-Type: application/json ... { "error": "access_denied", "error_description": "request_denied" } 8.3.2. Submitting permission requests As part of the authorization process, clients need first to obtain a permission ticket from a UMA protected resource server in order to exchange it with an RPT at the Red Hat build of Keycloak Token Endpoint. By default, Red Hat build of Keycloak responds with a 403 HTTP status code and a request_denied error in case the client can not be issued with an RPT. Red Hat build of Keycloak denies the authorization request HTTP/1.1 403 Forbidden Content-Type: application/json ... { "error": "access_denied", "error_description": "request_denied" } Such response implies that Red Hat build of Keycloak could not issue an RPT with the permissions represented by a permission ticket. In some situations, client applications may want to start an asynchronous authorization flow and let the owner of the resources being requested decide whether or not access should be granted. For that, clients can use the submit_request request parameter along with an authorization request to the token endpoint: curl -X POST \ http://USD{host}:USD{port}/realms/USD{realm}/protocol/openid-connect/token \ -H "Authorization: Bearer USD{access_token}" \ --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" \ --data "ticket=USD{permission_ticket} \ --data "submit_request=true" When using the submit_request parameter, Red Hat build of Keycloak will persist a permission request for each resource to which access was denied. Once created, resource owners can check their account and manage their permissions requests. You can think about this functionality as a Request Access button in your application, where users can ask other users for access to their resources. 8.3.3. Managing access to users resources Users can manage access to their resources using the Red Hat build of Keycloak Account Console. To enable this functionality, you must first enable User-Managed Access for your realm. Procedure Log into the Admin Console. Click Realm Settings in the menu. Toggle User-Managed Access to ON . Click the user name at the top right of the Admin Console and select Manage Account . Click My Resources in the menu option. A page displays with the following options. Manage My resources This section contains a list of all resources owned by the user. Users can click on a resource for more details and share the resource with others. When permission requests are awaiting approval, an icon is put to the name of the resource. These requests are connected to the parties (users) requesting access to a particular resource. Users are allowed to approve or deny these requests. You can do so by clicking the icon Manage Resources shared with me This section contains a list of all resources shared with the user. Manage People with access to this resource This section contains a list of people with access to this resource. Users are allowed to revoke access by clicking on the Revoke button or by removing a specific Permission . Share the resource with others By typing the username or e-mail of another user, the user is able to share the resource and select the permissions he wants to grant access. 8.4. Protection API The Protection API provides a UMA-compliant set of endpoints providing: Resource Management With this endpoint, resource servers can manage their resources remotely and enable policy enforcers to query the server for the resources that need protection. Permission Management In the UMA protocol, resource servers access this endpoint to create permission tickets. Red Hat build of Keycloak also provides endpoints to manage the state of permissions and query permissions. Policy API Red Hat build of Keycloak leverages the UMA Protection API to allow resource servers to manage permissions for their users. In addition to the Resource and Permission APIs, Red Hat build of Keycloak provides a Policy API from where permissions can be set to resources by resource servers on behalf of their users. An important requirement for this API is that only resource servers are allowed to access its endpoints using a special OAuth2 access token called a protection API token (PAT). In UMA, a PAT is a token with the scope uma_protection . 8.4.1. What is a PAT and how to obtain it A protection API token (PAT) is a special OAuth2 access token with a scope defined as uma_protection . When you create a resource server, Red Hat build of Keycloak automatically creates a role, uma_protection , for the corresponding client application and associates it with the client's service account. Service Account granted with uma_protection role Resource servers can obtain a PAT from Red Hat build of Keycloak like any other OAuth2 access token. For example, using curl: curl -X POST \ -H "Content-Type: application/x-www-form-urlencoded" \ -d 'grant_type=client_credentials&client_id=USD{client_id}&client_secret=USD{client_secret}' \ "http://USD{host}:USD{port}/realms/USD{realm_name}/protocol/openid-connect/token" The example above is using the client_credentials grant type to obtain a PAT from the server. As a result, the server returns a response similar to the following: { "access_token": USD{PAT}, "expires_in": 300, "refresh_expires_in": 1800, "refresh_token": USD{refresh_token}, "token_type": "bearer", "id_token": USD{id_token}, "not-before-policy": 0, "session_state": "ccea4a55-9aec-4024-b11c-44f6f168439e" } Note Red Hat build of Keycloak can authenticate your client application in different ways. For simplicity, the client_credentials grant type is used here, which requires a client_id and a client_secret . You can choose to use any supported authentication method. 8.4.2. Managing resources Resource servers can manage their resources remotely using a UMA-compliant endpoint. This endpoint provides operations outlined as follows (entire path omitted for clarity): Create resource set description: POST /resource_set Read resource set description: GET /resource_set/{_id} Update resource set description: PUT /resource_set/{_id} Delete resource set description: DELETE /resource_set/{_id} List resource set descriptions: GET /resource_set For more information about the contract for each of these operations, see UMA Resource Registration API . 8.4.2.1. Creating a resource To create a resource you must send an HTTP POST request as follows: curl -v -X POST \ http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set \ -H 'Authorization: Bearer 'USDpat \ -H 'Content-Type: application/json' \ -d '{ "name":"Tweedl Social Service", "type":"http://www.example.com/rsrcs/socialstream/140-compatible", "icon_uri":"http://www.example.com/icons/sharesocial.png", "resource_scopes":[ "read-public", "post-updates", "read-private", "http://www.example.com/scopes/all" ] }' By default, the owner of a resource is the resource server. If you want to define a different owner, such as a specific user, you can send a request as follows: curl -v -X POST \ http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set \ -H 'Authorization: Bearer 'USDpat \ -H 'Content-Type: application/json' \ -d '{ "name":"Alice Resource", "owner": "alice" }' Where the property owner can be set with the username or the identifier of the user. 8.4.2.2. Creating user-managed resources By default, resources created via Protection API can not be managed by resource owners through the Account Console . To create resources and allow resource owners to manage these resources, you must set ownerManagedAccess property as follows: curl -v -X POST \ http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set \ -H 'Authorization: Bearer 'USDpat \ -H 'Content-Type: application/json' \ -d '{ "name":"Alice Resource", "owner": "alice", "ownerManagedAccess": true }' 8.4.2.3. Updating resources To update an existing resource, send an HTTP PUT request as follows: curl -v -X PUT \ http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set/{resource_id} \ -H 'Authorization: Bearer 'USDpat \ -H 'Content-Type: application/json' \ -d '{ "_id": "Alice Resource", "name":"Alice Resource", "resource_scopes": [ "read" ] }' 8.4.2.4. Deleting resources To delete an existing resource, send an HTTP DELETE request as follows: curl -v -X DELETE \ http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set/{resource_id} \ -H 'Authorization: Bearer 'USDpat 8.4.2.5. Querying resources To query the resources by id , send an HTTP GET request as follows: http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set/{resource_id} To query resources given a name , send an HTTP GET request as follows: http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set?name=Alice Resource By default, the name filter will match any resource with the given pattern. To restrict the query to only return resources with an exact match, use: http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set?name=Alice Resource&exactName=true To query resources given an uri , send an HTTP GET request as follows: http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set?uri=/api/alice To query resources given an owner , send an HTTP GET request as follows: http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set?owner=alice To query resources given an type , send an HTTP GET request as follows: http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set?type=albums To query resources given an scope , send an HTTP GET request as follows: http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set?scope=read When querying the server for permissions use parameters first and max results to limit the result. 8.4.3. Managing permission requests Resource servers using the UMA protocol can use a specific endpoint to manage permission requests. This endpoint provides a UMA-compliant flow for registering permission requests and obtaining a permission ticket. A permission ticket is a special security token type representing a permission request. Per the UMA specification, a permission ticket is: A correlation handle that is conveyed from an authorization server to a resource server, from a resource server to a client, and ultimately from a client back to an authorization server, to enable the authorization server to assess the correct policies to apply to a request for authorization data. In most cases, you do not need to deal with this endpoint directly. Red Hat build of Keycloak provides a policy enforcer that enables UMA for your resource server so it can obtain a permission ticket from the authorization server, return this ticket to client application, and enforce authorization decisions based on a final requesting party token (RPT). The process of obtaining permission tickets from Red Hat build of Keycloak is performed by resource servers and not regular client applications, where permission tickets are obtained when a client tries to access a protected resource without the necessary grants to access the resource. The issuance of permission tickets is an important aspects when using UMA as it allows resource servers to: Abstract from clients the data associated with the resources protected by the resource server Register in the Red Hat build of Keycloak authorization requests which in turn can be used later in workflows to grant access based on the resource's owner consent Decouple resource servers from authorization servers and allow them to protect and manage their resources using different authorization servers Client wise, a permission ticket has also important aspects that its worthy to highlight: Clients don't need to know about how authorization data is associated with protected resources. A permission ticket is completely opaque to clients. Clients can have access to resources on different resource servers and protected by different authorization servers These are just some of the benefits brought by UMA where other aspects of UMA are strongly based on permission tickets, specially regarding privacy and user controlled access to their resources. 8.4.3.1. Creating permission ticket To create a permission ticket, send an HTTP POST request as follows: curl -X POST \ http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/permission \ -H 'Authorization: Bearer 'USDpat \ -H 'Content-Type: application/json' \ -d '[ { "resource_id": "{resource_id}", "resource_scopes": [ "view" ] } ]' When creating tickets you can also push arbitrary claims and associate these claims with the ticket: curl -X POST \ http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/permission \ -H 'Authorization: Bearer 'USDpat \ -H 'Content-Type: application/json' \ -d '[ { "resource_id": "{resource_id}", "resource_scopes": [ "view" ], "claims": { "organization": ["acme"] } } ]' Where these claims will be available to your policies when evaluating permissions for the resource and scope(s) associated with the permission ticket. 8.4.3.2. Other non UMA-compliant endpoints 8.4.3.2.1. Creating permission ticket To grant permissions for a specific resource with id {resource_id} to a user with id {user_id}, as an owner of the resource send an HTTP POST request as follows: curl -X POST \ http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/permission/ticket \ -H 'Authorization: Bearer 'USDaccess_token \ -H 'Content-Type: application/json' \ -d '{ "resource": "{resource_id}", "requester": "{user_id}", "granted": true, "scopeName": "view" }' 8.4.3.2.2. Getting permission tickets curl http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/permission/ticket \ -H 'Authorization: Bearer 'USDaccess_token You can use any of these query parameters: scopeId resourceId owner requester granted returnNames first max 8.4.3.2.3. Updating permission ticket curl -X PUT \ http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/permission/ticket \ -H 'Authorization: Bearer 'USDaccess_token \ -H 'Content-Type: application/json' \ -d '{ "id": "{ticket_id}" "resource": "{resource_id}", "requester": "{user_id}", "granted": false, "scopeName": "view" }' 8.4.3.2.4. Deleting permission ticket curl -X DELETE http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/permission/ticket/{ticket_id} \ -H 'Authorization: Bearer 'USDaccess_token 8.4.4. Managing resource permissions using the Policy API Red Hat build of Keycloak leverages the UMA Protection API to allow resource servers to manage permissions for their users. In addition to the Resource and Permission APIs, Red Hat build of Keycloak provides a Policy API from where permissions can be set to resources by resource servers on behalf of their users. The Policy API is available at: This API is protected by a bearer token that must represent a consent granted by the user to the resource server to manage permissions on his behalf. The bearer token can be a regular access token obtained from the token endpoint using: Resource Owner Password Credentials Grant Type Token Exchange, in order to exchange an access token granted to some client (public client) for a token where audience is the resource server 8.4.4.1. Associating a permission with a resource To associate a permission with a specific resource you must send a HTTP POST request as follows: curl -X POST \ http://localhost:8180/realms/photoz/authz/protection/uma-policy/{resource_id} \ -H 'Authorization: Bearer 'USDaccess_token \ -H 'Cache-Control: no-cache' \ -H 'Content-Type: application/json' \ -d '{ "name": "Any people manager", "description": "Allow access to any people manager", "scopes": ["read"], "roles": ["people-manager"] }' In the example above we are creating and associating a new permission to a resource represented by resource_id where any user with a role people-manager should be granted with the read scope. You can also create policies using other access control mechanisms, such as using groups: curl -X POST \ http://localhost:8180/realms/photoz/authz/protection/uma-policy/{resource_id} \ -H 'Authorization: Bearer 'USDaccess_token \ -H 'Cache-Control: no-cache' \ -H 'Content-Type: application/json' \ -d '{ "name": "Any people manager", "description": "Allow access to any people manager", "scopes": ["read"], "groups": ["/Managers/People Managers"] }' Or a specific client: curl -X POST \ http://localhost:8180/realms/photoz/authz/protection/uma-policy/{resource_id} \ -H 'Authorization: Bearer 'USDaccess_token \ -H 'Cache-Control: no-cache' \ -H 'Content-Type: application/json' \ -d '{ "name": "Any people manager", "description": "Allow access to any people manager", "scopes": ["read"], "clients": ["my-client"] }' Or even using a custom policy using JavaScript: curl -X POST \ http://localhost:8180/realms/photoz/authz/protection/uma-policy/{resource_id} \ -H 'Authorization: Bearer 'USDaccess_token \ -H 'Cache-Control: no-cache' \ -H 'Content-Type: application/json' \ -d '{ "name": "Any people manager", "description": "Allow access to any people manager", "scopes": ["read"], "condition": "my-deployed-script.js" }' It is also possible to set any combination of these access control mechanisms. To update an existing permission, send an HTTP PUT request as follows: curl -X PUT \ http://localhost:8180/realms/photoz/authz/protection/uma-policy/{permission_id} \ -H 'Authorization: Bearer 'USDaccess_token \ -H 'Content-Type: application/json' \ -d '{ "id": "21eb3fed-02d7-4b5a-9102-29f3f09b6de2", "name": "Any people manager", "description": "Allow access to any people manager", "type": "uma", "scopes": [ "album:view" ], "logic": "POSITIVE", "decisionStrategy": "UNANIMOUS", "owner": "7e22131a-aa57-4f5f-b1db-6e82babcd322", "roles": [ "user" ] }' 8.4.4.2. Removing a permission To remove a permission associated with a resource, send an HTTP DELETE request as follows: curl -X DELETE \ http://localhost:8180/realms/photoz/authz/protection/uma-policy/{permission_id} \ -H 'Authorization: Bearer 'USDaccess_token 8.4.4.3. Querying permission To query the permissions associated with a resource, send an HTTP GET request as follows: To query the permissions given its name, send an HTTP GET request as follows: http://USD{host}:USD{port}/realms/USD{realm}/authz/protection/uma-policy?name=Any people manager To query the permissions associated with a specific scope, send an HTTP GET request as follows: To query all permissions, send an HTTP GET request as follows: When querying the server for permissions use parameters first and max results to limit the result. 8.5. Requesting party token A requesting party token (RPT) is a JSON web token (JWT) digitally signed using JSON web signature (JWS) . The token is built based on the OAuth2 access token previously issued by Red Hat build of Keycloak to a specific client acting on behalf of a user or on its own behalf. When you decode an RPT, you see a payload similar to the following: { "authorization": { "permissions": [ { "resource_set_id": "d2fe9843-6462-4bfc-baba-b5787bb6e0e7", "resource_set_name": "Hello World Resource" } ] }, "jti": "d6109a09-78fd-4998-bf89-95730dfd0892-1464906679405", "exp": 1464906971, "nbf": 0, "iat": 1464906671, "sub": "f1888f4d-5172-4359-be0c-af338505d86c", "typ": "kc_ett", "azp": "hello-world-authz-service" } From this token you can obtain all permissions granted by the server from the permissions claim. Also note that permissions are directly related with the resources/scopes you are protecting and completely decoupled from the access control methods that were used to actually grant and issue these same permissions. 8.5.1. Introspecting a requesting party token Sometimes you might want to introspect a requesting party token (RPT) to check its validity or obtain the permissions within the token to enforce authorization decisions on the resource server side. There are two main use cases where token introspection can help you: When client applications need to query the token validity to obtain a new one with the same or additional permissions When enforcing authorization decisions at the resource server side, especially when none of the built-in policy enforcers fits your application 8.5.2. Obtaining Information about an RPT The token introspection is essentially a OAuth2 token introspection -compliant endpoint from which you can obtain information about an RPT. To introspect an RPT using this endpoint, you can send a request to the server as follows: curl -X POST \ -H "Authorization: Basic aGVsbG8td29ybGQtYXV0aHotc2VydmljZTpzZWNyZXQ=" \ -H "Content-Type: application/x-www-form-urlencoded" \ -d 'token_type_hint=requesting_party_token&token=USD{RPT}' \ "http://localhost:8080/realms/hello-world-authz/protocol/openid-connect/token/introspect" Note The request above is using HTTP BASIC and passing the client's credentials (client ID and secret) to authenticate the client attempting to introspect the token, but you can use any other client authentication method supported by Red Hat build of Keycloak. The introspection endpoint expects two parameters: token_type_hint Use requesting_party_token as the value for this parameter, which indicates that you want to introspect an RPT. token Use the token string as it was returned by the server during the authorization process as the value for this parameter. As a result, the server response is: { "permissions": [ { "resource_id": "90ccc6fc-b296-4cd1-881e-089e1ee15957", "resource_name": "Hello World Resource" } ], "exp": 1465314139, "nbf": 0, "iat": 1465313839, "aud": "hello-world-authz-service", "active": true } If the RPT is not active, this response is returned instead: { "active": false } 8.5.3. Do I need to invoke the server every time I want to introspect an RPT? No. Just like a regular access token issued by a Red Hat build of Keycloak server, RPTs also use the JSON web token (JWT) specification as the default format. If you want to validate these tokens without a call to the remote introspection endpoint, you can decode the RPT and query for its validity locally. Once you decode the token, you can also use the permissions within the token to enforce authorization decisions. This is essentially what the policy enforcers do. Be sure to: Validate the signature of the RPT (based on the realm's public key) Query for token validity based on its exp , iat , and aud claims Additional resources JSON web token (JWT)
curl -X GET http://USD{host}:USD{port}/realms/USD{realm}/.well-known/uma2-configuration { // some claims are expected here // these are the main claims in the discovery document about Authorization Services endpoints location "token_endpoint": "http://USD{host}:USD{port}/realms/USD{realm}/protocol/openid-connect/token", "token_introspection_endpoint": "http://USD{host}:USD{port}/realms/USD{realm}/protocol/openid-connect/token/introspect", "resource_registration_endpoint": "http://USD{host}:USD{port}/realms/USD{realm}/authz/protection/resource_set", "permission_endpoint": "http://USD{host}:USD{port}/realms/USD{realm}/authz/protection/permission", "policy_endpoint": "http://USD{host}:USD{port}/realms/USD{realm}/authz/protection/uma-policy" } { 'result': true } [ { 'rsid': 'My Resource' 'scopes': ['view', 'update'] }, ] curl -X POST http://USD{host}:USD{port}/realms/USD{realm}/protocol/openid-connect/token -H "Authorization: Bearer USD{access_token}" --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" --data "audience={resource_server_client_id}" --data "permission=Resource A#Scope A" --data "permission=Resource B#Scope B" curl -X POST http://USD{host}:USD{port}/realms/USD{realm}/protocol/openid-connect/token -H "Authorization: Bearer USD{access_token}" --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" --data "audience={resource_server_client_id}" curl -X POST http://USD{host}:USD{port}/realms/USD{realm}/protocol/openid-connect/token -H "Authorization: Bearer USD{access_token}" --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" --data "ticket=USD{permission_ticket} HTTP/1.1 200 OK Content-Type: application/json { "access_token": "USD{rpt}", } HTTP/1.1 403 Forbidden Content-Type: application/json { "error": "access_denied", "error_description": "request_denied" } curl -X POST http://USD{host}:USD{port}/realms/USD{realm}/protocol/openid-connect/token -H "Authorization: Bearer USD{access_token}" --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" curl -X POST http://USD{host}:USD{port}/realms/USD{realm}/protocol/openid-connect/token -H "Authorization: Basic cGhvdGg6L7Jl13RmfWgtkk==pOnNlY3JldA==" --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" curl -X POST http://USD{host}:USD{port}/realms/USD{realm}/protocol/openid-connect/token --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" --data "claim_token=ewogICAib3JnYW5pemF0aW9uIjogWyJhY21lIl0KfQ==" --data "claim_token_format=urn:ietf:params:oauth:token-type:jwt" --data "client_id={resource_server_client_id}" --data "client_secret={resource_server_client_secret}" --data "audience={resource_server_client_id}" { "organization" : ["acme"] } curl -X GET http://USD{host}:USD{port}/my-resource-server/resource/1bfdfe78-a4e1-4c2d-b142-fc92b75b986f HTTP/1.1 401 Unauthorized WWW-Authenticate: UMA realm="USD{realm}", as_uri="https://USD{host}:USD{port}/realms/USD{realm}", ticket="016f84e8-f9b9-11e0-bd6f-0021cc6004de" curl -X POST http://USD{host}:USD{port}/realms/USD{realm}/protocol/openid-connect/token -H "Authorization: Bearer USD{access_token}" --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" --data "ticket=USD{permission_ticket} HTTP/1.1 200 OK Content-Type: application/json { "access_token": "USD{rpt}", } HTTP/1.1 403 Forbidden Content-Type: application/json { "error": "access_denied", "error_description": "request_denied" } HTTP/1.1 403 Forbidden Content-Type: application/json { "error": "access_denied", "error_description": "request_denied" } curl -X POST http://USD{host}:USD{port}/realms/USD{realm}/protocol/openid-connect/token -H "Authorization: Bearer USD{access_token}" --data "grant_type=urn:ietf:params:oauth:grant-type:uma-ticket" --data "ticket=USD{permission_ticket} --data "submit_request=true" curl -X POST -H "Content-Type: application/x-www-form-urlencoded" -d 'grant_type=client_credentials&client_id=USD{client_id}&client_secret=USD{client_secret}' "http://USD{host}:USD{port}/realms/USD{realm_name}/protocol/openid-connect/token" { "access_token": USD{PAT}, "expires_in": 300, "refresh_expires_in": 1800, "refresh_token": USD{refresh_token}, "token_type": "bearer", "id_token": USD{id_token}, "not-before-policy": 0, "session_state": "ccea4a55-9aec-4024-b11c-44f6f168439e" } http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set curl -v -X POST http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set -H 'Authorization: Bearer 'USDpat -H 'Content-Type: application/json' -d '{ "name":"Tweedl Social Service", "type":"http://www.example.com/rsrcs/socialstream/140-compatible", "icon_uri":"http://www.example.com/icons/sharesocial.png", "resource_scopes":[ "read-public", "post-updates", "read-private", "http://www.example.com/scopes/all" ] }' curl -v -X POST http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set -H 'Authorization: Bearer 'USDpat -H 'Content-Type: application/json' -d '{ "name":"Alice Resource", "owner": "alice" }' curl -v -X POST http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set -H 'Authorization: Bearer 'USDpat -H 'Content-Type: application/json' -d '{ "name":"Alice Resource", "owner": "alice", "ownerManagedAccess": true }' curl -v -X PUT http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set/{resource_id} -H 'Authorization: Bearer 'USDpat -H 'Content-Type: application/json' -d '{ "_id": "Alice Resource", "name":"Alice Resource", "resource_scopes": [ "read" ] }' curl -v -X DELETE http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set/{resource_id} -H 'Authorization: Bearer 'USDpat http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set/{resource_id} http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set?name=Alice Resource http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set?name=Alice Resource&exactName=true http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set?uri=/api/alice http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set?owner=alice http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set?type=albums http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/resource_set?scope=read http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/permission curl -X POST http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/permission -H 'Authorization: Bearer 'USDpat -H 'Content-Type: application/json' -d '[ { "resource_id": "{resource_id}", "resource_scopes": [ "view" ] } ]' curl -X POST http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/permission -H 'Authorization: Bearer 'USDpat -H 'Content-Type: application/json' -d '[ { "resource_id": "{resource_id}", "resource_scopes": [ "view" ], "claims": { "organization": ["acme"] } } ]' curl -X POST http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/permission/ticket -H 'Authorization: Bearer 'USDaccess_token -H 'Content-Type: application/json' -d '{ "resource": "{resource_id}", "requester": "{user_id}", "granted": true, "scopeName": "view" }' curl http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/permission/ticket -H 'Authorization: Bearer 'USDaccess_token curl -X PUT http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/permission/ticket -H 'Authorization: Bearer 'USDaccess_token -H 'Content-Type: application/json' -d '{ "id": "{ticket_id}" "resource": "{resource_id}", "requester": "{user_id}", "granted": false, "scopeName": "view" }' curl -X DELETE http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/permission/ticket/{ticket_id} -H 'Authorization: Bearer 'USDaccess_token http://USD{host}:USD{port}/realms/USD{realm_name}/authz/protection/uma-policy/{resource_id} curl -X POST http://localhost:8180/realms/photoz/authz/protection/uma-policy/{resource_id} -H 'Authorization: Bearer 'USDaccess_token -H 'Cache-Control: no-cache' -H 'Content-Type: application/json' -d '{ "name": "Any people manager", "description": "Allow access to any people manager", "scopes": ["read"], "roles": ["people-manager"] }' curl -X POST http://localhost:8180/realms/photoz/authz/protection/uma-policy/{resource_id} -H 'Authorization: Bearer 'USDaccess_token -H 'Cache-Control: no-cache' -H 'Content-Type: application/json' -d '{ "name": "Any people manager", "description": "Allow access to any people manager", "scopes": ["read"], "groups": ["/Managers/People Managers"] }' curl -X POST http://localhost:8180/realms/photoz/authz/protection/uma-policy/{resource_id} -H 'Authorization: Bearer 'USDaccess_token -H 'Cache-Control: no-cache' -H 'Content-Type: application/json' -d '{ "name": "Any people manager", "description": "Allow access to any people manager", "scopes": ["read"], "clients": ["my-client"] }' curl -X POST http://localhost:8180/realms/photoz/authz/protection/uma-policy/{resource_id} -H 'Authorization: Bearer 'USDaccess_token -H 'Cache-Control: no-cache' -H 'Content-Type: application/json' -d '{ "name": "Any people manager", "description": "Allow access to any people manager", "scopes": ["read"], "condition": "my-deployed-script.js" }' curl -X PUT http://localhost:8180/realms/photoz/authz/protection/uma-policy/{permission_id} -H 'Authorization: Bearer 'USDaccess_token -H 'Content-Type: application/json' -d '{ "id": "21eb3fed-02d7-4b5a-9102-29f3f09b6de2", "name": "Any people manager", "description": "Allow access to any people manager", "type": "uma", "scopes": [ "album:view" ], "logic": "POSITIVE", "decisionStrategy": "UNANIMOUS", "owner": "7e22131a-aa57-4f5f-b1db-6e82babcd322", "roles": [ "user" ] }' curl -X DELETE http://localhost:8180/realms/photoz/authz/protection/uma-policy/{permission_id} -H 'Authorization: Bearer 'USDaccess_token http://USD{host}:USD{port}/realms/USD{realm}/authz/protection/uma-policy?resource={resource_id} http://USD{host}:USD{port}/realms/USD{realm}/authz/protection/uma-policy?name=Any people manager http://USD{host}:USD{port}/realms/USD{realm}/authz/protection/uma-policy?scope=read http://USD{host}:USD{port}/realms/USD{realm}/authz/protection/uma-policy { "authorization": { "permissions": [ { "resource_set_id": "d2fe9843-6462-4bfc-baba-b5787bb6e0e7", "resource_set_name": "Hello World Resource" } ] }, "jti": "d6109a09-78fd-4998-bf89-95730dfd0892-1464906679405", "exp": 1464906971, "nbf": 0, "iat": 1464906671, "sub": "f1888f4d-5172-4359-be0c-af338505d86c", "typ": "kc_ett", "azp": "hello-world-authz-service" } http://USD{host}:USD{port}/realms/USD{realm_name}/protocol/openid-connect/token/introspect curl -X POST -H "Authorization: Basic aGVsbG8td29ybGQtYXV0aHotc2VydmljZTpzZWNyZXQ=" -H "Content-Type: application/x-www-form-urlencoded" -d 'token_type_hint=requesting_party_token&token=USD{RPT}' "http://localhost:8080/realms/hello-world-authz/protocol/openid-connect/token/introspect" { "permissions": [ { "resource_id": "90ccc6fc-b296-4cd1-881e-089e1ee15957", "resource_name": "Hello World Resource" } ], "exp": 1465314139, "nbf": 0, "iat": 1465313839, "aud": "hello-world-authz-service", "active": true } { "active": false }