e071084ebe
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
397 lines
10 KiB
Markdown
397 lines
10 KiB
Markdown
# 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.
|