keycloak-scim/topics/getting-started/hello-world.adoc
2016-06-08 09:32:31 -03:00

355 lines
No EOL
16 KiB
Text
Executable file

== 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.
[NOTE]
Before going further, make sure you followed all the instructions in the link:../getting-started/getting-started.html[Getting Started] guide.
=== 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" : "secret",
"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" : "password",
"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\"]"
}
}
]
}
```
[NOTE]
All this configuration can also be done using the {{book.project.name}} Administration Console. We are using the import tool just for demonstration purposes
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 its 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 ABAC, 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.
In this case, for the sake of simplicity, our resource server *hello-world-authz-service* is also acting as a client application. Which 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 and 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, or you can create new policies and change the permission to use them.
== 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 consists of all the 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.
Another way to validate a RPT and obtain the granted permissions is using the link:../service/protection/token-introspection.html[Token Introspection Endpoint].