wrapper/auth/README.md
The auth wrapper package provides server and client wrappers for adding authentication and authorization to your go-micro services.
import "go-micro.dev/v5/wrapper/auth"
The auth wrapper consists of three main components:
AuthHandler) - Protects service endpointsAuthClient) - Adds auth tokens to requestsThe server wrapper enforces authentication and authorization on incoming requests.
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()
}
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
}
For each incoming request:
SkipEndpoints, skip authAuthorization: Bearer <token> from metadataauth.Inspect(token) to get accountrules.Verify(account, resource)auth.ContextWithAccount()Errors:
401 Unauthorized - Missing or invalid token403 Forbidden - Token valid but insufficient permissionsEnforce auth on all endpoints (no public endpoints):
micro.WrapHandler(
authWrapper.AuthRequired(authProvider, rules),
)
Allow specific endpoints to be public:
micro.WrapHandler(
authWrapper.PublicEndpoints(authProvider, rules, []string{
"Health.Check",
"Status.Version",
}),
)
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
}
The client wrapper adds authentication tokens to outgoing requests.
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{})
}
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
}
Use a static token for all requests:
client.Wrap(
authWrapper.FromToken("eyJhbGciOi..."),
)
Best for:
Extract account from context and generate token per-request:
client.Wrap(
authWrapper.FromContext(authProvider),
)
Best for:
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
}
Low-level helpers for working with auth tokens in metadata.
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:
ErrMissingToken - No Authorization header foundErrInvalidToken - Not in "Bearer <token>" formatAdd 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)
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)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()
}
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{})
}
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...
}
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),
),
)
}
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:
Authorization headerEven 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)
// ✅ Production
authProvider, _ := jwt.NewAuth(
auth.Issuer("your-company"),
auth.PrivateKey(privateKey),
auth.PublicKey(publicKey),
)
// ❌ Development only
authProvider := noop.NewAuth()
// ✅ 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: "*"},
})
// ✅ 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")
}
// ...
}
// ✅ 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
}
Check:
micro.WrapHandler(authWrapper.AuthHandler(...))SkipEndpointsCheck:
authProvider.Inspect(token)micro.WrapClient(authWrapper.FromToken(...))token.ExpiryCheck:
md.Get("Authorization")Bearer <token>AuthHandler(opts HandlerOptions) server.HandlerWrapperPublicEndpoints(auth, rules, endpoints) HandlerOptionsAuthRequired(auth, rules) HandlerOptionsAuthOptional(auth) server.HandlerWrapperAuthClient(opts ClientOptions) client.WrapperFromToken(token) client.WrapperFromContext(auth) client.WrapperTokenFromMetadata(md) (string, error)TokenToMetadata(md, token) MetadataAccountFromMetadata(md, auth) (*Account, error)MetadataKeyAuthorization = "Authorization"BearerPrefix = "Bearer "ErrMissingToken - No authorization token in metadataErrInvalidToken - Token format invalid (not "Bearer <token>")Apache 2.0