I know I’m 2+ years late but I figure I’d share what I know and hopefully alleviate some pain for future readers. Full transparency- I am by no means a Keycloak/OAuth/OIDC expert and what I know is mostly from reading the docs, books, good ol’ YouTube and playing around with the tool.
This post will be comprised of two parts:
- I’ll attempt to answer all your questions to the best of my ability
- I’ll show you all how you can play around with policies/scopes/permissions in Keycloak without needing to deploy a separate app in order to better understand some of the core concepts in this thread. Do note though that this is mostly meant to get you all started. I’m using
Keycloak 8.0.0.
Part I
Some terminology before we get started:
- In Keycloak, you can create 2 types of permissions: Resource-Based and Scope-Based.
- Simply put, for
Resource-Basedpermissions, you apply it directly to your resource - For
Scoped-Basedpermission, you apply it to your scope(s) or scope(s) and resource.
is it best practice to create just one “view” scope, and use it across multiple resources (account, transaction, etc)? Or should I create a “viewAccount” scope, a “viewTransaction” scope, etc?
Scopes represent a set of rights at a protected resource. In your case, you have 2 resources: account and transaction, so I would lean towards the second approach.
In the long run, having a global view scope associated with all your resources (e.g. account, transaction, customer, settlement…) makes authorization difficult to both manage and adapt to security requirement changes.
Here are a few examples that you can check out to get a feel for design
- Slack API
- Box API
- Stripe
Do note though – I am not claiming that you shouldn’t share scopes across resources. Matter of fact, Keycloak allows this for resources with the same type. You could for instance need both viewAccount and viewTransaction scope to read a transaction under a given account (after all you might need access to the account to view transactions). Your requirements and standards will heavily influence your design.
For each practical combination of resource and scope, is it usual practice to create a permission?
Apologies, I don’t fully understand the question so I’ll be a bit broad. In order to grant/deny access to a resource, you need to:
- Define your policies
- Define your permissions
- Apply your policies to your permissions
- Associate your permissions to a
scopeorresource(or both)
for policy enforcement to take effect. See Authorization Process.
How you go about setting all this up is entirely up to you. You could for instance:
-
Define individual policies, and tie each policy under the appropriate permission.
-
Better yet, define individual policies, then group all your related policies under an
aggregatedpolicy (a policy of policies) and then associate that aggregated policy with thescope-basedpermission. You could have thatscoped-basedpermission apply to both the resource and all its associated scope. -
Or, you could further break apart your permissions by leveraging the two separate types. You could create permissions solely for your resources via the
resource-basedpermission type, and separately associate other permissions solely with a scope via thescope-basedpermission type.
You have options.
If there are multiple permissions matching a given resource/scope, what does Keycloak do?
This depends on
- The resource server’s
Decision Strategy - Each permission’s
Decision Strategy - Each policy’s
Logicvalue.
The Logic value is similar with Java’s ! operator. It can either be Positive or Negative. When the Logic is Positive, the policy’s final evaluation remains unchanged. When its Negative, the final result is negated (e.g. if a policy evaluates to false and its Logic is Negative, then it will be true). To keep things simple, let’s assume that the Logic is always set to Positive.
The Decision Strategy is what we really want to tackle. The Decision Strategy can either be Unanimous or Affirmative. From the docs,
Decision Strategy
This configurations changes how the policy evaluation engine decides whether or not a resource or scope should be granted based on the outcome from all evaluated permissions. Affirmative means that at least one permission must evaluate to a positive decision in order grant access to a resource and its scopes. Unanimous means that all permissions must evaluate to a positive decision in order for the final decision to be also positive. As an example, if two permissions for a same resource or scope are in conflict (one of them is granting access and the other is denying access), the permission to the resource or scope will be granted if the chosen strategy is Affirmative. Otherwise, a single deny from any permission will also deny access to the resource or scope.
Let’s use an example to better understand the above. Suppose you have a resource with 2 permissions and someone is trying to access that resource (remember, the Logic is Positive for all policies). Now:
Permission Onehas aDecision Strategyset toAffirmative. It also has 3 policies where they each evaluate to:truefalsefalse
Since one of the policies is set to true, Permission One is set to true (Affirmative – only 1 needs to be true).
Permission Twohas aDecision Strategyset toUnanimouswith 2 policies:truefalse
In this case Permission Two is false since one policy is false (Unanimous – they all need to be true).
- Now comes the final evaluation. If the resource server’s
Decision Strategyis set toAffirmative, access to that resource would be granted becausePermission Oneistrue. If on the other hand, the resource server’sDecision Strategyis set toUnanimous, access would be denied.
See:
- Resource Server Settings
- Managing Permissions
We’ll keep revisiting this. I explain how to set the resource sever’s Decision Strategy in Part II.
so for example I could have permission to access “accounts” and permission for “view” scope, so therefore I would have permission to view accounts?
The short answer is yes. Now, let’s expand on this a bit 🙂
If you have the following scenario:
- Resource server’s
Decision Strategyset toUnanimousorAffirmative - Permission to access the
account/{id}resource istrue - Permission to access the
viewscope istrue
You will be granted access to view the account.
true+trueis equal totrueunder theAffirmativeorUnanimousDecision Strategy.
Now if you have this
- Resource server’s
Decision Strategyset toAffirmative - Permission to access the
account/{id}resource istrue - Permission to access the
viewscope isfalse
You will also be granted access to view the account.
true+falseistrueunder theAffirmativestrategy.
The point here is that access to a given resource also depends on your setup so be careful as you may not want the second scenario.
But am I right that this means I need a policy for each of the legacy groups that a user could belong to?
I’m not sure how Keycloak behaved 2 years ago, but you can specify a Group-Based policy and simply add all your groups under that policy. You certainly do not need to create one policy per group.
For example, if I have a “helpdesk” role, then I need a “helpdesk membership” policy, which I could then add to the “viewAccount” permission. Is this correct?
Pretty much. There are many ways you can set this up. For instance, you can:
- Create your resource (e.g.
/account/{id}) and associate it with theaccount:viewscope. - create a Role-Based Policy and add the
helpdeskrole under that policy - Create a
Scope-Basedpermission calledviewAccountand tie it withscope,resourceandpolicy
We’ll set up something similar in Part II.
Part II
Keycloak has a neat little tool which allows you test all your policies. Better yet, you actually do not need to spin up another application server and deploy a separate app for this to work.
Here’s the scenario that we’ll set up:
- We’ll create a new realm called
stackoverflow-demo - We’ll create a
bank-apiclient under that realm - We will define a resource called
/account/{id}for that client - The
account/{id}will have theaccount:viewscope - We’ll create a user called
bobunder the new realm - We’ll also create three roles:
bank_teller,account_owneranduser- We will not associate
bobwith any roles. This is not needed right now.
- We will not associate
- We’ll set up the following two
Role-Basedpolicies:bank_tellerandaccount_ownerhave access to the/account/{id}resourceaccount_ownerhas access to theaccount:viewscopeuserdoes not have access to the resource or scope
- We’ll play around with the
Evaluatetool to see how access can be granted or
denied.
Do forgive me, this example is unrealistic but I’m not familiar with the banking sector 🙂
Keycloak setup
Download and run Keycloak
cd tmp
wget https://downloads.jboss.org/keycloak/8.0.0/keycloak-8.0.0.zip
unzip keycloak-8.0.0.zip
cd keycloak-8.0.0/bin
./standalone.sh
Create initial admin user
- Go to
http://localhost:8080/auth - Click on the
Administration Consolelink - Create the admin user and login
Visit Getting Started for more information. For our purposes, the above is enough.
Setting up the stage
Create a new realm
- Hover your mouse around the
masterrealm and click on theAdd Realmbutton. - Enter
stackoverflow-demoas the name. - Click on
Create. - The top left should now say
stackoverflow-demoinstead of themasterrealm.
See Creating a New Realm
Create a new user
- Click on the
Userslink on the left - Click on the
Add Userbutton - Enter the
username(e.g.bob) - Ensure that
User Enabledis turned on - Click
Save
See Creating a New User
Create new roles
- Click on the
Roleslink - Click on
Add Role - Add the following roles:
bank_teller,account_owneranduser
Again, do not associate your user with the roles. For our purposes, this is not needed.
See Roles
Create a client
- Click on the
Clientslink - Click on
Create - Enter
bank-apifor theClient ID - For the
Root URLenterhttp://127.0.0.1:8080/bank-api - Click on
Save - Ensure that
Client Protocolisopenid-connect - Change the
Access Typetoconfidential - Change
Authorization EnabledtoOn - Scroll down and hit
Save. A newAuthorizationtab should appear at the top. - Click on the
Authorizationtab and thenSettings - Ensure that the
Decision Strategyis set toUnanimous- This is the resource server’s
Decision Strategy
- This is the resource server’s
See:
- Creating a Client Application
- Enabling Authorization Services
Create Custom Scopes
- Click on the
Authorizationtab - Click on
Authorization Scopes>Createto bring upAdd Scopepage - Enter
account:viewin the name and hit enter.
Create “View Account Resource”
- Click on
Authorizationlink above - Click on
Resources - Click on
Create - Enter
View Account Resourcefor both theNameandDisplay name - Enter
account/{id}for theURI - Enter
account:viewin theScopestextbox - Click
Save
See Creating Resources
Create your policies
- Again under the
Authorizationtab, click onPolicies - Select
Rolefrom the theCreate Policydropdown - In the
Namesection, typeOnly Bank Teller and Account Owner Policy - Under
Realm Rolesselect both thebank_tellerandaccount_ownerrole - Ensure that
Logicis set toPositive - Click
Save - Click on the
Policieslink - Select
Roleagain from theCreate Policydropdown. - This time use
Only Account Owner Policyfor theName - Under
Realm Rolesselectaccount_owner - Ensure that
Logicis set toPositive - Click
Save - Click on the
Policieslink at the top, you should now see your newly created policies.
See Role-Based Policy
Do note that Keycloak has much more powerful policies. See Managing Policies
Create Resource-Based Permission
- Again under the
Authorizationtab, click onPermissions - Select
Resource-Based - Type
View Account Resource Permissionfor theName - Under
ResourcestypeView Account Resource Permission - Under
Apply PolicyselectOnly Bank Teller and Account Owner Policy - Ensure that the
Decision Strategyis set toUnanimous - Click
Save
See Create Resource-Based Permissions
Phew…
Evaluating the Resource-Based permission
- Again under the
Authorizationtab, selectEvaluate - Under
Userenterbob - Under
Rolesselectuser- This is where we will associate our user with our created roles.
- Under
ResourcesselectView Account Resourceand clickAdd - Click on Evaluate.
- Expand the
View Account Resource with scopes [account:view]to see the results and you should seeDENY.

