GoKeeper is a comprehensive authentication and authorization wrapper for Go applications that integrates with Keycloak. It provides both a standalone service and a library that can be imported into existing projects, along with a JavaScript client for seamless frontend integration.
- Keycloak Integration: Full wrapper around the go-keycloak library
- Secure Cookie Management: Automatic chunking for large credential data
- JavaScript Client: Clean, modern JavaScript class with read-only properties
- Resource-Based Authorization: Granular access control for different resources
- Standalone or Library: Use as a service or import into existing projects
- Auto-Authentication: Automatic session validation and renewal
- Mock Service: Built-in mock for development and testing
- CORS Support: Cross-origin request handling
- Role-Based Access: Fine-grained role checking capabilities
# Clone the project to your GolandProjects directory
cp -r gokeeper (https://github.com/TBNilles/gokeeper)
cd gokeeper
go mod tidy
# Run with default configuration (uses mock Keycloak)
go run main.go
# Run with custom configuration
KEYCLOAK_URL=http://localhost:8080 \
KEYCLOAK_REALM=myrealm \
KEYCLOAK_CLIENT_ID=myapp \
KEYCLOAK_CLIENT_SECRET=secret \
go run main.go
Open your browser to http://localhost:8081
to see the interactive demo.
POST /api/auth/authenticate
- Login with username/passwordGET /api/auth/validate
- Validate current sessionPOST /api/auth/resource-authenticate
- Check resource accessPOST /api/auth/logout
- Logout user
GET /api/protected/profile
- Get current user (requires auth)GET /api/admin/users
- Admin only endpointGET /api/public/info
- Public endpoint
The JavaScript client provides a clean, modern interface for authentication:
// Initialize GoKeeper client
const auth = new GoKeeper();
// Authenticate user
const success = await auth.authenticate('username', 'password');
// Access user properties (read-only)
if (auth.isAuthenticated) {
console.log(auth.user.first_name);
console.log(auth.user.last_name);
console.log(auth.user.roles); // Array of roles
}
// Check resource access
const canAccess = await auth.resourceAuthenticate('admin', 'read');
// Check roles
const isAdmin = auth.hasRole('admin');
const hasAnyRole = auth.hasAnyRole(['admin', 'user']);
// Event listeners
auth.on('login', (user) => console.log('User logged in:', user));
auth.on('logout', () => console.log('User logged out'));
auth.on('session-expired', () => console.log('Session expired'));
Configure GoKeeper using environment variables:
# Keycloak Configuration
KEYCLOAK_URL=http://localhost:8080
KEYCLOAK_REALM=master
KEYCLOAK_CLIENT_ID=gokeeper
KEYCLOAK_CLIENT_SECRET=your-client-secret
# Server Configuration
SERVER_PORT=8081
SERVER_HOST=localhost
# Cookie Configuration
COOKIE_DOMAIN=localhost
COOKIE_SECURE=false
COOKIE_MAX_AGE=3600
COOKIE_CHUNK_SIZE=4000
# Security
JWT_SECRET=your-secret-key-change-in-production
# Development
DEBUG=true
You can import GoKeeper into your existing Go project:
package main
import (
"gokeeper/auth"
"gokeeper/config"
"gokeeper/web"
"github.com/gin-gonic/gin"
)
func main() {
// Load configuration
cfg := config.New()
// Create auth service
authService := auth.NewKeycloakService(
cfg.KeycloakURL,
cfg.KeycloakRealm,
cfg.KeycloakClientID,
cfg.KeycloakSecret,
cfg.JWTSecret,
)
// Create cookie manager
cookieManager := auth.NewCookieManager(
cfg.CookieDomain,
cfg.CookieSecure,
cfg.CookieMaxAge,
cfg.CookieChunkSize,
)
// Create handler
handler := web.NewHandler(authService, cookieManager, cfg)
// Setup routes
r := gin.Default()
handler.SetupRoutes(r)
// Add your own routes
protected := r.Group("/my-api")
protected.Use(handler.AuthRequired())
{
protected.GET("/data", func(c *gin.Context) {
user, _ := c.Get("user")
// Your protected endpoint logic
})
}
r.Run(":8080")
}
GoKeeper automatically handles large authentication data by splitting it into multiple cookies when needed:
- Maximum 4KB per cookie (configurable)
- Automatic reconstruction of chunked data
- Secure cookie attributes (HttpOnly, SameSite)
- Automatic cleanup of expired chunks
Define granular access controls:
// Check if user can read admin resources
authorized, err := authService.ResourceAuthenticate(user, "admin", "read")
// Check if user can write to user resources
authorized, err := authService.ResourceAuthenticate(user, "user", "write")
// JavaScript client
auth.hasRole('admin')
auth.hasAnyRole(['admin', 'moderator'])
// Go middleware
router.Use(handler.RequireRole("admin", "moderator"))
For development and testing, GoKeeper includes a mock Keycloak service:
// Use mock service
authService := auth.NewMockKeycloakService()
// Test users available:
// Username: admin, Password: any (roles: admin, user)
// Username: user, Password: any (roles: user)
The example page includes comprehensive testing tools:
- Login/logout functionality
- Resource access testing
- Role checking
- Session management
- API demonstrations
gokeeper/
βββ config/
β βββ config.go # Configuration management
βββ auth/
β βββ auth.go # Core authentication interfaces
β βββ cookies.go # Cookie chunking and management
β βββ keycloak.go # Keycloak service implementation
βββ web/
β βββ handlers.go # HTTP handlers and middleware
βββ static/
β βββ gokeeper.js # JavaScript client library
β βββ example.html # Demo/example page
βββ examples/
β βββ integration/ # Integration examples
βββ main.go # Standalone service entry point
βββ go.mod # Go module definition
βββ README.md # This file
// Add GoKeeper to existing app
func addAuth(r *gin.Engine) {
cfg := config.New()
authService := auth.NewKeycloakService(/* config */)
cookieManager := auth.NewCookieManager(/* config */)
handler := web.NewHandler(authService, cookieManager, cfg)
// Add auth routes
handler.SetupRoutes(r)
// Protect existing routes
api := r.Group("/api")
api.Use(handler.AuthRequired())
// ... your existing routes
}
<!DOCTYPE html>
<html>
<head>
<script src="/static/gokeeper.js"></script>
</head>
<body>
<script>
const auth = new GoKeeper();
// Auto-validate session on page load
auth.autoAuthenticate().then(isAuthenticated => {
if (!isAuthenticated) {
// Show login form
} else {
// Show authenticated content
document.getElementById('username').textContent = auth.user.first_name;
}
});
</script>
</body>
</html>
Extend the resource authentication logic:
// In your KeycloakService implementation
func (ks *KeycloakService) checkResourceAccess(user *User, resource string, action string) bool {
// Custom business logic here
switch resource {
case "financial-data":
return user.HasAnyRole([]string{"finance", "admin"})
case "hr-records":
return user.HasRole("hr") || user.HasRole("admin")
default:
return false
}
}
Create custom authentication middleware:
func CustomAuthMiddleware(authService auth.AuthService) gin.HandlerFunc {
return func(c *gin.Context) {
token := c.GetHeader("Authorization")
if token == "" {
c.JSON(401, gin.H{"error": "No token provided"})
c.Abort()
return
}
user, err := authService.Validate(token)
if err != nil {
c.JSON(401, gin.H{"error": "Invalid token"})
c.Abort()
return
}
c.Set("user", user)
c.Next()
}
}
- Cookie size limits: If you're getting cookie errors, reduce
COOKIE_CHUNK_SIZE
- CORS issues: Enable debug mode or configure proper origins
- Keycloak connection: Verify
KEYCLOAK_URL
and network connectivity - Session expiry: Check token expiration and refresh logic
Enable debug mode for detailed logging:
DEBUG=true go run main.go
This project is licensed under the MIT License. See the LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.
If you have questions or need help:
- Check the example implementation in
static/example.html
- Review the API documentation above
- Enable debug mode for detailed logging
- Create an issue on the project repository
GoKeeper - Secure, Simple, Scalable Authentication for Go & JavaScript π