8000 GitHub - dsheehan167/go-keycloak: golang keycloak client · GitHub
[go: up one dir, main page]

Skip to content

About

golang keycloak client

Resources

Stars

0 stars

Watchers

0 watching

Forks

Latest commit

 

History

5 Commits

Folders and files

NameName
Last commit message
Last commit date
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

keycloak

A Go client for the Keycloak Admin REST API and OIDC endpoints.

Install

go get github.com/dsheehan167/go-keycloak

Requires Go 1.25 or newer.

Quick start

package main

import (
    "context"
    "errors"
    "log"
    "net/http"
    "time"

    "github.com/dsheehan167/go-keycloak"
)

func main() {
    ctx := context.Background()

    c, err := keycloak.NewClient("https://kc.example.com", "my-realm",
        keycloak.WithTimeout(15*time.Second),
    )
    if err != nil {
        log.Fatal(err)
    }

    tok, err := c.GetToken(ctx, keycloak.TokenRequest{
        GrantType:    keycloak.GrantClientCredentials,
        ClientID:     "admin-cli",
        ClientSecret: "<secret>",
    })
    if err != nil {
        log.Fatal(err)
    }
    c.SetAccessToken(tok.AccessToken)

    user, err := c.Users.Get(ctx, "<user-uuid>")
    if err != nil {
        var kc *keycloak.Error
        if errors.As(err, &kc) && kc.StatusCode == http.StatusNotFound {
            log.Printf("no such user")
            return
        }
        log.Fatal(err)
    }
    log.Printf("found %s", user.Username)
}

Client options

NewClient(baseURL, realm string, opts ...Option):

Option Purpose
WithLogger(*zap.Logger) Install a logger. Default zap.NewNop().
WithHTTPClient(*http.Client) Supply a configured client. Overrides WithTimeout and WithInsecureSkipVerify.
WithTimeout(time.Duration) HTTP timeout. Default 30s.
WithInsecureSkipVerify(bool) Disable TLS verification. Development only.
WithAccessToken(string) Seed the bearer token at construction.

baseURL is the scheme + host (and optional path prefix), e.g. https://kc.example.com or https://kc.example.com/auth. Trailing slashes are trimmed.

Tokens can be rotated with c.SetAccessToken(token) at any time; the field is mutex-guarded and safe for concurrent reads.

API

Flat methods on *Client

Method Endpoint
GetToken(ctx, TokenRequest) POST /realms/{realm}/protocol/openid-connect/token
Logout(ctx, LogoutRequest) POST /realms/{realm}/protocol/openid-connect/logout
ListCerts(ctx) GET /realms/{realm}/protocol/openid-connect/certs
ListPermissions(ctx, audience) UMA permission introspection

c.Users

Method Purpose
Get(ctx, userID) Fetch a user
List(ctx, ListUsersOptions) Paginated user search
ListClientRoles(ctx, clientUUID, userID) Client roles mapped to the user
AddClientRoles(ctx, clientUUID, userID, roleIDs) Assign client roles
RemoveClientRoles(ctx, clientUUID, userID, roleIDs) Unassign client roles
ListGroups(ctx, userID) Groups the user belongs to
AddToGroup(ctx, userID, groupID) Add to group
RemoveFromGroup(ctx, userID, groupID) Remove from group

c.Groups

Method Purpose
Get(ctx, groupID) Fetch a group
List(ctx, ListGroupsOptions) Paginated group search
Create(ctx, CreateGroupOptions) Create a group
Update(ctx, groupID, UpdateGroupOptions) Update fields
Delete(ctx, groupID) Delete
ListMembers(ctx, groupID, ListGroupMembersOptions) Users in the group
ListClientRoles(ctx, clientUUID, groupID) Client roles mapped to the group
AddClientRoles(ctx, clientUUID, groupID, roleIDs) Assign client roles
RemoveClientRoles(ctx, clientUUID, groupID, roleIDs) Unassign client roles

c.Roles

Method Purpose
Get(ctx, roleID) Fetch a realm role by id
GetClientRole(ctx, clientUUID, roleName) Fetch a client role by name
ListClientRoles(ctx, clientUUID, ListClientRolesOptions) Roles under a client
CreateClientRole(ctx, clientUUID, CreateClientRoleOptions) Create
UpdateClientRole(ctx, clientUUID, roleName, UpdateClientRoleOptions) Update
DeleteClientRole(ctx, clientUUID, roleName) Delete
ListClientRoleUsers(ctx, clientUUID, roleName, opts) Users carrying a role
ListClientRoleGroups(ctx, clientUUID, roleName, opts) Groups carrying a role

c.Policies

Method Purpose
Get(ctx, clientUUID, policyID) Fetch a policy
List(ctx, clientUUID, ListPoliciesOptions) List client policies
Update(ctx, clientUUID, policyID, UpdatePolicyOptions) Update fields (not membership)
AddRoles(ctx, clientUUID, policyID, []*PolicyRole) Add roles to a role policy
RemoveRoles(ctx, clientUUID, policyID, roleIDs) Drop roles from a role policy

Errors

Every non-2xx response is wrapped in *keycloak.Error carrying the HTTP status, Keycloak's error code, a message, and the raw body. Match with errors.As:

var kc *keycloak.Error
if errors.As(err, &kc) {
    switch kc.StatusCode {
    case http.StatusNotFound:
        // ...
    case http.StatusUnauthorized:
        // token rejected
    }
}

keycloak.ErrNoAccessToken is returned when an admin method is called without a bearer token set. Match with errors.Is to drive a refresh flow:

if errors.Is(err, keycloak.ErrNoAccessToken) {
    tok, _ := c.GetToken(ctx, req)
    c.SetAccessToken(tok.AccessToken)
    // retry
}

Validation errors (missing required fields) return plain error — they signal programmer error, not a recoverable condition.

Partial updates

UpdateClientRoleOptions and UpdatePolicyOptions use pointer fields so callers can distinguish "leave alone" from "set". Use go-utils/utils for literal pointer helpers:

import "github.com/dsheehan167/go-utils/utils"

opts := keycloak.UpdateClientRoleOptions{
    Description: utils.String("new description"),
    // Name is nil → preserved
}

Pagination

Every List*Options embeds Pagination:

roles, err := c.Roles.ListClientRoles(ctx, clientUUID, keycloak.ListClientRolesOptions{
    Pagination: keycloak.Pagination{First: 0, Max: 100},
    Search:     "admin",
})

About

golang keycloak client

Resources

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Contributors

Languages

0