- This makes sense because we only allow two roles access to that resource via the
Only Bank Teller and Account Owner Policy. Let’s test this to make sure this is true! - Click on the
Backlink right above the evaluation result - Change bob’s role to
account_ownerand click onEvaluate. You should now see the result asPERMIT. Same deal if you go back and change the role tobank_teller
See Evaluating and Testing Policies
Create Scope-Based Permission
- Go back to the
Permissionssection - Select
Scope-Basedthis time under theCreate Permissiondropdown. - Under
Name, enterView Account Scope Permission - Under
Scopes, enteraccount:view - Under
Apply Policy, enterOnly Account Owner Policy - Ensure that the
Decision Strategyis set toUnanimous - Click
Save
See Creating Scope-Based Permissions
Second test run
Evaluating our new changes
- Go back to the
Authorizationsection - Click on
Evaluate - User should be
bob - Roles should be
bank_teller - Resources should be
View Account Resourceand clickAdd - Click on
Evaluateand we should getDENY.- Again this should come as no surprise as the
bank_tellerhas access to theresourcebut not thescope. Here one permission evaluates to true, and the other to false. Given that the resource server’sDecision Strategyis set toUnanimous, the final decision isDENY.
- Again this should come as no surprise as the
- Click on
Settingsunder theAuthorizationtab, and change theDecision StrategytoAffirmativeand go back to steps 1-6 again. This time, the final result should bePERMIT(one permission is true, so final decision is true). - For the sake of completeness, turn the resource server’s
Decision Strategyback toUnanimous. Again, go back to steps 1 through 6 but this time, set the role asaccount_owner. This time, the final result is againPERMITwhich makes sense, given that theaccount_ownerhas access to both theresourceandscope.
Neat 🙂 Hope this helps.