Authorization Services Guide
Authorization Services Guide Red Hat build of Keycloak 24.0 Red Hat Customer Content Services
http://USD{host}:USD{port}/my-resource-server // by default, grants any permission associated with this policy USDevaluation.grant(); const context = USDevaluation.getContext(); const contextAttributes = context.getAttributes(); if (contextAttributes.containsValue('kc.client.network.ip_address', '127.0.0.1')) { USDevaluation.grant(); } const context = USDevaluation.getContext(); const identity = context.getIdentity(); const attributes = identity.getAttributes(); const email = attributes.getValue('email').asString(0); if (email.endsWith('@keycloak.org')) { USDevaluation.grant(); } const context = USDevaluation.getContext(); const identity = context.getIdentity(); if (identity.hasRealmRole('keycloak_user')) { USDevaluation.grant(); } const context = USDevaluation.getContext(); const identity = context.getIdentity(); if (identity.hasClientRole('my-client', 'my-client-role')) { USDevaluation.grant(); } const realm = USDevaluation.getRealm(); if (realm.isUserInRealmRole('marta', 'role-a')) { USDevaluation.grant(); } const realm = USDevaluation.getRealm(); if (realm.isUserInClientRole('marta', 'my-client', 'some-client-role')) { USDevaluation.grant(); } const realm = USDevaluation.getRealm(); if (realm.isGroupInRole('/Group A/Group D', 'role-a')) { USDevaluation.grant(); } const permission = USDevaluation.getPermission(); // decide if permission should be granted if (granted) { permission.addClaim('claim-a', 'claim-a'); permission.addClaim('claim-a', 'claim-a1'); permission.addClaim('claim-b', 'claim-b'); } const realm = USDevaluation.getRealm(); if (realm.isUserInGroup('marta', '/Group A/Group B')) { USDevaluation.grant(); } const context = USDevaluation.getContext(); const identity = context.getIdentity(); const attributes = identity.getAttributes(); const email = attributes.getValue('email').asString(0); if (identity.hasRealmRole('admin') || email.endsWith('@keycloak.org')) { USDevaluation.grant(); } public interface Evaluation { /** * Returns the {@link ResourcePermission} to be evaluated. * * @return the permission to be evaluated */ ResourcePermission getPermission(); /** * Returns the {@link EvaluationContext}. Which provides access to the whole evaluation runtime context. * * @return the evaluation context */ EvaluationContext getContext(); /** * Returns a {@link Realm} that can be used by policies to query information. * * @return a {@link Realm} instance */ Realm getRealm(); /** * Grants the requested permission to the caller. */ void grant(); /** * Denies the requested permission. */ void deny(); } public interface EvaluationContext { /** * Returns the {@link Identity} that represents an entity (person or non-person) to which the permissions must be granted, or not. * * @return the identity to which the permissions must be granted, or not */ Identity getIdentity(); /** * Returns all attributes within the current execution and runtime environment. * * @return the attributes within the current execution and runtime environment */ Attributes getAttributes(); } 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 } <dependencies> <dependency> <groupId>org.keycloak</groupId> <artifactId>keycloak-authz-client</artifactId> <version>USD{KEYCLOAK_VERSION}</version> </dependency> </dependencies> { "realm": "hello-world-authz", "auth-server-url" : "http://localhost:8080", "resource" : "hello-world-authz-service", "credentials": { "secret": "secret" } } // create a new instance based on the configuration defined in a keycloak.json located in your classpath AuthzClient authzClient = AuthzClient.create(); // create a new instance based on the configuration defined in keycloak.json AuthzClient authzClient = AuthzClient.create(); // create an authorization request AuthorizationRequest request = new AuthorizationRequest(); // send the entitlement request to the server in order to // obtain an RPT with all permissions granted to the user AuthorizationResponse response = authzClient.authorization("alice", "alice").authorize(request); String rpt = response.getToken(); System.out.println("You got an RPT: " + rpt); // now you can use the RPT to access protected resources on the resource server // create a new instance based on the configuration defined in keycloak.json AuthzClient authzClient = AuthzClient.create(); // create an authorization request AuthorizationRequest request = new AuthorizationRequest(); // add permissions to the request based on the resources and scopes you want to check access request.addPermission("Default Resource"); // send the entitlement request to the server in order to // obtain an RPT with permissions for a single resource AuthorizationResponse response = authzClient.authorization("alice", "alice").authorize(request); String rpt = response.getToken(); System.out.println("You got an RPT: " + rpt); // now you can use the RPT to access protected resources on the resource server // create a new instance based on the configuration defined in keycloak.json AuthzClient authzClient = AuthzClient.create(); // create a new resource representation with the information we want ResourceRepresentation newResource = new ResourceRepresentation(); newResource.setName("New Resource"); newResource.setType("urn:hello-world-authz:resources:example"); newResource.addScope(new ScopeRepresentation("urn:hello-world-authz:scopes:view")); ProtectedResource resourceClient = authzClient.protection().resource(); ResourceRepresentation existingResource = resourceClient.findByName(newResource.getName()); if (existingResource != null) { resourceClient.delete(existingResource.getId()); } // create the resource on the server ResourceRepresentation response = resourceClient.create(newResource); String resourceId = response.getId(); // query the resource using its newly generated id ResourceRepresentation resource = resourceClient.findById(resourceId); System.out.println(resource); // create a new instance based on the configuration defined in keycloak.json AuthzClient authzClient = AuthzClient.create(); // send the authorization request to the server in order to // obtain an RPT with all permissions granted to the user AuthorizationResponse response = authzClient.authorization("alice", "alice").authorize(); String rpt = response.getToken(); // introspect the token TokenIntrospectionResponse requestingPartyToken = authzClient.protection().introspectRequestingPartyToken(rpt); System.out.println("Token status is: " + requestingPartyToken.getActive()); System.out.println("Permissions granted by the server: "); for (Permission granted : requestingPartyToken.getPermissions()) { System.out.println(granted); } <dependency> <groupId>org.keycloak</groupId> <artifactId>keycloak-policy-enforcer</artifactId> <version>USD{keycloak.version}</version> </dependency> { "enforcement-mode" : "ENFORCING", "paths": [ { "path" : "/users/*", "methods" : [ { "method": "GET", "scopes" : ["urn:app.com:scopes:view"] }, { "method": "POST", "scopes" : ["urn:app.com:scopes:create"] } ] } ] } { "paths": [ { "path": "/protected/resource", "claim-information-point": { "claims": { "claim-from-request-parameter": "{request.parameter['a']}", "claim-from-header": "{request.header['b']}", "claim-from-cookie": "{request.cookie['c']}", "claim-from-remoteAddr": "{request.remoteAddr}", "claim-from-method": "{request.method}", "claim-from-uri": "{request.uri}", "claim-from-relativePath": "{request.relativePath}", "claim-from-secure": "{request.secure}", "claim-from-json-body-object": "{request.body['/a/b/c']}", "claim-from-json-body-array": "{request.body['/d/1']}", "claim-from-body": "{request.body}", "claim-from-static-value": "static value", "claim-from-multiple-static-value": ["static", "value"], "param-replace-multiple-placeholder": "Test {keycloak.access_token['/custom_claim/0']} and {request.parameter['a']}" } } } ] } { "paths": [ { "path": "/protected/resource", "claim-information-point": { "http": { "claims": { "claim-a": "/a", "claim-d": "/d", "claim-d0": "/d/0", "claim-d-all": [ "/d/0", "/d/1" ] }, "url": "http://mycompany/claim-provider", "method": "POST", "headers": { "Content-Type": "application/x-www-form-urlencoded", "header-b": [ "header-b-value1", "header-b-value2" ], "Authorization": "Bearer {keycloak.access_token}" }, "parameters": { "param-a": [ "param-a-value1", "param-a-value2" ], "param-subject": "{keycloak.access_token['/sub']}", "param-user-name": "{keycloak.access_token['/preferred_username']}", "param-other-claims": "{keycloak.access_token['/custom_claim']}" } } } } ] } { "paths": [ { "path": "/protected/resource", "claim-information-point": { "claims": { "claim-from-static-value": "static value", "claim-from-multiple-static-value": ["static", "value"] } } } ] } public class MyClaimInformationPointProviderFactory implements ClaimInformationPointProviderFactory<MyClaimInformationPointProvider> { @Override public String getName() { return "my-claims"; } @Override public void init(PolicyEnforcer policyEnforcer) { } @Override public MyClaimInformationPointProvider create(Map<String, Object> config) { return new MyClaimInformationPointProvider(config); } } public class MyClaimInformationPointProvider implements ClaimInformationPointProvider { private final Map<String, Object> config; public MyClaimInformationPointProvider(Map<String, Object> config) { this.config = config; } @Override public Map<String, List<String>> resolve(HttpFacade httpFacade) { Map<String, List<String>> claims = new HashMap<>(); // put whatever claim you want into the map return claims; } } HttpServletRequest request = // obtain javax.servlet.http.HttpServletRequest AuthorizationContext authzContext = (AuthorizationContext) request.getAttribute(AuthorizationContext.class.getName()); if (authzContext.hasResourcePermission("Project Resource")) { // user can access the Project Resource } if (authzContext.hasResourcePermission("Admin Resource")) { // user can access administration resources } if (authzContext.hasScopePermission("urn:project.com:project:create")) { // user can create new projects } if (User.hasRole('user')) { // user can access the Project Resource } if (User.hasRole('admin')) { // user can access administration resources } if (User.hasRole('project-manager')) { // user can create new projects } ClientAuthorizationContext clientContext = ClientAuthorizationContext.class.cast(authzContext); AuthzClient authzClient = clientContext.getClient(); <script src="http://.../js/keycloak-authz.js"></script> const keycloak = ... // obtain a Keycloak instance from keycloak.js library const authorization = new KeycloakAuthorization(keycloak); 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" // prepare a authorization request with the permission ticket const authorizationRequest = {}; authorizationRequest.ticket = ticket; // send the authorization request, if successful retry the request Identity.authorization.authorize(authorizationRequest).then(function (rpt) { // onGrant }, function () { // onDeny }, function () { // onError }); authorization.entitlement('my-resource-server-id').then(function (rpt) { // onGrant callback function. // If authorization was successful you'll receive an RPT // with the necessary permissions to access the resource server }); authorization.entitlement('my-resource-server', { "permissions": [ { "id" : "Some Resource" } ] }).then(function (rpt) { // onGrant }); const authorizationRequest = { "permissions": [ { "id" : "Some Resource", "scopes" : ["view", "edit"] } ] } const rpt = authorization.rpt; { "truststore": "path_to_your_trust_store", "truststore-password": "trust_store_password" }