diff --git a/credentials/oauth/oauth.go b/credentials/oauth/oauth.go
index 1e4d74eb..28ed0c39 100644
--- a/credentials/oauth/oauth.go
+++ b/credentials/oauth/oauth.go
@@ -61,6 +61,36 @@ func (ts TokenSource) GetRequestMetadata(ctx context.Context) (map[string]string
 	}, nil
 }
 
+type jwtAccess struct {
+	ts oauth2.TokenSource
+}
+
+func NewJWTAccessFromFile(keyFile string, audience string) (credentials.Credentials, error) {
+	jsonKey, err := ioutil.ReadFile(keyFile)
+	if err != nil {
+		return nil, fmt.Errorf("credentials: failed to read the service account key file: %v", err)
+	}
+	return NewJWTAccessFromKey(jsonKey, audience)
+}
+
+func NewJWTAccessFromKey(jsonKey []byte, audience string) (credentials.Credentials, error) {
+	ts, err := google.JWTAccessTokenSourceFromJSON(jsonKey, audience)
+	if err != nil {
+		return nil, err
+	}
+	return jwtAccess{ts: ts}, nil
+}
+
+func (j jwtAccess) GetRequestMetadata(ctx context.Context) (map[string]string, error) {
+	token, err := j.ts.Token()
+	if err != nil {
+		return nil, err
+	}
+	return map[string]string{
+		"authorization": token.TokenType + " " + token.AccessToken,
+	}, nil
+}
+
 // NewComputeEngine constructs the credentials that fetches access tokens from
 // Google Compute Engine (GCE)'s metadata server. It is only valid to use this
 // if your program is running on a GCE instance.
diff --git a/interop/client/client.go b/interop/client/client.go
index 5ee8d522..048ce000 100644
--- a/interop/client/client.go
+++ b/interop/client/client.go
@@ -72,7 +72,8 @@ var (
         empty_stream : full-duplex streaming with zero message;
         timeout_on_sleeping_server: fullduplex streaming;
         compute_engine_creds: large_unary with compute engine auth;
-	service_account_creds: large_unary with service account auth;
+        service_account_creds: large_unary with service account auth;
+	jwt_token_creds: large_unary with jwt token auth;
 	cancel_after_begin: cancellation after metadata has been sent but before payloads are sent;
 	cancel_after_first_response: cancellation after receiving 1st message from the server.`)
 )
@@ -343,6 +344,26 @@ func doServiceAccountCreds(tc testpb.TestServiceClient) {
 	grpclog.Println("ServiceAccountCreds done")
 }
 
+func doJWTTokenCreds(tc testpb.TestServiceClient) {
+	pl := newPayload(testpb.PayloadType_COMPRESSABLE, largeReqSize)
+	req := &testpb.SimpleRequest{
+		ResponseType: testpb.PayloadType_COMPRESSABLE.Enum(),
+		ResponseSize: proto.Int32(int32(largeRespSize)),
+		Payload:      pl,
+		FillUsername: proto.Bool(true),
+	}
+	reply, err := tc.UnaryCall(context.Background(), req)
+	if err != nil {
+		grpclog.Fatal("/TestService/UnaryCall RPC failed: ", err)
+	}
+	jsonKey := getServiceAccountJSONKey()
+	user := reply.GetUsername()
+	if !strings.Contains(string(jsonKey), user) {
+		grpclog.Fatalf("Got user name %q which is NOT a substring of %q.", user, jsonKey)
+	}
+	grpclog.Println("JWTtokenCreds done")
+}
+
 var (
 	testMetadata = metadata.MD{
 		"key1": []string{"value1"},
@@ -422,6 +443,12 @@ func main() {
 				grpclog.Fatalf("Failed to create JWT credentials: %v", err)
 			}
 			opts = append(opts, grpc.WithPerRPCCredentials(jwtCreds))
+		} else if *testCase == "jwt_token_creds" {
+			jwtCreds, err := oauth.NewJWTAccessFromFile(*serviceAccountKeyFile, "https://"+*serverHost+":"+string(*serverPort)+"/"+"TestService")
+			if err != nil {
+				grpclog.Fatalf("Failed to create JWT credentials: %v", err)
+			}
+			opts = append(opts, grpc.WithPerRPCCredentials(jwtCreds))
 		}
 	}
 	conn, err := grpc.Dial(serverAddr, opts...)
@@ -455,6 +482,11 @@ func main() {
 			grpclog.Fatalf("TLS is not enabled. TLS is required to execute service_account_creds test case.")
 		}
 		doServiceAccountCreds(tc)
+	case "jwt_token_creds":
+		if !*useTLS {
+			grpclog.Fatalf("TLS is not enabled. TLS is required to execute jwt_token_creds test case.")
+		}
+		doJWTTokenCreds(tc)
 	case "cancel_after_begin":
 		doCancelAfterBegin(tc)
 	case "cancel_after_first_response":