Merge pull request #5 from pedroigor/master

Initial getting started tutorials
This commit is contained in:
Pedro Igor 2016-06-03 01:05:51 -03:00
commit bfbd4dc08c
10 changed files with 694 additions and 1 deletions

View file

@ -3,6 +3,8 @@
. link:topics/overview/overview.adoc[Overview]
.. link:topics/overview/architecture.adoc[Architecture]
.. link:topics/overview/terminology.adoc[Terminology]
. link:topics/getting-started/getting-started.adoc[Getting Started]
.. link:topics/getting-started/hello-world.adoc[Hello Authorization World]
. link:topics/resource-server/overview.adoc[Managing Resource Servers]
.. link:topics/resource-server/view.adoc[Viewing Resource Servers]
.. link:topics/resource-server/create.adoc[Creating Resource Servers]

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 66 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 43 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 64 KiB

View file

@ -0,0 +1,12 @@
== Getting Started
The purpose of this guide is to get you up and running as quickly as possible so that you can play with and test drive various authorization features that {{book.project.name}} has. It will help
you to understand the basic steps to enable fine-grained authorization to an application and how to interact with the Authorization Services provided by {{book.project.name}}.
[NOTE]
This guide assume that you are able to install and boot a {{book.project.name}} Server. For more information, please follow the intrusctions https://keycloak.gitbooks.io/getting-started-tutorials/content/[here].
Make sure you have a {{book.project.name}} instance up and running on http://localhost:8080/auth[http://localhost:8080/auth]. If everything is OK, you would be able to login to the
_Administration Console_ and get a page like that:
image:../../images/gs-keycloak-console-page.png[alt="Keycloak Administration Console"]

View file

