Golang Keycloak API Package
This client is based on: go-keycloak
For Questions either raise an issue, or come to the gopher-slack into the channel #gocloak
If u are using the echo framework have a look at gocloak-echo
Benchmarks can be found here
(WIP) https://github.com/Nerzal/gocloak/wiki/Contribute
For release notes please consult the specific releases here
go get github.com/Nerzal/gocloak/v13
import "github.com/Nerzal/gocloak/v13"
client := gocloak.NewClient("https://mycool.keycloak.instance")
ctx := context.Background()
token, err := client.LoginAdmin(ctx, "user", "password", "realmName")
if err != nil {
panic("Something wrong with the credentials or url")
}
user := gocloak.User{
FirstName: gocloak.StringP("Bob"),
LastName: gocloak.StringP("Uncle"),
Email: gocloak.StringP("something@really.wrong"),
Enabled: gocloak.BoolP(true),
Username: gocloak.StringP("CoolGuy"),
}
_, err = client.CreateUser(ctx, token.AccessToken, "realm", user)
if err != nil {
panic("Oh no!, failed to create user :(")
}
client := gocloak.NewClient(hostname)
ctx := context.Background()
token, err := client.LoginClient(ctx, clientID, clientSecret, realm)
if err != nil {
panic("Login failed:"+ err.Error())
}
rptResult, err := client.RetrospectToken(ctx, token.AccessToken, clientID, clientSecret, realm)
if err != nil {
panic("Inspection failed:"+ err.Error())
}
if !*rptResult.Active {
panic("Token is not active")
}
permissions := rptResult.Permissions
// Do something with the permissions ;)
Client has 2 identity fields- id
and clientId
and both are unique in one realm.
id
is generated automatically by Keycloak.clientId
is configured by users inAdd client
page.
To get the clientId
from id
, use GetClients
method with GetClientsParams{ClientID: &clientName}
.
clients, err := c.Client.GetClients(
c.Ctx,
c.JWT.AccessToken,
c.Realm,
gocloak.GetClientsParams{
ClientID: &clientName,
},
)
if err != nil {
panic("List clients failed:"+ err.Error())
}
for _, client := range clients {
return *client.ID, nil
}
GoCloakIface holds all methods a client should fulfil.
client := gocloak.NewClient(serverURL)
restyClient := client.RestyClient()
restyClient.SetDebug(true)
restyClient.SetTLSClientConfig(&tls.Config{ InsecureSkipVerify: true })
For local testing you need to start a docker container. Simply run following commands prior to starting the tests:
docker pull quay.io/keycloak/keycloak
docker run -d \
-e KEYCLOAK_USER=admin \
-e KEYCLOAK_PASSWORD=secret \
-e KEYCLOAK_IMPORT=/tmp/gocloak-realm.json \
-v "`pwd`/testdata/gocloak-realm.json:/tmp/gocloak-realm.json" \
-p 8080:8080 \
--name gocloak-test \
quay.io/keycloak/keycloak:latest -Dkeycloak.profile.feature.upload_scripts=enabled
go test
Or you can run with docker compose using the run-tests script
./run-tests.sh
or
./run-tests.sh <TestCase>
Or you can run the tests on you own keycloak:
export GOCLOAK_TEST_CONFIG=/path/to/gocloak/config.json
All resources created as a result of unit tests will be deleted, except for the test user defined in the configuration file.
To remove running docker container after completion of tests:
docker stop gocloak-test
docker rm gocloak-test
The custom types contain many pointers, so printing them yields mostly pointer values, which aren't much help when debugging your application. For example
someRealmRepresentation := gocloak.RealmRepresentation{
<snip>
}
fmt.Println(someRealmRepresentation)
yields a large set of pointer values
{<nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> 0xc00000e960 <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> 0xc000093cf0 <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> null <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil> <nil>}
For convenience, the String()
interface has been added so you can easily see the contents, even for nested custom types. For example,
fmt.Println(someRealmRepresentation.String())
yields
{
"clients": [
{
"name": "someClient",
"protocolMappers": [
{
"config": {
"bar": "foo",
"ping": "pong"
},
"name": "someMapper"
}
]
},
{
"name": "AnotherClient"
}
],
"displayName": "someRealm"
}
Note that empty parameters are not included, because of the use of omitempty
in the type definitions.