Keycloak, administration REST

Prérequis

Démarrer Keycloak (testé avec la version 20)

docker run --publish 8888:8080 \
           --env KEYCLOAK_ADMIN=admin --env KEYCLOAK_ADMIN_PASSWORD=admin \
           --name kc-example --rm \
           --detach keycloak/keycloak:20.0 start-dev

Initialisation

Initialisation d’un Realm avec un Client et un User.

base_url=http://localhost:8888

# Authenticate and get token
token=$(curl -s -d "client_id=admin-cli" -d "username=admin" -d "password=admin" -d "grant_type=password" \
             "$base_url/realms/master/protocol/openid-connect/token" \
          | jq -r .access_token)

# Create realm
curl -H "Authorization: bearer $token" -H "Content-Type: application/json" \
     -d '{"realm":"sewatech", "enabled":true}' \
     $base_url/admin/realms

# Create private client (with secret)
curl -H "Authorization: bearer $token" -H "Content-Type: application/json"  \
     -d '{"clientId":"private-jtips", "enabled":true, "standardFlowEnabled":true,  \
          "directAccessGrantsEnabled":true, "rootUrl":"http://localhost:4200",  \
          "redirectUris":["http://localhost:4200/*"], "secret":"example-secret"}'  \
     $base_url/admin/realms/sewatech/clients
# Create public client (without secret)
curl -H "Authorization: bearer $token" -H "Content-Type: application/json"  \
     -d '{"clientId":"public-jtips", "enabled":true, "standardFlowEnabled":true,  \
          "directAccessGrantsEnabled":true, "rootUrl":"http://localhost:4200",  \
          "redirectUris":["http://localhost:4200/*"], "publicClient":true}'  \
     $base_url/admin/realms/sewatech/clients

# Create user with password
curl -H "Authorization: bearer $token" -H "Content-Type: application/json"  \
     -d '{"username":"jtips", "enabled":true,  \
          "credentials": [{"type":"password","value":"jtipspwd","temporary":false}]}'  \
          $base_url/admin/realms/sewatech/users

Identifiants

Pour retrouver l’id du client:

clientId=$(curl -s -H "Authorization: bearer $token" -H "Content-Type: application/json" \
                $base_url/admin/realms/sewatech/clients?clientId=public-jtips \
           | jq -r .[0].id)

Direct flow

Le direct flow est activé par défaut. On peut l’utiliser dans Postman ou en ligne de commande.

Authentication to keycloack from Postman
curl -d "client_id=public-jtips" -d "grant_type=password" \
     -d "username=jtips" -d "password=jtipspwd" \
     $base_url/realms/sewatech/protocol/openid-connect/token

Ce flow peut être désactivé au niveau du client.

client=$(curl -s -H "Authorization: bearer $token"  $base_url/admin/realms/sewatech/clients/$clientId  \
          | jq -r '.directAccessGrantsEnabled=false')
curl -s -H "Authorization: bearer $token" -H "Content-Type: application/json" -X PUT  \
     -d "$client" $base_url/admin/realms/sewatech/clients/$clientId

X.509 direct flow

Configuration du direct flow (utilisé depuis postman) pour authentification par un certificat X.509.

Attention, il y a des prérequis sur les certificats et sur le démarrage de keycloak.

# base_url et token sont initialisé comme ci-dessus

# X509 direct flow
curl --silent --header "Authorization: bearer $token" --header "Content-Type: application/json" \
     --data '{"alias":"x509 direct grant", "providerId": "basic-flow", "topLevel": true, \
              "authenticationExecutions": [{    \
              "authenticator\": "direct-grant-auth-x509-username",  \
              "requirement": "REQUIRED",  \
              "priority": 0, \
              "userSetupAllowed": false, \
              "autheticatorFlow": false \
              }]}'    \
     $base_url/admin/realms/sewatech/authentication/flows

curl --silent --header "Authorization: bearer $token" --header "Content-Type: application/json" \
      --data '{"provider":"direct-grant-auth-x509-username"}' \
     $base_url/admin/realms/sewatech/authentication/flows/x509%20direct%20grant/executions/execution

curl -X PUT --silent --header "Authorization: bearer $token" --header "Content-Type: application/json" \
     --data '{"directGrantFlow": "x509 direct grant"}' \
     $base_url/admin/realms/sewatech

execution_id=$(curl --silent --header "Authorization: bearer $token" --header "Content-Type: application/json" \
                    $base_url/admin/realms/sewatech/authentication/flows/x509%20direct%20grant/executions \
               | jq -r .[0].id)

...

SubjectDN

L’attribut CN du SubjectDN sert de username.

...

curl --silent --header "Authorization: bearer $token" --header "Content-Type: application/json" \
     --data '{"config": {"x509-cert-auth.mapping-source-selection": "Subject's Common Name", \
                         "x509-cert-auth.canonical-dn-enabled": "true", \
                         "x509-cert-auth.user-attribute-name": "usercertificate", \
                         "x509-cert-auth.confirmation-page-disallowed": true}, \
                         "alias":"X509"}' \
     $base_url/admin/realms/sewatech/authentication/executions/$execution_id/config

Serial et IssuerDN

Dans cet exemple, la correspondance entre le certificat et l’utilisateur est faite par le n° de série et le DN de l’émetteur.

...

curl --silent --header "Authorization: bearer $token" --header "Content-Type: application/json"  \
     --data '{"config": {"x509-cert-auth.mapping-source-selection":"Certificate Serial Number and IssuerDN", \
                         "x509-cert-auth.mapper-selection":"Custom Attribute Mapper",
                         "x509-cert-auth.mapper-selection.user-attribute-name":"SerialNumber##IssuerDN", \
                         "x509-cert-auth.canonical-dn-enabled":true, \
                         "x509-cert-auth.user-attribute-name":"usercertificate", \
                         "x509-cert-auth.confirmation-page-disallowed":true}, \
              "alias":"X509serial"}' \
     $base_url/admin/realms/sewatech/authentication/executions/$execution_id/config

Pour être trouvé, l’utilisateur doit avoir par 2 attributs personnalisés : SerialNumber et IssuerDN.

# Create user with Serial and IssuerDN
curl --silent --header "Authorization: bearer $token" --header "Content-Type: application/json"  \
     --data '{"username":"swserialuser", "enabled":true, \
              "attributes":{"SerialNumber":["2"],"IssuerDN":["o=sewatech,cn=client-ca"]}}'  \
     $base_url/admin/realms/sewatech/users

L’IssuerDN est en minuscules parce qu’on a activé le format canonical.