@ -0,0 +1,202 @@
=== Creating the Hello World AuthZ Realm
For this guide, we are going to create a *hello-world-authz* realm. Just import the following JSON file to create the new realm:
```json
{
"realm" : "hello-world-authz",
"enabled" : true,
"privateKey" : "MIIEpQIBAAKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQABAoIBAAwa4wVnKBOIS6srmYPfBTDNsTBBCEjxiYEErmn7JhoWxQ1DCPUxyxU6F177/q9Idqoj1FFOCtEO9P6/9+ym470HQmEQkR2Xxd1d3HOZy9oKuCro3ZbTDkVxY0JnlyxZz4MihGFxDH2e4MArfHy0sAgYbdIU+x2pWKGWSMzDd/TMSOExhc/sIQAg6ljbPCLLXCPQFAncoHRyGPrkRZs6UTZi5SJuCglVa2/3G+0drDdPuA83/mwsZfIBqQgbGbFgtq5T5C6CKMkPOQ42Rcclm7kEr6riTkJRo23EO1iOJVpxzI0tbxZsJAsW7zeqv0wWRyUgVfQAje6OdsNexp5aCtECgYEA6nMHCQ9xXvufCyzpIbYGxdAGqH6m1AR5gXerHqRiGNx+8UUt/E9cy/HTOhmZDK/eC4BT9tImeF01l1oSU/+wGKfux0SeAQchBhhq8GD6jmrtgczKAfZHp0Zrht7o9qu9KE7ZNWRmY1foJN9yNYmzY6qqHEy+zNo9amcqT7UZKO8CgYEA35sp9fMpMqkJE+NEJ9Ph/t2081BEkC0DYIuETZRSi+Ek5AliWTyEkg+oisTbWzi6fMQHS7W+M1SQP6djksLQNPP+353DKgup5gtKS+K/y2xNd7fSsNmkjW1bdJJpID7WzwwmwdahHxpcnFFuEXi5FkG3Vqmtd3cD0TYL33JlRy0CgYEA0+a3eybsDy9Zpp4m8IM3R98nxW8DlimdMLlafs2QpGvWiHdAgwWwF90wTxkHzgG+raKFQVbb0npcj7mnSyiUnxRZqt2H+eHZpUq4jR76F3LpzCGui2tvg+8QDMy4vwqmYyIxDCL8r9mqRnl3HpChBPoh2oY7BahTTjKEeZpzbR0CgYEAoNnVjX+mGzNNvGi4Fo5s/BIwoPcU20IGM+Uo/0W7O7Rx/Thi7x6BnzB0ZZ7GzRA51paNSQEsGXCzc5bOIjzR2cXLisDKK+zIAxwMDhrHLWZzM7OgdGeb38DTEUBhLzkE/VwYZUgoD1+/TxOkwhy9yCzt3gGhL1cF//GJCOwZvuECgYEAgsO4rdYScgCpsyePnHsFk+YtqtdORnmttF3JFcL3w2QneXuRwg2uW2Kfz8CVphrR9eOU0tiw38w6QTHIVeyRY8qqlHtiXj6dEYz7frh/k4hI29HwFx43rRpnAnN8kBEJYBYdbjaQ35Wsqkfu1tvHJ+6fxSwvQu/TVdGp0OfilAY=",
"publicKey" : "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQAB",
"certificate" : "MIICsTCCAZkCBgFVETX4AzANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFIZWxsbyBXb3JsZCBBdXRoWjAeFw0xNjA2MDIxMzAxMzdaFw0yNjA2MDIxMzAzMTdaMBwxGjAYBgNVBAMMEUhlbGxvIFdvcmxkIEF1dGhaMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQANm5gIT/c50lwjawM686gNXpppLA928WsCOn9NIIWjSKekP8Bf9S73kf7vWcsEppm5B8rRyRxolXmzwghv74L7uVDg8Injjgj+XbPVQP+cJqWpSaMZHF7UfWe0/4M945Xcbmsl5q+m9PmrPG0AaaZhqXHcp4ehB1H+awyRqiERpJUuwZNycw2+2kjDADpsFf8hZVUd1F6ReYyOkqUyUjbL+jYTC7ZBNa7Ok+w6HCXWgkgVATAgQXJRM3w14IOc5MH/vfMCrCl/eNQLbjGl9y7u8PKwh3MXHDO2OLqtg6hOTSrOGUPJZGmGtUAl+2/R7FzoWkML/BNe2hjsL6UJwg91",
"requiredCredentials" : [ "password" ],
"roles" : {
"realm" : [
{
"name" : "uma_protection"
}
]
},
"users" :
[
{
"username" : "alice",
"enabled" : true,
"credentials" : [ {
"type" : "password",
"value" : "password"
} ],
"clientRoles" : {
"hello-world-authz-service" : [ "uma_authorization" ]
}
},
{
"username" : "jdoe",
"enabled" : true,
"credentials" : [ {
"type" : "password",
"value" : "password"
} ],
"clientRoles" : {
"hello-world-authz-service" : [ "uma_authorization" ]
}
},
{
"username" : "service-account-hello-world-authz-service",
"enabled" : true,
"serviceAccountClientId" : "hello-world-authz-service",
"realmRoles" : [ "uma_protection"]
}
],
"clients" : [
{
"clientId" : "hello-world-authz-service",
"secret" : "password",
"serviceAccountsEnabled" : true,
"enabled" : true,
"redirectUris" : [ "http://localhost:8080/hello-world-authz-service" ],
"directAccessGrantsEnabled" : true,
"publicClient" : false
}
]
}
```
The realm *hello-world-authz* consists of:
** Two users: _alice_ and _jdoe_
** One client application: _hello-world-authz-service_
** One global role: _uma_protection_
** One client role: _uma_authorization_
The _hello-world-authz-service_ is the application with the resources we want to protect. In other words, it will act as a link:../overview/terminology.html[Resource Server].
In {{book.project.name}} a resource server is just a regular client application with some specific characteristics. It _must_ be a *confidential* client application as defined by:
```json
"publicClient" : false
```
It must have a *client_id*, *client_secret* and *Service Account* enabled:
```json
"clients" : [
{
"clientId" : "hello-world-authz-service",
"secret" : "7801be7c-437a-44ae-be34-67da32f024eb",
"serviceAccountsEnabled" : true,
...
}
]
```
And finally, an user mapping to the client's service account:
```json
{
"username" : "service-account-my-resource-server",
"enabled" : true,
"serviceAccountClientId" : "hello-world-authz-service",
"realmRoles" : [ "uma_protection"]
}
```
In the latter case, we are also granting the *uma_protection* role to the client's service account. As you'll see, that will be necessary in order to get access to the link:../service/protection-api.html[Protection API].
=== Creating a Resource Server and Protecting Resources
Now that we have the *hello-world-authz* realm properly configured, we need to enable the *hello-world-authz-service* as a resource server. For that, click on the *Authorization* in the left menu bar.
image:../../images/gs-keycloak-authz-page.png[alt="Keycloak Authorization Page"]
To create a resource server you can click on the *Create* button.
image:../../images/gs-keycloak-authz-create-rs-page.png[alt="Create Resource Server"]
From that page you can create a resource server by manually filling that form or you can just import a JSON file with the configuration you want. For this guide, we'll just import a JSON file as follows:
```json
{
"clientId": "hello-world-authz-service",
"resources": [
{
"name": "Hello World Resource"
}
],
"policies": [
{
"name": "Only Special Users Policy",
"type": "user",
"logic": "POSITIVE",
"config": {
"users": "[\"alice\"]"
}
},
{
"name": "Hello World Resource Permission",
"type": "resource",
"config": {
"resources": "[\"Hello World Resource\"]",
"applyPolicies": "[\"Only Special Users Policy\"]"
}
}
]
}
```
After importing the JSON file above, you would see a page like that:
image:../../images/gs-authz-hello-rs-created-page.png[alt="Resource Server Successfully Created"]
You may take some time now exploring the resource server we just created. But first, let's understand what we just created.
The resource server was created based on the *hello-world-authz-service* client application, as you can see from the following configuration:
```json
{
"clientId": "hello-world-authz-service",
...
}
```
What we did was basically tell {{book.project.name}} that we want that client application acting as a resource server, so we can start creating the resources we want to protect as well the permissions
and authorization policies we want to use to actually protect the resources.
The purpose of this guide is keep things simple to get you started, so our newly created resource server has a single protected resource, as defined by the following configuration:
```json
{
...
"resources": [
{
"name": "Hello World Resource"
}
],
...
}
```
The *Hello World Resource* represents a set of one or more resources we want to protect. It can map to a single or to multiple resources in an application.
In order to protect it, we need to create the authorization policies and permissions we want to apply. Policies define the conditions that must be satisfied to grant a permission. Where a
permission is the link between a resource and the policies(or conditions) we want to enforce when someone wants to access a resource.
In this example, we have a single policy *Only Special Users Policy*. This policy tells that only the specified users are allowed to access _something_ (we don't know what, yet. That is up to the permission).
[NOTE]
{{book.project.name}} provides a few link:../policy/overview.html[policy types] that you can start using out-of-the-box. There are policies for RBAC, time constraints or even rules written using JavaScript or JBoss Drools.
The last step when protecting a resource is to define a permission. For that, we have defined a *Hello World Resource Permission* that links the resource we want to protect, _Hello World Resource_, with the
policy we want to apply to that resource, _Only Special Users Policy_.
```json
{
"name": "Hello World Resource Permission",
"type": "resource",
"config": {
"resources": "[\"Hello World Resource\"]",
"applyPolicies": "[\"Only Special Users Policy\"]"
}
}
```

View file

@ -0,0 +1 @@
== Obtaining Permissions using the Entitlement API

View file

@ -0,0 +1,126 @@
== Obtaining Permissions using UMA
One of the main features provided by {{book.project.name}} {{book.project.module}} is support for https://docs.kantarainitiative.org/uma/rec-uma-core.html[UMA] specification. UMA defines :
"`... how resource owners can control protected-resource access by clients operated by arbitrary requesting parties, where the resources reside on any number of resource servers, and where a centralized authorization server governs access based on resource owner policies.`"
[NOTE]
For now, {{book.project.name}} focus on API security and doesn't fully implement UMA. We don't support, yet, authorization flows where the user can manage their own resources and policies, share resources, and so forth.
=== Obtaining a Protection API Token (PAT)
The authorization process begins with a special type of token: the *Permission Ticket*. A permission ticket can only be obtained by a resource server using a OAuth2 access token with the scope
*uma_protection*. In UMA, the access token with the scope *uma_protection* is called a Protection API Token or PAT.
When we created the *hello-world-authz* realm, we also granted the scope *uma_protection* to the *hello-world-authz-service* client application. Considering that this application is also a *confidential* client,
you can obtain a PAT as follows:
```curl
curl -X POST \
-H "Authorization: Basic aGVsbG8td29ybGQtYXV0aHotc2VydmljZTpwYXNzd29yZA==" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d 'grant_type=client_credentials' \
"http://localhost:8080/auth/realms/hello-world-authz/protocol/openid-connect/token"
```
As a result, you will get the following response from the server:
```json
{
"access_token": ${access_token},
"expires_in": 300,
"refresh_expires_in": 1800,
"refresh_token": ${refresh_token},
"token_type": "bearer",
"id_token": ${id_token},
"not-before-policy": 0,
"session_state": "ccea4a55-9aec-4024-b11c-44f6f168439e"
}
```
Here, the ${access_token} represents a PAT, which is basically an OAuth2 access token with the scope *uma_protection*. With this token, you can now obtain a permission ticket from the
Protection API.
=== Obtaining a Permission Ticket
Once you have a PAT, you can use it to access the Protection API in order to obtain a permission ticket as follows:
```json
curl -X POST \
-H "Authorization: Bearer ${access_token}" \
-d '
{
"resource_set_id" : "Hello World Resource"
}
' \
"http://localhost:8080/auth/realms/hello-world-authz/authz/protection/permission"
```
As a result, you will get the following response from the server:
```json
{
"ticket": ${permission_ticket}
}
```
Here, we are asking the Protection API for a permission ticket representing the same extent of requested access that the resource server registered. In this case, the access request to
the _Hello World Resource_ resource.
=== Obtaining an Authorization API Token
The final step is obtain a *Requesting Party Token* or *RPT* with a set of authorization data, that will be used to actually gain access to protected resources at the resource server.
A RPT is obtained from the link:../service/authorization-api.html[Authorization API]. Just like the Protection API and Permission Tickets, the Authorization API also requires a special
OAuth2 access token to obtain a RPT. In UMA, this access token is called a *Authorization API Token* or *AAT*, which contains a scope *uma_authorization* in it.
The *uma_authorization* scope indicates that an user consented to allow a given client application to access authorization data on his behalf. When we created the *hello-world-authz* realm,
we also defined a client role to the users:
```json
"clientRoles" : {
"hello-world-authz-service" : [ "uma_authorization" ]
}
```
To obtain an AAT you can send a request to {{book.project.name}} as follows:
```bash
curl -X POST \
-H "Authorization: Basic aGVsbG8td29ybGQtYXV0aHotc2VydmljZTpwYXNzd29yZA==" \
-H "Content-Type: application/x-www-form-urlencoded" \
-d 'username=alice&password=password&grant_type=password' \
"http://localhost:8080/auth/realms/hello-world-authz/protocol/openid-connect/token"
```
As a result, you will get the following response from the server:
```json
{
"access_token": ${access_token},
"expires_in": 300,
"refresh_expires_in": 1800,
"refresh_token": ${refresh_token},
"token_type": "bearer",
"id_token": ${id_token},
"not-before-policy": 0,
"session_state": "3cad2afc-855b-47b7-8e4d-a21c66e312fb"
}
```
=== Obtaining a Requesting Party Token or RPT
Now that we have a AAT we can call the Authorization API and ask for a RPT.
```bash
curl -X POST
-H "Authorization: Bearer ${AAT}" -d '{
"ticket" : ${permission_ticket}
}' "http://localhost:8080/auth/realms/hello-world-authz/authz/authorize"
```
As a result, you will get the follow response from the server:
```json
{"rpt":"${rpt}"}
```
=== Obtaining Permissions using the Entitlement API

View file

@ -0,0 +1,350 @@
== Hello Authorization World
This guide will show you how to:
* Create a realm with the necessary configuration to enable fine-grained authorization to your applications
* Create a resource server and the resources that must be protected
* Create permissions, authorization policies and how to apply them to your protected resources
* Access the {{book.project.name}} {{book.project.module}} and enforce authorization decisions
The purpose of this guide is to give you a generic overview of {{book.project.name}} {{book.project.module}} so you can understand
some core concepts and start protecting your applications and services despite the platform they are running on.
=== Creating the Hello World AuthZ Realm
For this guide, we are going to create a *hello-world-authz* realm. Just import the following JSON file to create the new realm:
```json
{
"realm" : "hello-world-authz",
"enabled" : true,
"privateKey" : "MIIEpQIBAAKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQABAoIBAAwa4wVnKBOIS6srmYPfBTDNsTBBCEjxiYEErmn7JhoWxQ1DCPUxyxU6F177/q9Idqoj1FFOCtEO9P6/9+ym470HQmEQkR2Xxd1d3HOZy9oKuCro3ZbTDkVxY0JnlyxZz4MihGFxDH2e4MArfHy0sAgYbdIU+x2pWKGWSMzDd/TMSOExhc/sIQAg6ljbPCLLXCPQFAncoHRyGPrkRZs6UTZi5SJuCglVa2/3G+0drDdPuA83/mwsZfIBqQgbGbFgtq5T5C6CKMkPOQ42Rcclm7kEr6riTkJRo23EO1iOJVpxzI0tbxZsJAsW7zeqv0wWRyUgVfQAje6OdsNexp5aCtECgYEA6nMHCQ9xXvufCyzpIbYGxdAGqH6m1AR5gXerHqRiGNx+8UUt/E9cy/HTOhmZDK/eC4BT9tImeF01l1oSU/+wGKfux0SeAQchBhhq8GD6jmrtgczKAfZHp0Zrht7o9qu9KE7ZNWRmY1foJN9yNYmzY6qqHEy+zNo9amcqT7UZKO8CgYEA35sp9fMpMqkJE+NEJ9Ph/t2081BEkC0DYIuETZRSi+Ek5AliWTyEkg+oisTbWzi6fMQHS7W+M1SQP6djksLQNPP+353DKgup5gtKS+K/y2xNd7fSsNmkjW1bdJJpID7WzwwmwdahHxpcnFFuEXi5FkG3Vqmtd3cD0TYL33JlRy0CgYEA0+a3eybsDy9Zpp4m8IM3R98nxW8DlimdMLlafs2QpGvWiHdAgwWwF90wTxkHzgG+raKFQVbb0npcj7mnSyiUnxRZqt2H+eHZpUq4jR76F3LpzCGui2tvg+8QDMy4vwqmYyIxDCL8r9mqRnl3HpChBPoh2oY7BahTTjKEeZpzbR0CgYEAoNnVjX+mGzNNvGi4Fo5s/BIwoPcU20IGM+Uo/0W7O7Rx/Thi7x6BnzB0ZZ7GzRA51paNSQEsGXCzc5bOIjzR2cXLisDKK+zIAxwMDhrHLWZzM7OgdGeb38DTEUBhLzkE/VwYZUgoD1+/TxOkwhy9yCzt3gGhL1cF//GJCOwZvuECgYEAgsO4rdYScgCpsyePnHsFk+YtqtdORnmttF3JFcL3w2QneXuRwg2uW2Kfz8CVphrR9eOU0tiw38w6QTHIVeyRY8qqlHtiXj6dEYz7frh/k4hI29HwFx43rRpnAnN8kBEJYBYdbjaQ35Wsqkfu1tvHJ+6fxSwvQu/TVdGp0OfilAY=",
"publicKey" : "MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQAB",
"certificate" : "MIICsTCCAZkCBgFVETX4AzANBgkqhkiG9w0BAQsFADAcMRowGAYDVQQDDBFIZWxsbyBXb3JsZCBBdXRoWjAeFw0xNjA2MDIxMzAxMzdaFw0yNjA2MDIxMzAzMTdaMBwxGjAYBgNVBAMMEUhlbGxvIFdvcmxkIEF1dGhaMIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzMhNM9HXNQWhVf1m64zS67SIyQjj+tV5GR+MqlRTWDXdo8GAWHd+alY1urRhfRoqMy4F499+8wh2REKFykNt0ng6s6wWnEaKDboS3SAUV6lybcOAkwIOCtCZj1ItddKG3m64fzxDDQrcpkbiAvw3S8KJ4UJK+pyh9iX01duSDtM/HhPawsPdY8JSMfuo1IxQ2Vxw+8RKwbbdUeew6cyYGYAeFYwA66mlM3otB0RBHh4bjwg8297+2g53TdwM2rbCHRbrorMQD3031OTyFSp7lXCtoMLWRfAFnOP/2yZWZMXbiJheC0R3sLbU7Ef0/cUbYyk4Ckfq6pcYDR+VZBF7AwIDAQABMA0GCSqGSIb3DQEBCwUAA4IBAQANm5gIT/c50lwjawM686gNXpppLA928WsCOn9NIIWjSKekP8Bf9S73kf7vWcsEppm5B8rRyRxolXmzwghv74L7uVDg8Injjgj+XbPVQP+cJqWpSaMZHF7UfWe0/4M945Xcbmsl5q+m9PmrPG0AaaZhqXHcp4ehB1H+awyRqiERpJUuwZNycw2+2kjDADpsFf8hZVUd1F6ReYyOkqUyUjbL+jYTC7ZBNa7Ok+w6HCXWgkgVATAgQXJRM3w14IOc5MH/vfMCrCl/eNQLbjGl9y7u8PKwh3MXHDO2OLqtg6hOTSrOGUPJZGmGtUAl+2/R7FzoWkML/BNe2hjsL6UJwg91",
"requiredCredentials" : [ "password" ],
"roles" : {
"realm" : [
{
"name" : "uma_protection", "kc_entitlements"
}
]
},
"users" :
[
{
"username" : "alice",
"enabled" : true,
"credentials" : [ {
"type" : "password",
"value" : "password"
} ],
"clientRoles" : {
"hello-world-authz-service" : [ "uma_authorization", "kc_entitlement" ]
}
},
{
"username" : "jdoe",
"enabled" : true,
"credentials" : [ {
"type" : "password",
"value" : "password"
} ],
"clientRoles" : {
"hello-world-authz-service" : [ "uma_authorization", "kc_entitlement" ]
}
},
{
"username" : "service-account-hello-world-authz-service",
"enabled" : true,
"serviceAccountClientId" : "hello-world-authz-service",
"realmRoles" : [ "uma_protection"]
}
],
"clients" : [
{
"clientId" : "hello-world-authz-service",
"secret" : "password",
"serviceAccountsEnabled" : true,
"enabled" : true,
"redirectUris" : [ "http://localhost:8080/hello-world-authz-service" ],
"directAccessGrantsEnabled" : true,
"publicClient" : false
}
]
}
```
The realm *hello-world-authz* consists of:
** Two users: _alice_ and _jdoe_
** One client application: _hello-world-authz-service_
** One global role: _uma_protection_
** One client role: _uma_authorization_
The _hello-world-authz-service_ is the application with the resources we want to protect. In other words, it will act as a link:../overview/terminology.html[Resource Server].
In {{book.project.name}} a resource server is just a regular client application with some specific characteristics. It _must_ be a *confidential* client application as defined by:
```json
"publicClient" : false
```
It must have a *client_id*, *client_secret* and *Service Account* enabled:
```json
"clients" : [
{
"clientId" : "hello-world-authz-service",
"secret" : "7801be7c-437a-44ae-be34-67da32f024eb",
"serviceAccountsEnabled" : true,
...
}
]
```
And finally, an user mapping to the client's service account:
```json
{
"username" : "service-account-my-resource-server",
"enabled" : true,
"serviceAccountClientId" : "hello-world-authz-service",
"realmRoles" : [ "uma_protection"]
}
```
In the latter case, we are also granting the *uma_protection* role to the client's service account. As you'll see, that will be necessary in order to get access to the link:../service/protection-api.html[Protection API].
=== Creating a Resource Server and Protecting Resources
Now that we have the *hello-world-authz* realm properly configured, we need to enable the *hello-world-authz-service* as a resource server. For that, click on the *Authorization* in the left menu bar.
image:../../images/gs-keycloak-authz-page.png[alt="Keycloak Authorization Page"]
To create a resource server you can click on the *Create* button.
image:../../images/gs-keycloak-authz-create-rs-page.png[alt="Create Resource Server"]
From that page you can create a resource server by manually filling that form or you can just import a JSON file with the configuration you want. For this guide, we'll just import a JSON file as follows:
```json
{
"clientId": "hello-world-authz-service",
"resources": [
{
"name": "Hello World Resource"
}
],
"policies": [
{
"name": "Only Special Users Policy",
"type": "user",
"logic": "POSITIVE",
"config": {
"users": "[\"alice\"]"
}
},
{
"name": "Hello World Resource Permission",
"type": "resource",
"config": {
"resources": "[\"Hello World Resource\"]",
"applyPolicies": "[\"Only Special Users Policy\"]"
}
}
]
}
```
After importing the JSON file above, you would see a page like that:
image:../../images/gs-authz-hello-rs-created-page.png[alt="Resource Server Successfully Created"]
You may take some time now exploring the resource server we just created. But first, let's understand what we just created.
The resource server was created based on the *hello-world-authz-service* client application, as you can see from the following configuration:
```json
{
"clientId": "hello-world-authz-service",
...
}
```
What we did was basically tell {{book.project.name}} that we want that client application acting as a resource server, so we can start creating the resources we want to protect as well the permissions
and authorization policies we want to use to actually protect the resources.
The purpose of this guide is keep things simple to get you started, so our newly created resource server has a single protected resource, as defined by the following configuration:
```json
{
...
"resources": [
{
"name": "Hello World Resource"
}
],
...
}
```
The *Hello World Resource* represents a set of one or more resources we want to protect. It can map to a single or to multiple resources in an application.
In order to protect it, we need to create the authorization policies and permissions we want to apply. Policies define the conditions that must be satisfied to grant a permission. Where a
permission is the link between a resource and the policies(or conditions) we want to enforce when someone wants to access a resource.
In this example, we have a single policy *Only Special Users Policy*. This policy tells that only the specified users are allowed to access _something_ (we don't know what, yet. That is up to the permission).
[NOTE]
{{book.project.name}} provides a few link:../policy/overview.html[policy types] that you can start using out-of-the-box. There are policies for RBAC, time constraints or even rules written using JavaScript or JBoss Drools.
The last step when protecting a resource is to define a permission. For that, we have defined a *Hello World Resource Permission* that links the resource we want to protect, _Hello World Resource_, with the
policy we want to apply to that resource, _Only Special Users Policy_.
```json
{
"name": "Hello World Resource Permission",
"type": "resource",
"config": {
"resources": "[\"Hello World Resource\"]",
"applyPolicies": "[\"Only Special Users Policy\"]"
}
}
```
== Obtaining Permissions using the Entitlement API
In {{book.project.name}}, authorization data is represented by a special security token called a *Requesting Party* Token or *RPT*. This token consists of all the permissions granted
to an user as a result of the evaluation of the permissions and authorization policies associated with the resource being requested. In this guide, we'll see how to obtain a RPT using the link:../service/entitlement-api.html[Entitlement API].
Now that the *hello-world-authz* realm is properly configured with the resources we want to protected and their corresponding permissions and authorization policies, we can ask the server for what an user
is entitled to do. In other words, what are the permissions the user has.
The Entitlement API consists of a single endpoint. This endpoint expects an OAuth2 Access Token with a scope *kc_entitlement* representing the user identity and the identifier of the resource server we want to access. The *kc_entitlement*
scope is necessary to indicate that the user consented access to his authorization data from a given client application. The access token with a scope *kc_entitlement* is called *Entitlement API Token* or *EAT*.
In this case, for the sake of simplicity, our resource server *hello-world-authz-service* is also acting as a client application. What means that the user allowed this application to access his
authorization data and obtain RPTs on his behalf.
The first step is obtain a EAT as follows:
```bash
curl -X POST
-H "Authorization: Basic aGVsbG8td29ybGQtYXV0aHotc2VydmljZTpwYXNzd29yZA=="
-H "Content-Type: application/x-www-form-urlencoded"
-d 'username=alice&password=password&grant_type=password'
"http://localhost:8080/auth/realms/hello-world-authz/protocol/openid-connect/token"
```
Here we are using the _Resource Owner Password Credentials Grant Type_ (Direct Access Grant in Keycloak terminology), as defined by OAuth2 specification, to obtain an EAT on behalf of _alice_. As a result, you
would get a response from the server as follows:
```json
{
"access_token": ${EAT},
"expires_in": 300,
"refresh_expires_in": 1800,
"refresh_token": ${refresh_token},
"token_type": "bearer",
"id_token": ${id_token},
"not-before-policy": 0,
"session_state": "1ad4d54c-7758-4698-92d3-d57d821f130b"
}
```
[NOTE]
Resource Owner Password Credentials Grant Type is only used here for demonstration purposes. In the real world, you'll usually obtain an access token using a more secure grant type such as _authorization_code_.
The idea here is demonstrate that you need to obtain an access token with the scope *kc_entitlement* prior to access the Entitlement API to ask for a RPT.
Finally, the last step is obtain a RPT from the Entitlement API as follows:
```bash
curl -X GET \
-H "Authorization: Bearer ${EAT}" \
"http://localhost:8080/auth/realms/hello-world-authz/authz/entitlement/hello-world-authz-service"
```
As result, you'll get a response from the server as follows:
```json
{
"rpt": ${RPT}
}
```
By default, {{book.project.name}} issues a RPT consisting of permissions for every single resource protected/managed by the resource server. If you want to limit the permissions to only a specific
set of resources you can request a RPT as follows:
```bash
curl -X POST \
-H "Content-Type: application/json" \
-H "Authorization: Bearer %{EAT}" \
-d '{
"permissions" : [
{
"resource_set_name" : "Hello World Resource"
}
]
}' \
"http://localhost:8080/auth/realms/hello-world-authz/authz/entitlement/hello-world-authz-service"
```
Let's see now what happens when the user does not have access to a protected resource at the resource server. For that, let's obtain a new EAT but now using _jdoe_ credentials.
```bash
curl -X POST
-H "Authorization: Basic aGVsbG8td29ybGQtYXV0aHotc2VydmljZTpwYXNzd29yZA=="
-H "Content-Type: application/x-www-form-urlencoded"
-d 'username=jdoe&password=password&grant_type=password'
"http://localhost:8080/auth/realms/hello-world-authz/protocol/openid-connect/token"
```
Just like we did with _alice_, the server will return an EAT that we can use to obtain a RPT. But _jdoe_ is not supposed to access the protected resource, so the server is going to give you a response as follows:
```json
{
"error_description": "Authorization denied.",
"error": "not_authorized"
}
```
The reason for that is that the _Hello World Resource_ is protected by a _Only Special Users Policy_ that says that only _alice_ is allowed to access the resource. You can play around now by changing that policy to
include _jdoe_ as a valid user and see the results.
== Enforcing Authorization Decisions
A RPT is basically a https://tools.ietf.org/html/rfc7519[JSON Web Token (JWT)] digitally signed using https://www.rfc-editor.org/rfc/rfc7515.txt[JSON Web Signature (JWS)]. Its lifetime is the same as with the OAuth2 access token (EAT) that was used to obtain it.
When you decode a RPT you will see something like that:
```json
{
"permissions": [
{
"resource_set_id": "152251e6-f4cf-4464-8d91-f1b7960fa5fc",
"resource_set_name": "Hello World Resource"
"scopes": []
}
],
"accessToken": ${EAT},
"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"
}
```
The *permissions* claim lists all resource (and their respective scopes if any) permissions granted by the server. There is also a *accessToken* property holding the EAT that was used to issue the RPT.
The RPT provides everything you need to enforce authorization decisions at the resource server side. That can be easily accomplished by:
* Validating the RPT signature (based on realm's public key)
* Checking the token validity
* Decoding the RPT and extracting the permissions
* Checking if a request sent to the resource server trying to access a protected resource matches any permissions within the RPT
You can even use the information within a RPT to protect resources within a page like buttons or any other visual component.