// I would recommend to use interceptors: // client grpc.Dial(target, grpc.WithPerRPCCredentials(&loginCreds{ Username: "admin", Password: "admin123", })) type loginCreds struct { Username, Password string } func (c *loginCreds) GetRequestMetadata(context.Context, ...string) (map[string]string, error) { return map[string]string{ "username": c.Username, "password": c.Password, }, nil } func (c *loginCreds) RequireTransportSecurity() bool { return true } // server grpc.NewServer( grpc.StreamInterceptor(streamInterceptor), grpc.UnaryInterceptor(unaryInterceptor) ) func streamInterceptor(srv interface{}, stream grpc.ServerStream, info *grpc.StreamServerInfo, handler grpc.StreamHandler) error { if err := authorize(stream.Context()); err != nil { return err } return handler(srv, stream) } func unaryInterceptor(ctx context.Context, req interface{}, info *grpc.UnaryServerInfo, handler grpc.UnaryHandler) (interface{}, error) { if err := authorize(ctx); err != nil { return err } return handler(ctx, req) } func authorize(ctx context.Context) error { if md, ok := metadata.FromContext(ctx); ok { if len(md["username"]) > 0 && md["username"][0] == "admin" && len(md["password"]) > 0 && md["password"][0] == "admin123" { return nil } return AccessDeniedErr } return EmptyMetadataErr }