Non-expired JWT rejected as expired by Temporal Frontend

Hello,

We are facing the following issue when authenticating a worker to the Temporal API server with authentication/authorization enabled: JWT having valid iat/nbf/exp is rejected by the server with the “Token is expired” message.

The server is deployed with TEMPORAL_AUTH_AUTHORIZER=default, TEMPORAL_AUTH_CLAIM_MAPPER= default.

A worker acquires JWT with iat=nbf=<5 minutes back in the past from now>, exp=<24 hours + now> and sends it to the server. The server rejects the token with the following message in log:

temporal-frontend {“level”:“error”,“ts”:“2023-02-22T08:12:38.955Z”,“msg”:“Authorization error”,“error”:“Token is expired”,“logging-call-at”:“interceptor.go:169”,“stacktrace”:“go.temporal.io/server/common/log.(*zapLogger).Error\n\t/home/builder/temporal/common/log/zap_logger.go:144\ngo.temporal.io/server/common/authorization.(*interceptor).logAuthError\n\t/home/builder/temporal/common/authorization/interceptor.go:169\ngo.temporal.io/server/common/authorization.(*interceptor).Interceptor\n\t/home/builder/temporal/common/authorization/interceptor.go:115\ngoogle.golang.org/grpc.chainUnaryInterceptors.func1.1\n\t/go/pkg/mod/google.golang.org/grpc@v1.50.1/server.go:1165\ngo.temporal.io/server/common/rpc/interceptor.(*TelemetryInterceptor).Intercept\n\t/home/builder/temporal/common/rpc/interceptor/telemetry.go:142\ngoogle.golang.org/grpc.chainUnaryInterceptors.func1.1\n\t/go/pkg/mod/google.golang.org/grpc@v1.50.1/server.go:1165\ngo.temporal.io/server/common/metrics.NewServerMetricsContextInjectorInterceptor.func1\n\t/home/builder/temporal/common/metrics/grpc.go:66\ngoogle.golang.org/grpc.chainUnaryInterceptors.func1.1\n\t/go/pkg/mod/google.golang.org/grpc@v1.50.1/server.go:1165\ngo.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc.UnaryServerInterceptor.func1\n\t/go/pkg/mod/go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc@v0.36.1/interceptor.go:352\ngoogle.golang.org/grpc.chainUnaryInterceptors.func1.1\n\t/go/pkg/mod/google.golang.org/grpc@v1.50.1/server.go:1165\ngo.temporal.io/server/common/rpc/interceptor.(*NamespaceLogInterceptor).Intercept\n\t/home/builder/temporal/common/rpc/interceptor/namespace_logger.go:84\ngoogle.golang.org/grpc.chainUnaryInterceptors.func1.1\n\t/go/pkg/mod/google.golang.org/grpc@v1.50.1/server.go:1165\ngo.temporal.io/server/common/rpc/interceptor.(*NamespaceValidatorInterceptor).LengthValidationIntercept\n\t/home/builder/temporal/common/rpc/interceptor/namespace_validator.go:103\ngoogle.golang.org/grpc.chainUnaryInterceptors.func1.1\n\t/go/pkg/mod/google.golang.org/grpc@v1.50.1/server.go:1165\ngo.temporal.io/server/common/rpc.ServiceErrorInterceptor\n\t/home/builder/temporal/common/rpc/grpc.go:137\ngoogle.golang.org/grpc.chainUnaryInterceptors.func1.1\n\t/go/pkg/mod/google.golang.org/grpc@v1.50.1/server.go:1165\ngoogle.golang.org/grpc.chainUnaryInterceptors.func1\n\t/go/pkg/mod/google.golang.org/grpc@v1.50.1/server.go:1167\ngo.temporal.io/api/workflowservice/v1._WorkflowService_ListNamespaces_Handler\n\t/go/pkg/mod/go.temporal.io/api@v1.13.1-0.20221110200459-6a3cb21a3415/workflowservice/v1/service.pb.go:1410\ngoogle.golang.org/grpc.(*Server).processUnaryRPC\n\t/go/pkg/mod/google.golang.org/grpc@v1.50.1/server.go:1340\ngoogle.golang.org/grpc.(*Server).handleStream\n\t/go/pkg/mod/google.golang.org/grpc@v1.50.1/server.go:1713\ngoogle.golang.org/grpc.(*Server).serveStreams.func1.2\n\t/go/pkg/mod/google.golang.org/grpc@v1.50.1/server.go:965”}

Interestingly, a token with iat=nbf=<5 minutes back in the past from now>, exp=<1 hour + now> is accepted.

Apparently, the “long” token validity of 24 hours is what confuses the validator. Unfortunately, we do not control validity of the token (set by auth provider - Azure Active Directory).

Has anyone faced this or similar issue ? Are there any workarounds ?

Thanks

@sstro - Curious to know if you were able to get around this issue. We are facing a similar issue where a valid token is rejected by the server and the log trace on the server looks pretty similar too.

@AniketD - Unfortunately, not yet, as a workaround we use a different mechanism to obtain the token from AAD - client credentials flow (where token validity is 1h) instead of workload identity (where the validity is 24h).