Skip to main content

gRPC Middleware

Using authorization middleware when building gRPC servers focuses the responsibility of making authorization decisions to a single component instead of fragmenting the logic across many service methods.

The middleware can be configured to retrieve authorization information, such as user identity, from incoming messages and streams.

Installation​

go get github.com/aserto-dev/go-aserto/middleware/grpz

Creating Middleware​

Creating middleware requires two arguments: an authorizer client, and a Policy that identifies the authorization policy to be applied, the decision rule to evaluate, and optionally a path to a policy module. If a path isn't provided, the middleware infers the policy path from the incoming request's URL. This behavior too can be further customized to fit other naming schemes.

import (
"github.com/aserto-dev/go-aserto/middleware"
"github.com/aserto-dev/go-aserto/middleware/grpcz"
)

...

// Create gRPC middleware.
mw := grpcz.New(
azClient,
middleware.Policy{
Name: "<policy name>",
Decision: "allowed", // Name of the policy rule to evaluate.
},
)

Configuring Middleware​

Middleware can be configured to extract authorization information from incoming requests. This information includes:

  1. Identity: the identity of the caller.
  2. Policy Path: the policy module to evaluate (e.g. "peoplefinder.GET.api.users.__id").
  3. Resource: contextual information about the resource being accessed.

Identity​

Identity information is set on the middleware's .Identity.

For example, to read a subject name from a "username" context value on the incoming request (presumably, set by some authentication middleware):

mw.Identity.Subject().FromContextValue("username")

Or, to configure the middleware to identify callers using a JWT in the "identity" metadata key on the incoming request context: request context:

mw.Identity.JWT().FromMetadata("identity")

Policy Path​

If a policy path isn't specified when the middleware is created, it will be inferred from the request method, replacing path separators (/) with dots(.) (e.g. /service/method becomes, service.method).

To provide your own logic for determining the policy path use:

mw.WithPolicyPathMapper(
func((ctx context.Context, message interface{})) string {
// custom logic inspects the request and returns the policy path.
},
)

Resource​

Resource information can be added to authorization requests using .WithResourceMapper():

mw.WithResourceMapper(
func(ctx, context.Context, message interface{}, resource map[string]interface{}) {
// Custom function to retrieve the owner of the resource being accessed.
resource["ownerId"] = GetOwner(ctx, message)
},
)

Connecting Middleware​

With a configured middleware in hand, all that's left is connecting it to your gRPC server:

// Create gRPC server with middleware.
s := grpc.NewServer(
grpc.UnaryInterceptor(mw.Unary),
grpc.StreamInterceptor(mw.Stream),
)