Auth Wrapper
The auth wrapper package provides server and client wrappers for adding authentication and authorization to your go-micro services.
Installation
import "go-micro.dev/v5/wrapper/auth"
Overview
The auth wrapper consists of three main components:
- Server Wrapper (
AuthHandler) - Protects service endpoints - Client Wrapper (
AuthClient) - Adds auth tokens to requests - Metadata Helpers - Extract/inject tokens from/to metadata
Server Wrapper
The server wrapper enforces authentication and authorization on incoming requests.
Basic Usage
import (
"go-micro.dev/v5"
"go-micro.dev/v5/auth/jwt"
authWrapper "go-micro.dev/v5/wrapper/auth"
)
func main() {
// Create auth provider
authProvider, _ := jwt.NewAuth()
// Create authorization rules
rules := auth.NewRules()
// Wrap service with auth
service := micro.NewService(
micro.Name("myservice"),
micro.WrapHandler(
authWrapper.AuthHandler(authWrapper.HandlerOptions{
Auth: authProvider,
Rules: rules,
}),
),
)
service.Run()
}
Configuration Options
type HandlerOptions struct {
// Auth provider for token verification (required)
Auth auth.Auth
// Rules for authorization checks (optional)
Rules auth.Rules
// SkipEndpoints is a list of endpoints that don't require auth
// Format: "Service.Method" e.g., "Greeter.Hello"
SkipEndpoints []string
}
Auth Flow
For each incoming request:
- Check Skip List: If endpoint in
SkipEndpoints, skip auth - Extract Token: Get
Authorization: Bearer <token>from metadata - Verify Token: Call
auth.Inspect(token)to get account - Check Authorization: Call
rules.Verify(account, resource) - Inject Context: Add account to context with
auth.ContextWithAccount() - Call Handler: Proceed to actual handler
Errors:
401 Unauthorized- Missing or invalid token403 Forbidden- Token valid but insufficient permissions
Helper Functions
AuthRequired
Enforce auth on all endpoints (no public endpoints):
micro.WrapHandler(
authWrapper.AuthRequired(authProvider, rules),
)
PublicEndpoints
Allow specific endpoints to be public:
micro.WrapHandler(
authWrapper.PublicEndpoints(authProvider, rules, []string{
"Health.Check",
"Status.Version",
}),
)
AuthOptional
Extract auth if present but don't enforce (useful for endpoints that behave differently for authenticated users):
micro.WrapHandler(
authWrapper.AuthOptional(authProvider),
)
With AuthOptional, the handler can check:
func (s *Service) Hello(ctx context.Context, req *Request, rsp *Response) error {
if acc, ok := auth.AccountFromContext(ctx); ok {
rsp.Msg = "Hello, " + acc.ID
} else {
rsp.Msg = "Hello, anonymous"
}
return nil
}
Client Wrapper
The client wrapper adds authentication tokens to outgoing requests.
Basic Usage
import (
"go-micro.dev/v5"
"go-micro.dev/v5/client"
authWrapper "go-micro.dev/v5/wrapper/auth"
)
func main() {
token := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
service := micro.NewService(
micro.Name("myclient"),
micro.WrapClient(
authWrapper.FromToken(token),
),
)
service.Init()
// All calls now include the token
client := pb.NewMyServiceClient("myservice", service.Client())
rsp, err := client.SomeMethod(ctx, &pb.Request{})
}
Configuration Options
type ClientOptions struct {
// Auth provider for token generation (optional)
Auth auth.Auth
// Static token to use (optional)
// If not provided, will try to extract from context
Token string
}
Helper Functions
FromToken
Use a static token for all requests:
client.Wrap(
authWrapper.FromToken("eyJhbGciOi..."),
)
Best for:
- Pre-generated tokens
- Service accounts
- Long-lived tokens
FromContext
Extract account from context and generate token per-request:
client.Wrap(
authWrapper.FromContext(authProvider),
)
Best for:
- Service-to-service auth
- Dynamic token generation
- Request context propagation
Example:
func (s *Service) HandleRequest(ctx context.Context, req *Request, rsp *Response) error {
// Account already in context from incoming request
// Client wrapper extracts account and generates token
client := pb.NewOtherService("other", s.Client())
// Token automatically added
otherRsp, err := client.SomeMethod(ctx, &pb.OtherRequest{})
return nil
}
Metadata Helpers
Low-level helpers for working with auth tokens in metadata.
TokenFromMetadata
Extract Bearer token from request metadata:
import (
"go-micro.dev/v5/metadata"
authWrapper "go-micro.dev/v5/wrapper/auth"
)
func handler(ctx context.Context, req *Request, rsp *Response) error {
md, _ := metadata.FromContext(ctx)
token, err := authWrapper.TokenFromMetadata(md)
if err != nil {
return err // ErrMissingToken or ErrInvalidToken
}
// Use token...
}
Returns:
- Token string (without "Bearer " prefix)
ErrMissingToken- No Authorization header foundErrInvalidToken- Not in "Bearer " format
TokenToMetadata
Add Bearer token to outgoing request metadata:
md := metadata.Metadata{}
md = authWrapper.TokenToMetadata(md, "eyJhbGciOi...")
ctx := metadata.NewContext(context.Background(), md)
// Make RPC call with metadata
client.Call(ctx, req, rsp)
AccountFromMetadata
Extract token and verify in one step:
func handler(ctx context.Context, req *Request, rsp *Response) error {
md, _ := metadata.FromContext(ctx)
account, err := authWrapper.AccountFromMetadata(md, authProvider)
if err != nil {
return errors.Unauthorized("myservice", "invalid auth")
}
// Use account...
log.Printf("Request from: %s", account.ID)
}
This combines:
TokenFromMetadata(md)authProvider.Inspect(token)
Complete Example
Server
package main
import (
"context"
"go-micro.dev/v5"
"go-micro.dev/v5/auth"
"go-micro.dev/v5/auth/jwt"
authWrapper "go-micro.dev/v5/wrapper/auth"
)
type Greeter struct{}
func (g *Greeter) Hello(ctx context.Context, req *Request, rsp *Response) error {
// Get authenticated account
acc, ok := auth.AccountFromContext(ctx)
if !ok {
return errors.Unauthorized("greeter", "auth required")
}
rsp.Msg = "Hello, " + acc.ID
return nil
}
func main() {
authProvider, _ := jwt.NewAuth()
rules := auth.NewRules()
service := micro.NewService(
micro.Name("greeter"),
micro.WrapHandler(
authWrapper.PublicEndpoints(authProvider, rules, []string{
"Greeter.Health",
}),
),
)
pb.RegisterGreeterHandler(service.Server(), &Greeter{})
service.Run()
}
Client
package main
import (
"context"
"go-micro.dev/v5"
authWrapper "go-micro.dev/v5/wrapper/auth"
)
func main() {
token := "eyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9..."
service := micro.NewService(
micro.WrapClient(
authWrapper.FromToken(token),
),
)
client := pb.NewGreeterService("greeter", service.Client())
rsp, _ := client.Hello(context.Background(), &pb.Request{})
}
Testing
Mock Auth for Tests
import "go-micro.dev/v5/auth/noop"
func TestService(t *testing.T) {
// Use noop auth for testing (always grants access)
authProvider := noop.NewAuth()
service := micro.NewService(
micro.WrapHandler(
authWrapper.AuthHandler(authWrapper.HandlerOptions{
Auth: authProvider,
}),
),
)
// Test your service...
}
Generate Test Tokens
func TestWithAuth(t *testing.T) {
authProvider := noop.NewAuth()
// Generate test account
acc, _ := authProvider.Generate("test-user")
// Generate token
token, _ := authProvider.Token(
auth.WithCredentials(acc.ID, acc.Secret),
)
// Use token in tests
client := micro.NewService(
micro.WrapClient(
authWrapper.FromToken(token.AccessToken),
),
)
}
Integration with Gateway
If you're using the HTTP gateway (micro server), auth is automatically integrated:
# Gateway enforces auth on HTTP requests
micro server --auth jwt
The gateway:
- Extracts Bearer token from HTTP
Authorizationheader - Verifies token
- Adds account to metadata
- Forwards to service (service still checks with wrapper)
Best Practices
1. Always Use Server Wrapper
Even if using gateway auth, still wrap your services:
// ✅ Good: Defense in depth
micro.WrapHandler(authWrapper.AuthHandler(...))
// ❌ Bad: Only rely on gateway
// (services can be called directly, bypassing gateway)
2. Use Strong Auth in Production
// ✅ Production
authProvider, _ := jwt.NewAuth(
auth.Issuer("your-company"),
auth.PrivateKey(privateKey),
auth.PublicKey(publicKey),
)
// ❌ Development only
authProvider := noop.NewAuth()
3. Scope Your Rules
// ✅ Good: Specific scopes
rules.Grant(&auth.Rule{
Scope: "admin",
Resource: &auth.Resource{Endpoint: "Admin.*"},
})
// ⚠️ Risky: Too broad
rules.Grant(&auth.Rule{
Scope: "*",
Resource: &auth.Resource{Endpoint: "*"},
})
4. Check Account in Handlers
// ✅ Good: Verify account exists
func (s *Service) Delete(ctx context.Context, req *Request, rsp *Response) error {
acc, ok := auth.AccountFromContext(ctx)
if !ok || acc.ID != req.UserID {
return errors.Forbidden("service", "can only delete own data")
}
// ...
}
5. Use AuthOptional for Mixed Endpoints
// ✅ Good: Works for both auth and no-auth
func (s *Service) GetProfile(ctx context.Context, req *Request, rsp *Response) error {
if acc, ok := auth.AccountFromContext(ctx); ok {
// Authenticated: return private profile
rsp.Profile = s.getPrivateProfile(acc.ID)
} else {
// Public: return limited profile
rsp.Profile = s.getPublicProfile(req.UserID)
}
return nil
}
Troubleshooting
Issue: Handler receives requests without auth
Check:
- Is wrapper applied?
micro.WrapHandler(authWrapper.AuthHandler(...)) - Is endpoint in skip list? Check
SkipEndpoints - Is service registered correctly?
Issue: Client gets 401 errors
Check:
- Is token valid? Verify with
authProvider.Inspect(token) - Is client wrapper applied?
micro.WrapClient(authWrapper.FromToken(...)) - Is token expired? Check
token.Expiry
Issue: Token extraction fails
Check:
- Is Authorization header present?
md.Get("Authorization") - Is format correct? Must be
Bearer <token> - Is metadata propagated? Check context
API Reference
Server Wrapper
AuthHandler(opts HandlerOptions) server.HandlerWrapperPublicEndpoints(auth, rules, endpoints) HandlerOptionsAuthRequired(auth, rules) HandlerOptionsAuthOptional(auth) server.HandlerWrapper
Client Wrapper
AuthClient(opts ClientOptions) client.WrapperFromToken(token) client.WrapperFromContext(auth) client.Wrapper
Metadata Helpers
TokenFromMetadata(md) (string, error)TokenToMetadata(md, token) MetadataAccountFromMetadata(md, auth) (*Account, error)
Constants
MetadataKeyAuthorization="Authorization"BearerPrefix="Bearer "
Errors
ErrMissingToken- No authorization token in metadataErrInvalidToken- Token format invalid (not "Bearer ")
See Also
License
Apache 2.0