chore: import upstream snapshot with attribution
govulncheck / govulncheck (push) Has been cancelled
Lint / golangci-lint (push) Has been cancelled
Run Tests / Unit Tests (push) Has been cancelled
Run Tests / Etcd Integration Tests (push) Has been cancelled
Harness (E2E) / Harnesses (mock LLM) (push) Has been cancelled
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Has been cancelled
govulncheck / govulncheck (push) Has been cancelled
Lint / golangci-lint (push) Has been cancelled
Run Tests / Unit Tests (push) Has been cancelled
Run Tests / Etcd Integration Tests (push) Has been cancelled
Harness (E2E) / Harnesses (mock LLM) (push) Has been cancelled
Harness (E2E) / Provider harnesses (live LLM conformance) (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,12 @@
|
||||
# Compiled binaries
|
||||
server/server
|
||||
client/client
|
||||
|
||||
# Test binaries
|
||||
*.test
|
||||
|
||||
# Output files
|
||||
*.out
|
||||
|
||||
# Temporary files
|
||||
*.tmp
|
||||
@@ -0,0 +1,396 @@
|
||||
# Auth Example
|
||||
|
||||
This example demonstrates how to use the auth wrappers to protect your microservices with authentication and authorization.
|
||||
|
||||
## Overview
|
||||
|
||||
The example includes:
|
||||
|
||||
- **Server** - A Greeter service with:
|
||||
- Protected endpoint: `Greeter.Hello` (requires auth)
|
||||
- Public endpoint: `Greeter.Health` (no auth required)
|
||||
|
||||
- **Client** - Makes calls to the server:
|
||||
- With authentication (successful)
|
||||
- Without authentication (fails as expected)
|
||||
|
||||
## Architecture
|
||||
|
||||
```
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Client │
|
||||
│ ┌────────────────────────────────┐ │
|
||||
│ │ AuthClient Wrapper │ │
|
||||
│ │ - Adds Bearer token │ │
|
||||
│ │ - To all requests │ │
|
||||
│ └────────────────────────────────┘ │
|
||||
└──────────────┬──────────────────────────┘
|
||||
│ RPC with Authorization: Bearer <token>
|
||||
│
|
||||
▼
|
||||
┌─────────────────────────────────────────┐
|
||||
│ Server │
|
||||
│ ┌────────────────────────────────┐ │
|
||||
│ │ AuthHandler Wrapper │ │
|
||||
│ │ - Extracts token │ │
|
||||
│ │ - Verifies with auth.Inspect()│ │
|
||||
│ │ - Checks with rules.Verify() │ │
|
||||
│ │ - Returns 401/403 if denied │ │
|
||||
│ └────────────────────────────────┘ │
|
||||
│ │ │
|
||||
│ ▼ │
|
||||
│ ┌────────────────────────────────┐ │
|
||||
│ │ Handler (Greeter.Hello) │ │
|
||||
│ │ - Gets account from context │ │
|
||||
│ │ - Processes request │ │
|
||||
│ └────────────────────────────────┘ │
|
||||
└─────────────────────────────────────────┘
|
||||
```
|
||||
|
||||
## Files
|
||||
|
||||
```
|
||||
examples/auth/
|
||||
├── README.md # This file
|
||||
├── proto/
|
||||
│ ├── greeter.proto # Service definition
|
||||
│ └── greeter.pb.go # Generated Go code
|
||||
├── server/
|
||||
│ └── main.go # Protected service
|
||||
└── client/
|
||||
└── main.go # Client with auth
|
||||
```
|
||||
|
||||
## Running the Example
|
||||
|
||||
### 1. Start the Server
|
||||
|
||||
```bash
|
||||
cd server
|
||||
go run main.go
|
||||
```
|
||||
|
||||
The server will:
|
||||
- Start the Greeter service
|
||||
- Apply auth wrapper to protect endpoints
|
||||
- Generate a test token and print it
|
||||
|
||||
Output:
|
||||
```
|
||||
=== Test Token Generated ===
|
||||
Use this token to test the client:
|
||||
TOKEN=eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9... go run client/main.go
|
||||
|
||||
2026/02/11 10:00:00 Server [greeter] Listening on [::]:54321
|
||||
```
|
||||
|
||||
### 2. Run the Client (With Auth)
|
||||
|
||||
In a new terminal:
|
||||
|
||||
```bash
|
||||
cd client
|
||||
TOKEN=<token-from-server> go run main.go
|
||||
```
|
||||
|
||||
Output:
|
||||
```
|
||||
=== Test 1: Protected endpoint WITH auth ===
|
||||
Response: Hello, test-user!
|
||||
|
||||
=== Test 2: Public endpoint (no auth needed) ===
|
||||
Health Status: ok
|
||||
|
||||
=== Test 3: Protected endpoint WITHOUT auth (should fail) ===
|
||||
Expected error: {"id":"greeter","code":401,"detail":"missing authorization token","status":"Unauthorized"}
|
||||
```
|
||||
|
||||
### 3. Run the Client (Without Auth)
|
||||
|
||||
```bash
|
||||
cd client
|
||||
go run main.go
|
||||
```
|
||||
|
||||
This will auto-generate a token for testing.
|
||||
|
||||
## Code Walkthrough
|
||||
|
||||
### Server Setup
|
||||
|
||||
```go
|
||||
// 1. Create auth provider
|
||||
// For this example we use the noop auth (accepts all tokens)
|
||||
// In production, use JWT or a custom auth provider
|
||||
authProvider := noop.NewAuth()
|
||||
|
||||
// 2. Create authorization rules
|
||||
rules := auth.NewRules()
|
||||
rules.Grant(&auth.Rule{
|
||||
ID: "public-health",
|
||||
Scope: "",
|
||||
Resource: &auth.Resource{Endpoint: "Greeter.Health"},
|
||||
Access: auth.AccessGranted,
|
||||
})
|
||||
|
||||
// 3. Wrap service with auth handler
|
||||
service := micro.NewService(
|
||||
micro.Name("greeter"),
|
||||
micro.WrapHandler(
|
||||
authWrapper.AuthHandler(authWrapper.HandlerOptions{
|
||||
Auth: authProvider,
|
||||
Rules: rules,
|
||||
SkipEndpoints: []string{"Greeter.Health"},
|
||||
}),
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
### Client Setup
|
||||
|
||||
```go
|
||||
// 1. Get or generate token
|
||||
token := os.Getenv("TOKEN")
|
||||
|
||||
// 2. Wrap client with auth
|
||||
service := micro.NewService(
|
||||
micro.Name("greeter.client"),
|
||||
micro.WrapClient(
|
||||
authWrapper.FromToken(token),
|
||||
),
|
||||
)
|
||||
|
||||
// 3. Make calls (token automatically added)
|
||||
greeterClient := pb.NewGreeterService("greeter", service.Client())
|
||||
rsp, err := greeterClient.Hello(ctx, &pb.Request{Name: "John"})
|
||||
```
|
||||
|
||||
### Handler Implementation
|
||||
|
||||
```go
|
||||
func (g *Greeter) Hello(ctx context.Context, req *pb.Request, rsp *pb.Response) error {
|
||||
// Get account from context (added by auth wrapper)
|
||||
acc, ok := auth.AccountFromContext(ctx)
|
||||
if !ok {
|
||||
return errors.Unauthorized("greeter", "authentication required")
|
||||
}
|
||||
|
||||
rsp.Msg = "Hello, " + acc.ID + "!"
|
||||
return nil
|
||||
}
|
||||
```
|
||||
|
||||
## Auth Wrapper Features
|
||||
|
||||
### Server Wrapper (`AuthHandler`)
|
||||
|
||||
- **Token Extraction**: Reads `Authorization: Bearer <token>` from metadata
|
||||
- **Token Verification**: Validates token using `auth.Inspect()`
|
||||
- **Authorization**: Checks permissions using `rules.Verify()`
|
||||
- **Context Injection**: Adds account to context for handlers
|
||||
- **Error Handling**: Returns 401/403 with clear error messages
|
||||
- **Skip Endpoints**: Allows public endpoints without auth
|
||||
|
||||
### Client Wrapper (`AuthClient`)
|
||||
|
||||
- **Automatic Token Injection**: Adds Bearer token to all requests
|
||||
- **Context-Aware**: Can extract account from context
|
||||
- **Static Token**: Use `FromToken()` for pre-generated tokens
|
||||
- **Dynamic Token**: Use `FromContext()` to generate per-request
|
||||
|
||||
## Auth Strategies
|
||||
|
||||
### 1. All Endpoints Protected
|
||||
|
||||
```go
|
||||
micro.WrapHandler(
|
||||
authWrapper.AuthRequired(authProvider, rules),
|
||||
)
|
||||
```
|
||||
|
||||
### 2. Some Public Endpoints
|
||||
|
||||
```go
|
||||
micro.WrapHandler(
|
||||
authWrapper.PublicEndpoints(authProvider, rules, []string{
|
||||
"Health.Check",
|
||||
"Status.Version",
|
||||
}),
|
||||
)
|
||||
```
|
||||
|
||||
### 3. Optional Auth (Extract but Don't Enforce)
|
||||
|
||||
```go
|
||||
micro.WrapHandler(
|
||||
authWrapper.AuthOptional(authProvider),
|
||||
)
|
||||
```
|
||||
|
||||
## Authorization Rules
|
||||
|
||||
### Grant Public Access
|
||||
|
||||
```go
|
||||
rules.Grant(&auth.Rule{
|
||||
ID: "public",
|
||||
Scope: "", // No scope = public
|
||||
Resource: &auth.Resource{Endpoint: "Health.Check"},
|
||||
Access: auth.AccessGranted,
|
||||
})
|
||||
```
|
||||
|
||||
### Require Authentication
|
||||
|
||||
```go
|
||||
rules.Grant(&auth.Rule{
|
||||
ID: "authenticated",
|
||||
Scope: "*", // Any authenticated user
|
||||
Resource: &auth.Resource{Endpoint: "*"},
|
||||
Access: auth.AccessGranted,
|
||||
})
|
||||
```
|
||||
|
||||
### Require Specific Scope
|
||||
|
||||
```go
|
||||
rules.Grant(&auth.Rule{
|
||||
ID: "admin-only",
|
||||
Scope: "admin", // Only admin scope
|
||||
Resource: &auth.Resource{Endpoint: "Admin.*"},
|
||||
Access: auth.AccessGranted,
|
||||
})
|
||||
```
|
||||
|
||||
### Deny Access
|
||||
|
||||
```go
|
||||
rules.Grant(&auth.Rule{
|
||||
ID: "deny-delete",
|
||||
Scope: "*",
|
||||
Resource: &auth.Resource{Endpoint: "User.Delete"},
|
||||
Access: auth.AccessDenied,
|
||||
Priority: 100, // Higher priority = evaluated first
|
||||
})
|
||||
```
|
||||
|
||||
## Testing Without Server
|
||||
|
||||
You can test auth logic without a running server:
|
||||
|
||||
```go
|
||||
import "go-micro.dev/v5/auth/noop"
|
||||
|
||||
// Create auth provider (noop for testing)
|
||||
authProvider := noop.NewAuth()
|
||||
|
||||
// Generate account
|
||||
acc, _ := authProvider.Generate("test-user", auth.WithScopes("admin"))
|
||||
|
||||
// Generate token
|
||||
token, _ := authProvider.Token(auth.WithCredentials(acc.ID, acc.Secret))
|
||||
|
||||
// Verify token
|
||||
verified, _ := authProvider.Inspect(token.AccessToken)
|
||||
fmt.Println(verified.ID) // Returns a generated UUID
|
||||
```
|
||||
|
||||
## Production Considerations
|
||||
|
||||
### 1. Use Production Auth Provider
|
||||
|
||||
The noop auth provider (`auth.NewAuth()`) is for development only. It accepts any token.
|
||||
|
||||
For production, implement a proper auth provider or use the JWT implementation:
|
||||
|
||||
```go
|
||||
// Option 1: Implement custom auth.Auth interface
|
||||
type MyAuth struct {
|
||||
// Your implementation
|
||||
}
|
||||
|
||||
func (m *MyAuth) Generate(id string, opts ...auth.GenerateOption) (*auth.Account, error) {
|
||||
// Generate real accounts
|
||||
}
|
||||
|
||||
func (m *MyAuth) Inspect(token string) (*auth.Account, error) {
|
||||
// Verify real tokens (JWT, OAuth, etc.)
|
||||
}
|
||||
|
||||
// Option 2: Use JWT auth (requires jwt package implementation)
|
||||
// Note: The jwt package in auth/jwt depends on an external plugin
|
||||
// You may need to implement your own JWT auth or use a third-party library
|
||||
```
|
||||
|
||||
### 3. Add Gateway Auth
|
||||
|
||||
If using HTTP gateway:
|
||||
|
||||
```go
|
||||
// Add auth to HTTP gateway
|
||||
http.Handle("/", gateway.Handler(
|
||||
gateway.WithAuth(authProvider),
|
||||
))
|
||||
```
|
||||
|
||||
### 4. Service-to-Service Auth
|
||||
|
||||
Services calling other services:
|
||||
|
||||
```go
|
||||
// Service A calls Service B with its own token
|
||||
client := micro.NewService(
|
||||
micro.WrapClient(
|
||||
authWrapper.FromContext(authProvider),
|
||||
),
|
||||
)
|
||||
```
|
||||
|
||||
### 5. Token Refresh
|
||||
|
||||
```go
|
||||
// Check if token is expiring
|
||||
if time.Until(token.Expiry) < 5*time.Minute {
|
||||
token, _ = authProvider.Token(auth.WithToken(token.RefreshToken))
|
||||
}
|
||||
```
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
### Error: "missing authorization token"
|
||||
|
||||
- **Cause**: Client didn't send Authorization header
|
||||
- **Fix**: Wrap client with `authWrapper.FromToken(token)`
|
||||
|
||||
### Error: "invalid token"
|
||||
|
||||
- **Cause**: Token is expired or malformed
|
||||
- **Fix**: Generate a new token
|
||||
|
||||
### Error: "access denied"
|
||||
|
||||
- **Cause**: Account doesn't have required permissions
|
||||
- **Fix**: Check authorization rules with `rules.List()`
|
||||
|
||||
### Error: "token verification failed"
|
||||
|
||||
- **Cause**: Server can't verify token (wrong keys, expired, etc.)
|
||||
- **Fix**: Ensure server and client use same auth provider
|
||||
|
||||
## Next Steps
|
||||
|
||||
- Read the [Auth Documentation](/docs/auth)
|
||||
- Explore [JWT Auth](/auth/jwt)
|
||||
- Try [Custom Auth Provider](/examples/auth/custom)
|
||||
- See [Multi-Tenant Auth](/examples/auth/multi-tenant)
|
||||
|
||||
## Summary
|
||||
|
||||
The auth wrappers make it easy to:
|
||||
|
||||
1. **Protect services**: Add `WrapHandler(AuthHandler(...))`
|
||||
2. **Add authentication to clients**: Add `WrapClient(FromToken(...))`
|
||||
3. **Control access**: Define rules with `rules.Grant()`
|
||||
4. **Access account info**: Use `auth.AccountFromContext(ctx)`
|
||||
|
||||
That's it! Your microservices now have enterprise-grade authentication and authorization.
|
||||
@@ -0,0 +1,83 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"go-micro.dev/v6"
|
||||
"go-micro.dev/v6/auth"
|
||||
"go-micro.dev/v6/auth/noop"
|
||||
"go-micro.dev/v6/client"
|
||||
authWrapper "go-micro.dev/v6/wrapper/auth"
|
||||
|
||||
pb "go-micro.dev/v6/examples/auth/proto"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// Get token from environment or generate one
|
||||
token := os.Getenv("TOKEN")
|
||||
|
||||
// Create auth provider (same as server)
|
||||
authProvider := noop.NewAuth()
|
||||
|
||||
// If no token provided, generate one
|
||||
if token == "" {
|
||||
log.Println("No TOKEN env var provided, generating test token...")
|
||||
acc, err := authProvider.Generate("test-user")
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
t, err := authProvider.Token(auth.WithCredentials(acc.ID, acc.Secret))
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
token = t.AccessToken
|
||||
log.Printf("Generated token: %s\n", token)
|
||||
}
|
||||
|
||||
// Create service with auth client wrapper
|
||||
service := micro.NewService("greeter.client", micro.WrapClient(
|
||||
authWrapper.FromToken(token), // Add token to all requests
|
||||
),
|
||||
)
|
||||
|
||||
service.Init()
|
||||
|
||||
// Create greeter client
|
||||
greeterClient := pb.NewGreeterService("greeter", service.Client())
|
||||
|
||||
// Test 1: Call protected endpoint (Hello) with auth
|
||||
fmt.Println("\n=== Test 1: Protected endpoint WITH auth ===")
|
||||
rsp, err := greeterClient.Hello(context.Background(), &pb.Request{Name: "John"})
|
||||
if err != nil {
|
||||
log.Printf("Error: %v", err)
|
||||
} else {
|
||||
fmt.Printf("Response: %s\n", rsp.Msg)
|
||||
}
|
||||
|
||||
// Test 2: Call public endpoint (Health) without auth
|
||||
fmt.Println("\n=== Test 2: Public endpoint (no auth needed) ===")
|
||||
// Create client without auth wrapper for this test
|
||||
plainClient := client.NewClient()
|
||||
plainGreeterClient := pb.NewGreeterService("greeter", plainClient)
|
||||
|
||||
healthRsp, err := plainGreeterClient.Health(context.Background(), &pb.HealthRequest{})
|
||||
if err != nil {
|
||||
log.Printf("Error: %v", err)
|
||||
} else {
|
||||
fmt.Printf("Health Status: %s\n", healthRsp.Status)
|
||||
}
|
||||
|
||||
// Test 3: Call protected endpoint WITHOUT auth (should fail)
|
||||
fmt.Println("\n=== Test 3: Protected endpoint WITHOUT auth (should fail) ===")
|
||||
_, err = plainGreeterClient.Hello(context.Background(), &pb.Request{Name: "John"})
|
||||
if err != nil {
|
||||
fmt.Printf("Expected error: %v\n", err)
|
||||
} else {
|
||||
fmt.Println("Unexpected: Call succeeded without auth!")
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,144 @@
|
||||
// Code generated by protoc-gen-go. DO NOT EDIT.
|
||||
// source: greeter.proto
|
||||
|
||||
package greeter
|
||||
|
||||
import (
|
||||
context "context"
|
||||
fmt "fmt"
|
||||
client "go-micro.dev/v6/client"
|
||||
server "go-micro.dev/v6/server"
|
||||
)
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ = fmt.Errorf
|
||||
|
||||
type Request struct {
|
||||
Name string `protobuf:"bytes,1,opt,name=name,proto3" json:"name,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Request) Reset() { *m = Request{} }
|
||||
func (m *Request) String() string { return fmt.Sprintf("Request{Name:%s}", m.Name) }
|
||||
func (*Request) ProtoMessage() {}
|
||||
|
||||
func (m *Request) GetName() string {
|
||||
if m != nil {
|
||||
return m.Name
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type Response struct {
|
||||
Msg string `protobuf:"bytes,1,opt,name=msg,proto3" json:"msg,omitempty"`
|
||||
}
|
||||
|
||||
func (m *Response) Reset() { *m = Response{} }
|
||||
func (m *Response) String() string { return fmt.Sprintf("Response{Msg:%s}", m.Msg) }
|
||||
func (*Response) ProtoMessage() {}
|
||||
|
||||
func (m *Response) GetMsg() string {
|
||||
if m != nil {
|
||||
return m.Msg
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
type HealthRequest struct{}
|
||||
|
||||
func (m *HealthRequest) Reset() { *m = HealthRequest{} }
|
||||
func (m *HealthRequest) String() string { return "HealthRequest{}" }
|
||||
func (*HealthRequest) ProtoMessage() {}
|
||||
|
||||
type HealthResponse struct {
|
||||
Status string `protobuf:"bytes,1,opt,name=status,proto3" json:"status,omitempty"`
|
||||
}
|
||||
|
||||
func (m *HealthResponse) Reset() { *m = HealthResponse{} }
|
||||
func (m *HealthResponse) String() string { return fmt.Sprintf("HealthResponse{Status:%s}", m.Status) }
|
||||
func (*HealthResponse) ProtoMessage() {}
|
||||
|
||||
func (m *HealthResponse) GetStatus() string {
|
||||
if m != nil {
|
||||
return m.Status
|
||||
}
|
||||
return ""
|
||||
}
|
||||
|
||||
func init() {
|
||||
// Types registered
|
||||
}
|
||||
|
||||
// Reference imports to suppress errors if they are not otherwise used.
|
||||
var _ context.Context
|
||||
var _ client.Option
|
||||
var _ server.Option
|
||||
|
||||
// Client API for Greeter service
|
||||
|
||||
type GreeterService interface {
|
||||
Hello(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error)
|
||||
Health(ctx context.Context, in *HealthRequest, opts ...client.CallOption) (*HealthResponse, error)
|
||||
}
|
||||
|
||||
type greeterService struct {
|
||||
c client.Client
|
||||
name string
|
||||
}
|
||||
|
||||
func NewGreeterService(name string, c client.Client) GreeterService {
|
||||
return &greeterService{
|
||||
c: c,
|
||||
name: name,
|
||||
}
|
||||
}
|
||||
|
||||
func (c *greeterService) Hello(ctx context.Context, in *Request, opts ...client.CallOption) (*Response, error) {
|
||||
req := c.c.NewRequest(c.name, "Greeter.Hello", in)
|
||||
out := new(Response)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
func (c *greeterService) Health(ctx context.Context, in *HealthRequest, opts ...client.CallOption) (*HealthResponse, error) {
|
||||
req := c.c.NewRequest(c.name, "Greeter.Health", in)
|
||||
out := new(HealthResponse)
|
||||
err := c.c.Call(ctx, req, out, opts...)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return out, nil
|
||||
}
|
||||
|
||||
// Server API for Greeter service
|
||||
|
||||
type GreeterHandler interface {
|
||||
Hello(context.Context, *Request, *Response) error
|
||||
Health(context.Context, *HealthRequest, *HealthResponse) error
|
||||
}
|
||||
|
||||
func RegisterGreeterHandler(s server.Server, hdlr GreeterHandler, opts ...server.HandlerOption) error {
|
||||
type greeter interface {
|
||||
Hello(ctx context.Context, in *Request, out *Response) error
|
||||
Health(ctx context.Context, in *HealthRequest, out *HealthResponse) error
|
||||
}
|
||||
type Greeter struct {
|
||||
greeter
|
||||
}
|
||||
h := &greeterHandler{hdlr}
|
||||
return s.Handle(s.NewHandler(&Greeter{h}, opts...))
|
||||
}
|
||||
|
||||
type greeterHandler struct {
|
||||
GreeterHandler
|
||||
}
|
||||
|
||||
func (h *greeterHandler) Hello(ctx context.Context, in *Request, out *Response) error {
|
||||
return h.GreeterHandler.Hello(ctx, in, out)
|
||||
}
|
||||
|
||||
func (h *greeterHandler) Health(ctx context.Context, in *HealthRequest, out *HealthResponse) error {
|
||||
return h.GreeterHandler.Health(ctx, in, out)
|
||||
}
|
||||
@@ -0,0 +1,24 @@
|
||||
syntax = "proto3";
|
||||
|
||||
package greeter;
|
||||
|
||||
option go_package = "go-micro.dev/v5/examples/auth/proto;greeter";
|
||||
|
||||
service Greeter {
|
||||
rpc Hello(Request) returns (Response) {}
|
||||
rpc Health(HealthRequest) returns (HealthResponse) {}
|
||||
}
|
||||
|
||||
message Request {
|
||||
string name = 1;
|
||||
}
|
||||
|
||||
message Response {
|
||||
string msg = 1;
|
||||
}
|
||||
|
||||
message HealthRequest {}
|
||||
|
||||
message HealthResponse {
|
||||
string status = 1;
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"log"
|
||||
|
||||
"go-micro.dev/v6"
|
||||
"go-micro.dev/v6/auth"
|
||||
"go-micro.dev/v6/auth/noop"
|
||||
authWrapper "go-micro.dev/v6/wrapper/auth"
|
||||
|
||||
pb "go-micro.dev/v6/examples/auth/proto"
|
||||
)
|
||||
|
||||
// Greeter implements the Greeter service
|
||||
type Greeter struct{}
|
||||
|
||||
// Hello is a protected endpoint that requires authentication
|
||||
func (g *Greeter) Hello(ctx context.Context, req *pb.Request, rsp *pb.Response) error {
|
||||
// Get account from context (added by auth wrapper)
|
||||
acc, ok := auth.AccountFromContext(ctx)
|
||||
if !ok {
|
||||
rsp.Msg = "Hello, anonymous!"
|
||||
return nil
|
||||
}
|
||||
|
||||
rsp.Msg = "Hello, " + acc.ID + "!"
|
||||
return nil
|
||||
}
|
||||
|
||||
// Health is a public endpoint that doesn't require auth
|
||||
func (g *Greeter) Health(ctx context.Context, req *pb.HealthRequest, rsp *pb.HealthResponse) error {
|
||||
rsp.Status = "ok"
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
// Create auth provider (noop for this example)
|
||||
// In production, use JWT or custom auth provider
|
||||
authProvider := noop.NewAuth()
|
||||
|
||||
// Create authorization rules
|
||||
rules := auth.NewRules()
|
||||
|
||||
// Grant public access to health endpoint
|
||||
rules.Grant(&auth.Rule{
|
||||
ID: "public-health",
|
||||
Scope: "",
|
||||
Resource: &auth.Resource{Type: "service", Name: "*", Endpoint: "Greeter.Health"},
|
||||
Access: auth.AccessGranted,
|
||||
Priority: 100,
|
||||
})
|
||||
|
||||
// Require authentication for other endpoints
|
||||
rules.Grant(&auth.Rule{
|
||||
ID: "authenticated-hello",
|
||||
Scope: "*",
|
||||
Resource: &auth.Resource{Type: "service", Name: "*", Endpoint: "*"},
|
||||
Access: auth.AccessGranted,
|
||||
Priority: 50,
|
||||
})
|
||||
|
||||
// Create service with auth wrapper
|
||||
service := micro.NewService("greeter", micro.Version("latest"),
|
||||
micro.WrapHandler(
|
||||
authWrapper.AuthHandler(authWrapper.HandlerOptions{
|
||||
Auth: authProvider,
|
||||
Rules: rules,
|
||||
SkipEndpoints: []string{"Greeter.Health"}, // Public endpoints
|
||||
}),
|
||||
),
|
||||
)
|
||||
|
||||
service.Init()
|
||||
|
||||
// Register handler
|
||||
if err := pb.RegisterGreeterHandler(service.Server(), &Greeter{}); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
// Generate a test token for demonstration
|
||||
if acc, err := authProvider.Generate("test-user"); err == nil {
|
||||
if token, err := authProvider.Token(auth.WithCredentials(acc.ID, acc.Secret)); err == nil {
|
||||
log.Printf("\n=== Test Token Generated ===")
|
||||
log.Printf("Use this token to test the client:")
|
||||
log.Printf("TOKEN=%s go run client/main.go\n", token.AccessToken)
|
||||
}
|
||||
}
|
||||
|
||||
// Run service
|
||||
if err := service.Run(); err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user