package keycloak
 
import (
	"context"
	"github.com/Nerzal/gocloak/v8"
	"github.com/dgrijalva/jwt-go/v4"
	"log"
	"os"
)
 
type KeycloakInstanceData struct {
 
	ClientPtr      gocloak.GoCloak
	ServerEndpoint string
	User           string
	Password       string
	RealmName      string
	ClientName     string
 
}
 
var kcloak KeycloakInstanceData
 
// Initizalize keycloak instance
func Initialize() {
 
	kcloak.ServerEndpoint = os.Getenv("KCLOAK_ENDPOINT")
	kcloak.User = os.Getenv("KCLOAK_REALM_USER")
	kcloak.Password = os.Getenv("KCLOAK_REALM_PASSWORD")
	kcloak.RealmName = os.Getenv("KCLOAK_REALM_NAME")
	kcloak.ClientName = os.Getenv("KCLOAK_CLIENT_NAME")
 
	kcloak.ClientPtr = gocloak.NewClient(kcloak.ServerEndpoint)
 
}
 
// Check if 'userToken' is valid.
func CheckToken(userToken string) (bool, error) {
 
	_, err := kcloak.ClientPtr.GetUserInfo(context.Background(), userToken, kcloak.RealmName)
	if err != nil {
 
		log.Println(err)
		return false, err
 
	} else {
 
		return true, err
 
	}
}
 
// Getter for admin token.
func GetAdminToken() (*gocloak.JWT, error) {
 
	ctx := context.Background()
	token, err := kcloak.ClientPtr.LoginAdmin(ctx, kcloak.User, kcloak.Password, kcloak.RealmName)
	if err != nil {
		log.Println(err)
	}
 
	return token, err
 
}
 
// Getter for keycloak client instance pointer.
func GetClient() gocloak.GoCloak {
 
	if kcloak.ClientPtr != nil {
		Initialize()
	}
 
	return kcloak.ClientPtr
 
}
 
// Gathers user info from keycloak server using user provided token (userToken parameter)
func GetUserInfo(userToken string) (*gocloak.UserInfo, error) {
 
	ctx := context.Background()
	userInfo, err := kcloak.ClientPtr.GetUserInfo(ctx, userToken, kcloak.RealmName)
	if err != nil {
		log.Println(err)
	}
	return userInfo, err
 
}
 
// Gathers user Roles from keycloak server using 'UserInfo'
func GetUserRoles(userInfo *gocloak.UserInfo) ([]string, error) {
 
	adminToken, err := GetAdminToken()
	if err != nil {
		log.Println(err)
		return nil, err
	}
 
	roleMap, err := kcloak.ClientPtr.GetRoleMappingByUserID(context.Background(), adminToken.AccessToken, kcloak.RealmName, *userInfo.Sub)
	if err != nil {
		log.Println(err)
		return nil, err
	}
 
	rolesMapping := roleMap.RealmMappings
 
	var roles []string
 
	for _, element := range *rolesMapping {
		roles = append(roles, *element.Name)
	}
 
	return roles, nil
}
 
// Parse claims (info) from jwt body into a map. Insecure method, doesn't chek if token is valid.
func GetInsecureTokenClaimMap(tokenString string, clientSecret string) (jwt.MapClaims, error) {
 
	claims := jwt.MapClaims{}
	_, err := jwt.ParseWithClaims(tokenString, claims,
		func(token *jwt.Token) (interface{}, error) {
			return []byte(clientSecret), nil
		})
 
	if err != nil {
		log.Println(err)
		return nil, err
	}
 
	return claims, nil
 
}
 
func GetInsecureInsecureTokenClaimMap(tokenString string) (jwt.MapClaims, error) {
 
	claims := jwt.MapClaims{}
 
	_, err := jwt.ParseWithClaims(tokenString, claims, nil)
 
	return claims, err
 
}

Usage example:

	authHeader := c.GetHeader("Authorization")
	if authHeader == "" {
		c.JSON(http.StatusUnauthorized, gin.H{"status": http.StatusUnauthorized, "message": common.APINoTokenProvided})
		return
	}
 
	// Gets token from 'Authorization' header. Splits the Token from 'Bearer' keyword.
	accessTokenHeader := strings.Split(c.GetHeader("Authorization"), " ")
	if !(len(accessTokenHeader) == 2) {
		c.JSON(http.StatusUnauthorized, gin.H{"status": http.StatusUnauthorized, "message": common.APINoTokenProvided})
		return
	}
 
	// The 'authorization' header value includes the 'bearer' string with a whitespace before the 'Token' string.
	accessToken := accessTokenHeader[1]
 
	// Verify if the token is valid
	isTokenValid, err := keycloak.CheckToken(accessToken)
	if !isTokenValid {
		c.JSON(http.StatusUnauthorized, gin.H{"status": http.StatusUnauthorized, "message": err})
		return
	}

🌱 Back to Garden