Secured Kratos gRPC APIs
Access the generated microservices over ultra-fast gRPC using Kratos framework, fully authenticated by Keycloak.
Overview
The GO-DUCK CLI generated app binds the Gin REST webserver on port 8080 and simultaneously spins up an optimized Go-Kratos gRPC listener on port 9000. Both the REST and gRPC handlers share the exact same clean Repository architecture underneath, guaranteeing 100% data and logic consistency.
Automatic Compilation
api/v1/*.proto definitions are generated automatically whenever you add an entity or change fields.
Client Connection Example (Go)
This snippet demonstrates how a developer orchestrating another microservice connects securely using Google's clientcredentials to fulfill the Kratos Auth Header standards.
Crucial Architecture Note (Server-to-Server)
If an event triggers a gRPC call asynchronously in the background (meaning there is NO active human user pushing a token from a frontend), the calling microservice must authenticate itself using a Keycloak Service Account.
The calling server sends its own Client ID and Client Secret directly to Keycloak's Token Endpoint using the Client Credentials Grant flow. Keycloak responds with a valid JWT representing the "Server", which is then injected into the gRPC dialer below.
import (
"context"
"log"
"google.golang.org/grpc"
"golang.org/x/oauth2/clientcredentials"
pb "go-duck/api/v1"
)
func main() {
// 1. Configure the Client Credentials Flow
// These values should match your go-duck.security block in application-dev.yml
config := clientcredentials.Config{
ClientID: "internal", // Matches keycloak-client-id
ClientSecret: "internal", // Matches keycloak-secret
TokenURL: "http://localhost:9080/realms/master/protocol/openid-connect/token",
}
// 2. Fetch the token dynamically from Keycloak
tokenSource := config.TokenSource(context.Background())
// 3. Dial the local port 9000 (GoDuck Server)
// grpc.WithPerRPCCredentials injects the automatically updating token into every request!
conn, err := grpc.Dial("localhost:9000", grpc.WithInsecure(), grpc.WithPerRPCCredentials(tokenSource))
if err != nil {
log.Fatalf("did not connect: %v", err)
}
defer conn.Close()
// 4. Initialize the Kratos-generated gRPC Client
client := pb.NewEntityServiceClient(conn)
// 5. Execute Native Network Call over gRPC
res, err := client.GetEntity(context.Background(), &pb.GetEntityRequest{Id: 1})
if err != nil {
log.Fatalf("RPC failed: %v", err)
}
log.Printf("Successfully Received Data Pipeline Object OVER gRPC: %v", res)
}