# Keycloak

The `gm-jwt-keycloak` filter validates incoming requests against [Keycloak](https://www.keycloak.org/) and generates an internal Grey Matter [jwt token](http://jwt.io/).

## Filter Configuration Options

### Required Parameters

|                           |                  |                                                                                                                                                                                                                                                  |                                                                                                                                                                            |
| ------------------------- | ---------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | -------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| **Name**                  | **Type**         | **Example**                                                                                                                                                                                                                                      | Description                                                                                                                                                                |
| `clientSecret`            | string           | "40fcc8c9-0067-402e-95ac-a5aac213be8a"                                                                                                                                                                                                           | Client secret from keycloak. See [keycloak documentation](https://wjw465150.gitbooks.io/keycloak-documentation/content/server_admin/topics/clients/oidc/confidential.html) |
| `clientID`                | string           | "edge"                                                                                                                                                                                                                                           | Name of the client created in Keycloak                                                                                                                                     |
| `endpoint`                | string           | "[http://keycloak-host:80](http://keycloak-host)"                                                                                                                                                                                                | Full Keycloak URL                                                                                                                                                          |
| `authnHeaderName`         | string           | "access\_token"                                                                                                                                                                                                                                  | Name of header used for validating incoming and passing outgoing authentication tokens.                                                                                    |
| `authzheaderName`         | string           | "user\_info"                                                                                                                                                                                                                                     | Name of header used for validating incoming and passing outgoing authorization tokens                                                                                      |
| `realm`                   | string           | "greymatter"                                                                                                                                                                                                                                     | Name of the realm to be used in Keycloak                                                                                                                                   |
| `jwtPrivateKeyPath`       | string           | "./certs/private\_key.pem"                                                                                                                                                                                                                       | Private jwt key for Greymatter signing and validation                                                                                                                      |
| `jwks`                    | stringified JSON | "{"keys":\[{"crv":"P-521","kid":"1","kty":"EC","x":"AStrIEK2lPMCEPCiOA-vhIx65kwGL1tCYXGNmhIAFJU8BrGlPO8WYm3aUcmCXNJD76wYL3oh9Wu5d7iJifAdZhbg","y":"AehFcEyvkz0-8MvMGQSUfw5GVdYQTaWSRiOIiGVjK2FJCcl7n70CCIlNKpK3c2LNJu2BzrQmh7y21Ug7jvSafbQI"}]}" | Public JSON web key sets for internal validation of tokens                                                                                                                 |
| `sharedJwtKeycloakSecret` | string           | "password123"                                                                                                                                                                                                                                    | User-created synthetic password for logging in users to Keycloak                                                                                                           |

### Additional Parameters

| Name                 | Type   | Default                    | Description                                                                                                                                                                                                                       |
| -------------------- | ------ | -------------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| `useTls`             | bool   | false                      | Should the filter use certs in connecting to key cloak?                                                                                                                                                                           |
| `certPath`           | string | "./certs/server.crt"       | Certificate path                                                                                                                                                                                                                  |
| `keyPath`            | string | "./certs/server.key"       | Keyfile path                                                                                                                                                                                                                      |
| `caPath`             | string | "./certs/intermediate.crt" | Certificate authority or intermediate certificate path                                                                                                                                                                            |
| `insecureSkipVerify` | bool   | false                      | Should calls to keycloak require hostname verification in certs?                                                                                                                                                                  |
| `timeoutMs`          | int    | 1000ms                     | Timeout in milliseconds for the connection between gm-proxy and gm-jwt-keycloak service. Set to a negative number to disable timeouts completely, though **this is not advised** as it can cause an infinite hang in the sidecar. |
| `maxRetries`         | int    | 0                          | Number of retries after failed connection between gm-proxy and keycloak                                                                                                                                                           |
| `retryDelayMs`       | int    | 0                          | Amount of time in milliseconds between each unsuccessful retry                                                                                                                                                                    |
| `cacheLimit`         | int    | 100                        | Maximum number of tokens held in cache. If negative, caching is disabled, **must be > 0 to enable caching**                                                                                                                       |
| `cachedTokenExp`     | int    | 10m                        | Time in minutes to hold tokens in the cache. If negative, caching is disabled, **must be > 0 to enable caching**                                                                                                                  |
| `writeBody`          | bool   | false                      | Should tokens be written to the response body instead of headers?                                                                                                                                                                 |
| `fetchFullToken`     | bool   | false                      | Should the full token be fetched from Keycloak's `/userinfo` endpoint instead of logging in the client from the `/token` endpoint?                                                                                                |
| `authenticateOnly`   | bool   | false                      | If true, filter will only validate incoming tokens and will not reach out to keycloak to create new tokens                                                                                                                        |

## Setting Up PKI Users

In addition to using Keycloak as an OIDC provider for the mesh, the jwt.keycloak filter can also generate JWT tokens for users entering the mesh with certificates. To do this, the following must be configured:

Retrieve an admin-cli client token to make API changes in keycloak. An example of retrieving this with `curl` could be:

```bash
access_token=$(curl -k\
    --data "username=$username&password=$password&grant_type=password&client_id=admin-cli" \
    $keycloakEndpoint/auth/realms/master/protocol/openid-connect/token \
    | jq -r ".access_token")
```

Find a user's email in the certificate, predicated by `cn=`. For example, a user with the certificate metadata `"CN=bob.smith@greymatter.io,OU=,O=,L=,ST=,C="` will have the email `bob.smith@greymatter.io`.

Generate a user's synthetic password using a user's `username` (e.g. `bob.smith`) and `sharedJwtKeycloakSecret` that you have entered and configured in the filter. The algorithm we use for computing the synthetic password is `sha256(sharedJwtKeycloakSecret + username)`. For example, in golang this would be:

```go
password := sharedJwtKeycloakSecret + dn
return fmt.Sprintf("%x", sha256.Sum256([]byte(password)))
```

Create a json file `user.json` and enter in the information from above:

```javascript
{
  "firstName": "bob",
  "lastName": "smith",
  "email": "bob.smith@greymatter.io",
  "enabled": "true",
  "username": "bob.smith",
  "credentials": [
    {
      "type": "password",
      "value": "$PASSWORD",
      "temporary": false
    }
  ]
}
```

Make an API request to Keycloak's users API to create the user using the `access_token` generated above:

```bash
keycloakEndpoint=XXX.XXX.XXX
data=$(cat user.json)
echo $data
curl -k --location --request POST \
    --header 'Content-Type: application/json' \
    --header "Authorization: Bearer $access_token" \
    --data-raw "$data" \
     "$keycloakEndpoint/auth/admin/realms/greymatter/users"
```

The user is now successfully initialized in Keycloak and will be able to be logged in via PKI from the keycloak filter.

## Flow Chart

![](https://lucid.app/publicSegments/view/4d955d4a-10ce-4ef1-866e-59f87053ddc0/image.jpeg)
