diff --git a/idptoken/grpc_client.go b/idptoken/grpc_client.go index d590418..8d61fe1 100644 --- a/idptoken/grpc_client.go +++ b/idptoken/grpc_client.go @@ -30,7 +30,10 @@ import ( // DefaultGRPCClientRequestTimeout is a default timeout for the gRPC requests. const DefaultGRPCClientRequestTimeout = time.Second * 30 -const grpcMetaAuthorization = "authorization" +const ( + grpcMetaAuthorization = "authorization" + grpcMetaSessionID = "x-caching-idp-session-id" +) // GRPCClientOpts contains options for the GRPCClient. type GRPCClientOpts struct { @@ -54,6 +57,8 @@ type GRPCClient struct { clientConn *grpc.ClientConn reqTimeout time.Duration promMetrics *metrics.PrometheusMetrics + + sessionID string } // NewGRPCClient creates a new GRPCClient instance that communicates with the IDP token service. @@ -117,7 +122,11 @@ func (c *GRPCClient) IntrospectToken( req.ScopeFilter[i] = &pb.IntrospectionScopeFilter{ResourceNamespace: scopeFilter[i].ResourceNamespace} } - ctx = metadata.AppendToOutgoingContext(ctx, grpcMetaAuthorization, makeBearerToken(accessToken)) + if c.sessionID != "" { + ctx = metadata.AppendToOutgoingContext(ctx, grpcMetaSessionID, c.sessionID) + } else { + ctx = metadata.AppendToOutgoingContext(ctx, grpcMetaAuthorization, makeBearerToken(accessToken)) + } var resp *pb.IntrospectTokenResponse if err := c.do(ctx, "IDPTokenService/IntrospectToken", func(ctx context.Context) error { @@ -128,6 +137,12 @@ func (c *GRPCClient) IntrospectToken( return nil, err } + if md, ok := metadata.FromIncomingContext(ctx); ok { + if sessionIDList := md.Get(grpcMetaSessionID); len(sessionIDList) > 0 { + c.sessionID = sessionIDList[0] + } + } + claims := jwt.DefaultClaims{ RegisteredClaims: jwtgo.RegisteredClaims{ Issuer: resp.GetIss(), diff --git a/idptoken/introspector.go b/idptoken/introspector.go index 673beba..f9a9966 100644 --- a/idptoken/introspector.go +++ b/idptoken/introspector.go @@ -371,7 +371,10 @@ func (i *Introspector) introspectToken(ctx context.Context, token string) (Intro return nil, err } - // If introspection is unauthorized, then invalidate access token provider's cache and try again. + // If introspection is unauthorized, it is required to: + // 1. Drop current session id to issue an access token anew + i.GRPCClient.sessionID = "" + // 2. Invalidate access token provider's cache and try again. // To avoid invalidating the cache too often, we have a threshold - minimum interval between invalidations. t, ok := i.accessTokenProviderInvalidatedAt.Load().(time.Time) now := time.Now() @@ -520,9 +523,13 @@ func (i *Introspector) makeIntrospectFuncHTTP(introspectionEndpointURL string) i func (i *Introspector) makeIntrospectFuncGRPC() introspectFunc { return func(ctx context.Context, token string) (IntrospectionResult, error) { - accessToken, err := i.accessTokenProvider.GetToken(ctx, i.accessTokenScope...) - if err != nil { - return nil, fmt.Errorf("get access token for doing introspection: %w", err) + var err error + var accessToken string + if i.GRPCClient.sessionID == "" { + accessToken, err = i.accessTokenProvider.GetToken(ctx, i.accessTokenScope...) + if err != nil { + return nil, fmt.Errorf("get access token for doing introspection: %w", err) + } } res, err := i.GRPCClient.IntrospectToken(ctx, token, i.scopeFilter, accessToken) if err != nil {