Repository: istio-ecosystem/authservice
Pull request #248 (2024-04-15 09:12:28)

Update code owners
Update the code owners to the current active people, so that code reviews can happen in a timely manner.

It mgirates from the OWNERS file to GitHub CODEOWNERS. Once this is merged, a follow-up change will be done in `istio/test-infra` to update the Ci requirements accordingly.

@BrenoDeMedeiros @liminw @lizan @sawlanipradeep @incfly @Shikugawa you were listed in the OWNERS file, but as far as I know you're not active anymore in the project. Please leave a comment if you want to be kept in the CODEOWNERS file and I would be happy to add you before merging the PR. Thank you! :)

istio-testing: [APPROVALNOTIFIER] This PR is **APPROVED**

This pull-request has been approved by: *<a href="https://github.com/istio-ecosystem/authservice/pull/248#" title="Author self-approved">nacx</a>*

The full list of commands accepted by this bot can be found [here](https://go.k8s.io/bot-commands?repo=istio-ecosystem%2Fauthservice).

The pull request process is described [here](https://git.k8s.io/community/contributors/guide/owners.md#the-code-review-process)

<details >
Needs approval from an approver in each of these files:

- ~~[OWNERS](https://github.com/istio-ecosystem/authservice/blob/main/OWNERS)~~ [nacx]

Approvers can indicate their approval by writing `/approve` in a comment
Approvers can cancel approval by writing `/approve cancel` in a comment
</details>
<!-- META={"approvers":[]} -->
codecov-commenter: ## [Codecov](https://app.codecov.io/gh/istio-ecosystem/authservice/pull/248?dropdown=coverage&src=pr&el=h1&utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=istio-ecosystem) Report
All modified and coverable lines are covered by tests :white_check_mark:
> :exclamation: No coverage uploaded for pull request base (`main@11a2002`). [Click here to learn what that means](https://docs.codecov.io/docs/error-reference?utm_medium=referral&utm_source=github&utm_content=comment&utm_campaign=pr+comments&utm_term=istio-ecosystem#section-missing-base-commit).

> :exclamation: Current head 1751edc differs from pull request most recent head da10194. Consider uploading reports for the commit da10194 to get more accurate results

:exclamation: Your organization needs to install the [Codecov GitHub app](https://github.com/apps/codecov/installations/select_target) to enable full functionality.

<details><summary>Additional details and impacted files</summary>


```diff
@@           Coverage Diff           @@
##             main     #248   +/-   ##
=======================================
  Coverage        ?   90.31%           
=======================================
  Files           ?       24           
  Lines           ?     1569           
  Branches        ?        0           
=======================================
  Hits            ?     1417           
  Misses          ?      108           
  Partials        ?       44           
```


</details>
nacx: /hold

Patch:
diff --git a/.github/workflows/ci.yaml b/.github/workflows/ci.yaml
index fb0aeba..15b7d83 100644
--- a/.github/workflows/ci.yaml
+++ b/.github/workflows/ci.yaml
@@ -139,3 +139,24 @@ jobs:
             e2e/**/logs/*
             e2e/**/certs/*
           if-no-files-found: ignore
+
+  # Aggregate all the required jobs and make it easier to customize CI required jobs
+  ci-checks:
+    runs-on: ubuntu-latest
+    needs:
+      - build
+      - test
+      - lint
+      - e2e
+      - e2e-compat
+    # We need this to run always to force-fail (and not skip) if any needed
+    # job has failed. Otherwise, a skipped job will not fail the workflow.
+    if: always()
+    steps:
+      - run: |
+          echo "CI checks completed"
+          [ "${{
+              contains(needs.*.result, 'failure') ||
+              contains(needs.*.result, 'cancelled') ||
+              contains(needs.*.result, 'skipped')
+            }}" == "false" ] || exit 1
diff --git a/.github/workflows/scan.yaml b/.github/workflows/scan.yaml
index 77eb7d0..a6f0734 100644
--- a/.github/workflows/scan.yaml
+++ b/.github/workflows/scan.yaml
@@ -51,7 +51,11 @@ jobs:
         id: scan
         with:
           image: local/authservice:scan-amd64
+          # Only fail the build on PRs. Do not fail the build on the scheduled run, to let the workflow
+          # continue and have the report uploaded.
+          fail-build: ${{ github.event_name != 'schedule' }}
       - run: cat ${{ steps.scan.outputs.sarif }}
+        if: always()  # Always print the report to the stdout.
       # Do not upload the security advisories on every commit or pull request.
       # Upload the security advisories only for the nightly scans.
       - uses: github/codeql-action/upload-sarif@v3
diff --git a/CODEOWNERS b/CODEOWNERS
new file mode 100644
index 0000000..bb07838
--- /dev/null
+++ b/CODEOWNERS
@@ -0,0 +1,1 @@
+*    @nacx @ZackButcher @sergicastro
diff --git a/OWNERS b/OWNERS
deleted file mode 100644
index 50c0a40..0000000
--- a/OWNERS
+++ /dev/null
@@ -1,9 +0,0 @@
-approvers:
-  - BrenoDeMedeiros
-  - liminw
-  - lizan
-  - sawlanipradeep
-  - ZackButcher
-  - incfly
-  - Shikugawa
-  - nacx
diff --git a/config/gen/go/v1/oidc/config.pb.go b/config/gen/go/v1/oidc/config.pb.go
index cca0f26..0022880 100644
--- a/config/gen/go/v1/oidc/config.pb.go
+++ b/config/gen/go/v1/oidc/config.pb.go
@@ -176,7 +176,8 @@ type LogoutConfig struct {
 	// of the service application, or to the
 	// [logout endpoint of the OIDC Provider](https://openid.net/specs/openid-connect-session-1_0.html#RPLogout).
 	// As with all redirects, the user's browser will perform a GET to this URI.
-	// Required.
+	// Required when the OIDC discovery is not used or when the OIDC discovery does not provide the
+	// `end_session_endpoint`.
 	RedirectUri string `protobuf:"bytes,2,opt,name=redirect_uri,json=redirectUri,proto3" json:"redirect_uri,omitempty"`
 }
 
@@ -780,135 +781,134 @@ var file_v1_oidc_config_proto_rawDesc = []byte{
 	0x61, 0x6d, 0x62, 0x6c, 0x65, 0x22, 0x35, 0x0a, 0x0b, 0x52, 0x65, 0x64, 0x69, 0x73, 0x43, 0x6f,
 	0x6e, 0x66, 0x69, 0x67, 0x12, 0x26, 0x0a, 0x0a, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x5f, 0x75,
 	0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10,
-	0x01, 0x52, 0x09, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x55, 0x72, 0x69, 0x22, 0x57, 0x0a, 0x0c,
+	0x01, 0x52, 0x09, 0x73, 0x65, 0x72, 0x76, 0x65, 0x72, 0x55, 0x72, 0x69, 0x22, 0x4e, 0x0a, 0x0c,
 	0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x1b, 0x0a, 0x04,
 	0x70, 0x61, 0x74, 0x68, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72,
-	0x02, 0x10, 0x01, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x2a, 0x0a, 0x0c, 0x72, 0x65, 0x64,
-	0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42,
-	0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0b, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65,
-	0x63, 0x74, 0x55, 0x72, 0x69, 0x22, 0x9e, 0x0d, 0x0a, 0x0a, 0x4f, 0x49, 0x44, 0x43, 0x43, 0x6f,
-	0x6e, 0x66, 0x69, 0x67, 0x12, 0x2b, 0x0a, 0x11, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72,
-	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52,
-	0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x72,
-	0x69, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69,
-	0x6f, 0x6e, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x61, 0x75,
-	0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x72, 0x69, 0x12, 0x1b,
-	0x0a, 0x09, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28,
-	0x09, 0x52, 0x08, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x55, 0x72, 0x69, 0x12, 0x2a, 0x0a, 0x0c, 0x63,
-	0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x03, 0x20, 0x01, 0x28,
-	0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x0b, 0x63, 0x61, 0x6c, 0x6c,
-	0x62, 0x61, 0x63, 0x6b, 0x55, 0x72, 0x69, 0x12, 0x14, 0x0a, 0x04, 0x6a, 0x77, 0x6b, 0x73, 0x18,
-	0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04, 0x6a, 0x77, 0x6b, 0x73, 0x12, 0x5d, 0x0a,
-	0x0c, 0x6a, 0x77, 0x6b, 0x73, 0x5f, 0x66, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x18, 0x11, 0x20,
-	0x01, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
-	0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x6f, 0x69, 0x64, 0x63,
-	0x2e, 0x4f, 0x49, 0x44, 0x43, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x4a, 0x77, 0x6b, 0x73,
-	0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52,
-	0x0b, 0x6a, 0x77, 0x6b, 0x73, 0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x09,
-	0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42,
-	0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74,
-	0x49, 0x64, 0x12, 0x2e, 0x0a, 0x0d, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63,
-	0x72, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02,
-	0x10, 0x01, 0x48, 0x01, 0x52, 0x0c, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72,
-	0x65, 0x74, 0x12, 0x64, 0x0a, 0x11, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63,
-	0x72, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x15, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e,
-	0x61, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66,
-	0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x6f, 0x69, 0x64, 0x63, 0x2e, 0x4f, 0x49, 0x44, 0x43, 0x43,
-	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x66, 0x65,
-	0x72, 0x65, 0x6e, 0x63, 0x65, 0x48, 0x01, 0x52, 0x0f, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53,
-	0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x66, 0x12, 0x16, 0x0a, 0x06, 0x73, 0x63, 0x6f, 0x70,
-	0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52, 0x06, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73,
-	0x12, 0x2c, 0x0a, 0x12, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f,
-	0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f,
-	0x6f, 0x6b, 0x69, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x50, 0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x4c,
-	0x0a, 0x08, 0x69, 0x64, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b,
-	0x32, 0x27, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63,
-	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x6f, 0x69, 0x64, 0x63, 0x2e, 0x54, 0x6f,
-	0x6b, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01,
-	0x02, 0x10, 0x01, 0x52, 0x07, 0x69, 0x64, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x4a, 0x0a, 0x0c,
-	0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x18, 0x0a, 0x20, 0x01,
+	0x02, 0x10, 0x01, 0x52, 0x04, 0x70, 0x61, 0x74, 0x68, 0x12, 0x21, 0x0a, 0x0c, 0x72, 0x65, 0x64,
+	0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52,
+	0x0b, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x55, 0x72, 0x69, 0x22, 0x9e, 0x0d, 0x0a,
+	0x0a, 0x4f, 0x49, 0x44, 0x43, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x2b, 0x0a, 0x11, 0x63,
+	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x72, 0x69,
+	0x18, 0x13, 0x20, 0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x75, 0x72,
+	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x55, 0x72, 0x69, 0x12, 0x2b, 0x0a, 0x11, 0x61, 0x75, 0x74, 0x68,
+	0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x10, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69,
+	0x6f, 0x6e, 0x55, 0x72, 0x69, 0x12, 0x1b, 0x0a, 0x09, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x5f, 0x75,
+	0x72, 0x69, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x74, 0x6f, 0x6b, 0x65, 0x6e, 0x55,
+	0x72, 0x69, 0x12, 0x2a, 0x0a, 0x0c, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x5f, 0x75,
+	0x72, 0x69, 0x18, 0x03, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10,
+	0x01, 0x52, 0x0b, 0x63, 0x61, 0x6c, 0x6c, 0x62, 0x61, 0x63, 0x6b, 0x55, 0x72, 0x69, 0x12, 0x14,
+	0x0a, 0x04, 0x6a, 0x77, 0x6b, 0x73, 0x18, 0x04, 0x20, 0x01, 0x28, 0x09, 0x48, 0x00, 0x52, 0x04,
+	0x6a, 0x77, 0x6b, 0x73, 0x12, 0x5d, 0x0a, 0x0c, 0x6a, 0x77, 0x6b, 0x73, 0x5f, 0x66, 0x65, 0x74,
+	0x63, 0x68, 0x65, 0x72, 0x18, 0x11, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x38, 0x2e, 0x61, 0x75, 0x74,
+	0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e,
+	0x76, 0x31, 0x2e, 0x6f, 0x69, 0x64, 0x63, 0x2e, 0x4f, 0x49, 0x44, 0x43, 0x43, 0x6f, 0x6e, 0x66,
+	0x69, 0x67, 0x2e, 0x4a, 0x77, 0x6b, 0x73, 0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x43, 0x6f,
+	0x6e, 0x66, 0x69, 0x67, 0x48, 0x00, 0x52, 0x0b, 0x6a, 0x77, 0x6b, 0x73, 0x46, 0x65, 0x74, 0x63,
+	0x68, 0x65, 0x72, 0x12, 0x24, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64,
+	0x18, 0x05, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52,
+	0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, 0x64, 0x12, 0x2e, 0x0a, 0x0d, 0x63, 0x6c, 0x69,
+	0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x18, 0x06, 0x20, 0x01, 0x28, 0x09,
+	0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x48, 0x01, 0x52, 0x0c, 0x63, 0x6c, 0x69,
+	0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x12, 0x64, 0x0a, 0x11, 0x63, 0x6c, 0x69,
+	0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x72, 0x65, 0x66, 0x18, 0x15,
+	0x20, 0x01, 0x28, 0x0b, 0x32, 0x36, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69,
+	0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x6f, 0x69, 0x64,
+	0x63, 0x2e, 0x4f, 0x49, 0x44, 0x43, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x53, 0x65, 0x63,
+	0x72, 0x65, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x48, 0x01, 0x52, 0x0f,
+	0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x66, 0x12,
+	0x16, 0x0a, 0x06, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x18, 0x07, 0x20, 0x03, 0x28, 0x09, 0x52,
+	0x06, 0x73, 0x63, 0x6f, 0x70, 0x65, 0x73, 0x12, 0x2c, 0x0a, 0x12, 0x63, 0x6f, 0x6f, 0x6b, 0x69,
+	0x65, 0x5f, 0x6e, 0x61, 0x6d, 0x65, 0x5f, 0x70, 0x72, 0x65, 0x66, 0x69, 0x78, 0x18, 0x08, 0x20,
+	0x01, 0x28, 0x09, 0x52, 0x10, 0x63, 0x6f, 0x6f, 0x6b, 0x69, 0x65, 0x4e, 0x61, 0x6d, 0x65, 0x50,
+	0x72, 0x65, 0x66, 0x69, 0x78, 0x12, 0x4c, 0x0a, 0x08, 0x69, 0x64, 0x5f, 0x74, 0x6f, 0x6b, 0x65,
+	0x6e, 0x18, 0x09, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x73, 0x65,
+	0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e,
+	0x6f, 0x69, 0x64, 0x63, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67,
+	0x42, 0x08, 0xfa, 0x42, 0x05, 0x8a, 0x01, 0x02, 0x10, 0x01, 0x52, 0x07, 0x69, 0x64, 0x54, 0x6f,
+	0x6b, 0x65, 0x6e, 0x12, 0x4a, 0x0a, 0x0c, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x5f, 0x74, 0x6f,
+	0x6b, 0x65, 0x6e, 0x18, 0x0a, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x61, 0x75, 0x74, 0x68,
+	0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76,
+	0x31, 0x2e, 0x6f, 0x69, 0x64, 0x63, 0x2e, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x66,
+	0x69, 0x67, 0x52, 0x0b, 0x61, 0x63, 0x63, 0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12,
+	0x40, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32,
+	0x28, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f,
+	0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x6f, 0x69, 0x64, 0x63, 0x2e, 0x4c, 0x6f, 0x67,
+	0x6f, 0x75, 0x74, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x06, 0x6c, 0x6f, 0x67, 0x6f, 0x75,
+	0x74, 0x12, 0x38, 0x0a, 0x18, 0x61, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x5f, 0x73, 0x65,
+	0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x0c, 0x20,
+	0x01, 0x28, 0x0d, 0x52, 0x16, 0x61, 0x62, 0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x53, 0x65, 0x73,
+	0x73, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x69,
+	0x64, 0x6c, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65,
+	0x6f, 0x75, 0x74, 0x18, 0x0d, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x12, 0x69, 0x64, 0x6c, 0x65, 0x53,
+	0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x44, 0x0a,
+	0x1d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69,
+	0x63, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x0e,
+	0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x1b, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x43,
+	0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72,
+	0x69, 0x74, 0x79, 0x12, 0x4d, 0x0a, 0x22, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63,
+	0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f,
+	0x72, 0x69, 0x74, 0x79, 0x5f, 0x66, 0x69, 0x6c, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x48,
+	0x02, 0x52, 0x1f, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66,
+	0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x46, 0x69,
+	0x6c, 0x65, 0x12, 0x7d, 0x0a, 0x2e, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x65,
+	0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72,
+	0x69, 0x74, 0x79, 0x5f, 0x72, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x5f, 0x69, 0x6e, 0x74, 0x65,
+	0x72, 0x76, 0x61, 0x6c, 0x18, 0x16, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f,
+	0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72,
+	0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x2a, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x43, 0x65,
+	0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69,
+	0x74, 0x79, 0x52, 0x65, 0x66, 0x72, 0x65, 0x73, 0x68, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61,
+	0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x0f,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70, 0x72, 0x6f, 0x78, 0x79, 0x55, 0x72, 0x69, 0x12, 0x64,
+	0x0a, 0x1a, 0x72, 0x65, 0x64, 0x69, 0x73, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f,
+	0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x10, 0x20, 0x01,
 	0x28, 0x0b, 0x32, 0x27, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
 	0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x6f, 0x69, 0x64, 0x63, 0x2e,
-	0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x0b, 0x61, 0x63, 0x63,
-	0x65, 0x73, 0x73, 0x54, 0x6f, 0x6b, 0x65, 0x6e, 0x12, 0x40, 0x0a, 0x06, 0x6c, 0x6f, 0x67, 0x6f,
-	0x75, 0x74, 0x18, 0x0b, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x28, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x73,
-	0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31,
-	0x2e, 0x6f, 0x69, 0x64, 0x63, 0x2e, 0x4c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x43, 0x6f, 0x6e, 0x66,
-	0x69, 0x67, 0x52, 0x06, 0x6c, 0x6f, 0x67, 0x6f, 0x75, 0x74, 0x12, 0x38, 0x0a, 0x18, 0x61, 0x62,
-	0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74,
-	0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x0c, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x16, 0x61, 0x62,
-	0x73, 0x6f, 0x6c, 0x75, 0x74, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54, 0x69, 0x6d,
-	0x65, 0x6f, 0x75, 0x74, 0x12, 0x30, 0x0a, 0x14, 0x69, 0x64, 0x6c, 0x65, 0x5f, 0x73, 0x65, 0x73,
-	0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x74, 0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x18, 0x0d, 0x20, 0x01,
-	0x28, 0x0d, 0x52, 0x12, 0x69, 0x64, 0x6c, 0x65, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x54,
-	0x69, 0x6d, 0x65, 0x6f, 0x75, 0x74, 0x12, 0x44, 0x0a, 0x1d, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65,
-	0x64, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x5f, 0x61, 0x75,
-	0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x18, 0x0e, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52,
-	0x1b, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
-	0x61, 0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x12, 0x4d, 0x0a, 0x22,
-	0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63,
-	0x61, 0x74, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x66, 0x69,
-	0x6c, 0x65, 0x18, 0x14, 0x20, 0x01, 0x28, 0x09, 0x48, 0x02, 0x52, 0x1f, 0x74, 0x72, 0x75, 0x73,
-	0x74, 0x65, 0x64, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61, 0x74, 0x65, 0x41, 0x75,
-	0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x46, 0x69, 0x6c, 0x65, 0x12, 0x7d, 0x0a, 0x2e, 0x74,
-	0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
-	0x74, 0x65, 0x5f, 0x61, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x5f, 0x72, 0x65, 0x66,
-	0x72, 0x65, 0x73, 0x68, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x18, 0x16, 0x20,
-	0x01, 0x28, 0x0b, 0x32, 0x19, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
-	0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x44, 0x75, 0x72, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x2a,
-	0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x43, 0x65, 0x72, 0x74, 0x69, 0x66, 0x69, 0x63, 0x61,
-	0x74, 0x65, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x74, 0x79, 0x52, 0x65, 0x66, 0x72, 0x65,
-	0x73, 0x68, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x12, 0x1b, 0x0a, 0x09, 0x70, 0x72,
-	0x6f, 0x78, 0x79, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x0f, 0x20, 0x01, 0x28, 0x09, 0x52, 0x08, 0x70,
-	0x72, 0x6f, 0x78, 0x79, 0x55, 0x72, 0x69, 0x12, 0x64, 0x0a, 0x1a, 0x72, 0x65, 0x64, 0x69, 0x73,
-	0x5f, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, 0x73, 0x74, 0x6f, 0x72, 0x65, 0x5f, 0x63,
-	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x18, 0x10, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x27, 0x2e, 0x61, 0x75,
-	0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67,
-	0x2e, 0x76, 0x31, 0x2e, 0x6f, 0x69, 0x64, 0x63, 0x2e, 0x52, 0x65, 0x64, 0x69, 0x73, 0x43, 0x6f,
-	0x6e, 0x66, 0x69, 0x67, 0x52, 0x17, 0x72, 0x65, 0x64, 0x69, 0x73, 0x53, 0x65, 0x73, 0x73, 0x69,
-	0x6f, 0x6e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x49, 0x0a,
-	0x15, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x70, 0x65, 0x65,
-	0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x18, 0x12, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67,
-	0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56,
-	0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x73, 0x6b, 0x69, 0x70, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79,
-	0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x1a, 0xbc, 0x01, 0x0a, 0x11, 0x4a, 0x77, 0x6b,
-	0x73, 0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19,
-	0x0a, 0x08, 0x6a, 0x77, 0x6b, 0x73, 0x5f, 0x75, 0x72, 0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09,
-	0x52, 0x07, 0x6a, 0x77, 0x6b, 0x73, 0x55, 0x72, 0x69, 0x12, 0x3d, 0x0a, 0x1b, 0x70, 0x65, 0x72,
-	0x69, 0x6f, 0x64, 0x69, 0x63, 0x5f, 0x66, 0x65, 0x74, 0x63, 0x68, 0x5f, 0x69, 0x6e, 0x74, 0x65,
-	0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x18, 0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x18,
-	0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x69, 0x63, 0x46, 0x65, 0x74, 0x63, 0x68, 0x49, 0x6e, 0x74,
-	0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x12, 0x4d, 0x0a, 0x15, 0x73, 0x6b, 0x69, 0x70,
-	0x5f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72,
-	0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65,
-	0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42,
-	0x02, 0x18, 0x01, 0x52, 0x12, 0x73, 0x6b, 0x69, 0x70, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50,
-	0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x1a, 0x4c, 0x0a, 0x0f, 0x53, 0x65, 0x63, 0x72, 0x65,
-	0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63, 0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61,
-	0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e,
-	0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12, 0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65,
-	0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa, 0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52,
-	0x04, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x0d, 0x0a, 0x0b, 0x6a, 0x77, 0x6b, 0x73, 0x5f, 0x63, 0x6f,
-	0x6e, 0x66, 0x69, 0x67, 0x42, 0x1b, 0x0a, 0x14, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73,
-	0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x03, 0xf8, 0x42,
-	0x01, 0x42, 0x13, 0x0a, 0x11, 0x74, 0x72, 0x75, 0x73, 0x74, 0x65, 0x64, 0x5f, 0x63, 0x61, 0x5f,
-	0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0xf7, 0x01, 0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x61,
-	0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69,
-	0x67, 0x2e, 0x76, 0x31, 0x2e, 0x6f, 0x69, 0x64, 0x63, 0x42, 0x0b, 0x43, 0x6f, 0x6e, 0x66, 0x69,
-	0x67, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62,
-	0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x73, 0x74, 0x69, 0x6f, 0x2d, 0x65, 0x63, 0x6f, 0x73, 0x79,
-	0x73, 0x74, 0x65, 0x6d, 0x2f, 0x61, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65,
-	0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x67, 0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76,
-	0x31, 0x2f, 0x6f, 0x69, 0x64, 0x63, 0xa2, 0x02, 0x04, 0x41, 0x43, 0x56, 0x4f, 0xaa, 0x02, 0x1a,
-	0x41, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x66,
-	0x69, 0x67, 0x2e, 0x56, 0x31, 0x2e, 0x4f, 0x69, 0x64, 0x63, 0xca, 0x02, 0x1a, 0x41, 0x75, 0x74,
-	0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5c,
-	0x56, 0x31, 0x5c, 0x4f, 0x69, 0x64, 0x63, 0xe2, 0x02, 0x26, 0x41, 0x75, 0x74, 0x68, 0x73, 0x65,
-	0x72, 0x76, 0x69, 0x63, 0x65, 0x5c, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5c, 0x56, 0x31, 0x5c,
-	0x4f, 0x69, 0x64, 0x63, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61,
-	0xea, 0x02, 0x1d, 0x41, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x3a, 0x3a,
-	0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x3a, 0x3a, 0x56, 0x31, 0x3a, 0x3a, 0x4f, 0x69, 0x64, 0x63,
-	0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
+	0x52, 0x65, 0x64, 0x69, 0x73, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x52, 0x17, 0x72, 0x65, 0x64,
+	0x69, 0x73, 0x53, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x53, 0x74, 0x6f, 0x72, 0x65, 0x43, 0x6f,
+	0x6e, 0x66, 0x69, 0x67, 0x12, 0x49, 0x0a, 0x15, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x76, 0x65, 0x72,
+	0x69, 0x66, 0x79, 0x5f, 0x70, 0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x18, 0x12, 0x20,
+	0x01, 0x28, 0x0b, 0x32, 0x16, 0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f,
+	0x74, 0x6f, 0x62, 0x75, 0x66, 0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x52, 0x12, 0x73, 0x6b, 0x69,
+	0x70, 0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x1a,
+	0xbc, 0x01, 0x0a, 0x11, 0x4a, 0x77, 0x6b, 0x73, 0x46, 0x65, 0x74, 0x63, 0x68, 0x65, 0x72, 0x43,
+	0x6f, 0x6e, 0x66, 0x69, 0x67, 0x12, 0x19, 0x0a, 0x08, 0x6a, 0x77, 0x6b, 0x73, 0x5f, 0x75, 0x72,
+	0x69, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x52, 0x07, 0x6a, 0x77, 0x6b, 0x73, 0x55, 0x72, 0x69,
+	0x12, 0x3d, 0x0a, 0x1b, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x69, 0x63, 0x5f, 0x66, 0x65, 0x74,
+	0x63, 0x68, 0x5f, 0x69, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x5f, 0x73, 0x65, 0x63, 0x18,
+	0x02, 0x20, 0x01, 0x28, 0x0d, 0x52, 0x18, 0x70, 0x65, 0x72, 0x69, 0x6f, 0x64, 0x69, 0x63, 0x46,
+	0x65, 0x74, 0x63, 0x68, 0x49, 0x6e, 0x74, 0x65, 0x72, 0x76, 0x61, 0x6c, 0x53, 0x65, 0x63, 0x12,
+	0x4d, 0x0a, 0x15, 0x73, 0x6b, 0x69, 0x70, 0x5f, 0x76, 0x65, 0x72, 0x69, 0x66, 0x79, 0x5f, 0x70,
+	0x65, 0x65, 0x72, 0x5f, 0x63, 0x65, 0x72, 0x74, 0x18, 0x03, 0x20, 0x01, 0x28, 0x0b, 0x32, 0x16,
+	0x2e, 0x67, 0x6f, 0x6f, 0x67, 0x6c, 0x65, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x62, 0x75, 0x66,
+	0x2e, 0x56, 0x61, 0x6c, 0x75, 0x65, 0x42, 0x02, 0x18, 0x01, 0x52, 0x12, 0x73, 0x6b, 0x69, 0x70,
+	0x56, 0x65, 0x72, 0x69, 0x66, 0x79, 0x50, 0x65, 0x65, 0x72, 0x43, 0x65, 0x72, 0x74, 0x1a, 0x4c,
+	0x0a, 0x0f, 0x53, 0x65, 0x63, 0x72, 0x65, 0x74, 0x52, 0x65, 0x66, 0x65, 0x72, 0x65, 0x6e, 0x63,
+	0x65, 0x12, 0x1c, 0x0a, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x18, 0x01,
+	0x20, 0x01, 0x28, 0x09, 0x52, 0x09, 0x6e, 0x61, 0x6d, 0x65, 0x73, 0x70, 0x61, 0x63, 0x65, 0x12,
+	0x1b, 0x0a, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x18, 0x02, 0x20, 0x01, 0x28, 0x09, 0x42, 0x07, 0xfa,
+	0x42, 0x04, 0x72, 0x02, 0x10, 0x01, 0x52, 0x04, 0x6e, 0x61, 0x6d, 0x65, 0x42, 0x0d, 0x0a, 0x0b,
+	0x6a, 0x77, 0x6b, 0x73, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0x1b, 0x0a, 0x14, 0x63,
+	0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x73, 0x65, 0x63, 0x72, 0x65, 0x74, 0x5f, 0x63, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x12, 0x03, 0xf8, 0x42, 0x01, 0x42, 0x13, 0x0a, 0x11, 0x74, 0x72, 0x75, 0x73,
+	0x74, 0x65, 0x64, 0x5f, 0x63, 0x61, 0x5f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x42, 0xf7, 0x01,
+	0x0a, 0x1e, 0x63, 0x6f, 0x6d, 0x2e, 0x61, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63,
+	0x65, 0x2e, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x76, 0x31, 0x2e, 0x6f, 0x69, 0x64, 0x63,
+	0x42, 0x0b, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a,
+	0x3c, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x69, 0x73, 0x74, 0x69,
+	0x6f, 0x2d, 0x65, 0x63, 0x6f, 0x73, 0x79, 0x73, 0x74, 0x65, 0x6d, 0x2f, 0x61, 0x75, 0x74, 0x68,
+	0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x2f, 0x63, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2f, 0x67,
+	0x65, 0x6e, 0x2f, 0x67, 0x6f, 0x2f, 0x76, 0x31, 0x2f, 0x6f, 0x69, 0x64, 0x63, 0xa2, 0x02, 0x04,
+	0x41, 0x43, 0x56, 0x4f, 0xaa, 0x02, 0x1a, 0x41, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69,
+	0x63, 0x65, 0x2e, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x2e, 0x56, 0x31, 0x2e, 0x4f, 0x69, 0x64,
+	0x63, 0xca, 0x02, 0x1a, 0x41, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5c,
+	0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x5c, 0x56, 0x31, 0x5c, 0x4f, 0x69, 0x64, 0x63, 0xe2, 0x02,
+	0x26, 0x41, 0x75, 0x74, 0x68, 0x73, 0x65, 0x72, 0x76, 0x69, 0x63, 0x65, 0x5c, 0x43, 0x6f, 0x6e,
+	0x66, 0x69, 0x67, 0x5c, 0x56, 0x31, 0x5c, 0x4f, 0x69, 0x64, 0x63, 0x5c, 0x47, 0x50, 0x42, 0x4d,
+	0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x1d, 0x41, 0x75, 0x74, 0x68, 0x73, 0x65,
+	0x72, 0x76, 0x69, 0x63, 0x65, 0x3a, 0x3a, 0x43, 0x6f, 0x6e, 0x66, 0x69, 0x67, 0x3a, 0x3a, 0x56,
+	0x31, 0x3a, 0x3a, 0x4f, 0x69, 0x64, 0x63, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33,
 }
 
 var (
diff --git a/config/gen/go/v1/oidc/config.pb.validate.go b/config/gen/go/v1/oidc/config.pb.validate.go
index ae4c4cb..865f046 100644
--- a/config/gen/go/v1/oidc/config.pb.validate.go
+++ b/config/gen/go/v1/oidc/config.pb.validate.go
@@ -290,16 +290,7 @@ func (m *LogoutConfig) validate(all bool) error {
 		errors = append(errors, err)
 	}
 
-	if utf8.RuneCountInString(m.GetRedirectUri()) < 1 {
-		err := LogoutConfigValidationError{
-			field:  "RedirectUri",
-			reason: "value length must be at least 1 runes",
-		}
-		if !all {
-			return err
-		}
-		errors = append(errors, err)
-	}
+	// no validation rules for RedirectUri
 
 	if len(errors) > 0 {
 		return LogoutConfigMultiError(errors)
diff --git a/config/v1/oidc/config.proto b/config/v1/oidc/config.proto
index 2c68245..8c2070e 100644
--- a/config/v1/oidc/config.proto
+++ b/config/v1/oidc/config.proto
@@ -66,8 +66,9 @@ message LogoutConfig {
   // of the service application, or to the
   // [logout endpoint of the OIDC Provider](https://openid.net/specs/openid-connect-session-1_0.html#RPLogout).
   // As with all redirects, the user's browser will perform a GET to this URI.
-  // Required.
-  string redirect_uri = 2 [(validate.rules).string.min_len = 1];
+  // Required when the OIDC discovery is not used or when the OIDC discovery does not provide the
+  // `end_session_endpoint`.
+  string redirect_uri = 2;
 }
 
 // The configuration of an OpenID Connect filter that can be used to retrieve identity and access tokens
diff --git a/examples/minimal/authservice-config.yaml b/examples/minimal/authservice-config.yaml
new file mode 100644
index 0000000..3365842
--- /dev/null
+++ b/examples/minimal/authservice-config.yaml
@@ -0,0 +1,68 @@
+# Copyright 2024 Tetrate
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+apiVersion: v1
+kind: Secret
+metadata:
+  name: client-secret
+type: Opaque
+stringData:
+  client-secret: "authservice-secret"
+---
+kind: ConfigMap
+apiVersion: v1
+metadata:
+  name: authservice-config
+data:
+  config.json: |
+    {
+      "listen_address": "0.0.0.0",
+      "listen_port": "10003",
+      "log_level": "debug",
+      "allow_unmatched_requests": false,
+      "chains": [
+        {
+          "name": "oidc",
+          "filters": [
+            {
+              "oidc":
+              {
+                "configuration_uri": "https://OIDC_PROVIDER_WELLKNOWN_URI/.well-known/openid-configuration",
+                <!--
+                  REMOVE THIS COMMENT
+                  The `callback_url` must point to the application that is guarded behind the OIDC login.,
+                  The path can be "anything", as long as it is configured in the ingress Gateway or VirtualService,
+                  to be forwarded to the application (even when the application does not expose such path). The,
+                  Istio AuthorizationPolicy that intercepts traffic will intercept the calls to this path and forward,
+                  them appropriately to the authservice.
+                -->
+                "callback_uri": "https://APPLICATION_URI/callback",
+                "client_id": "authservice-client",
+                "client_secret_ref": {
+                  "namespace": "CHANGEME",
+                  "name": "client-secret"
+                },
+                "id_token": {
+                  "preamble": "Bearer",
+                  "header": "authorization"
+                },
+                "access_token": {
+                  "header": "x-access-token"
+                }
+              }
+            }
+          ]
+        }
+      ]
+    }
diff --git a/examples/minimal/authservice.yaml b/examples/minimal/authservice.yaml
new file mode 100644
index 0000000..860bec3
--- /dev/null
+++ b/examples/minimal/authservice.yaml
@@ -0,0 +1,89 @@
+# Copyright 2024 Tetrate
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+apiVersion: v1
+kind: Service
+metadata:
+  name: authservice
+  labels:
+    app: authservice
+spec:
+  ports:
+    # Main port where the authservice listens for gRPC requests.
+    # This is the port that needs to be set when configuring the `extensionProviders`
+    # in the Istio configuration.ß
+    - port: 10003
+      targetPort: 10003
+      name: grpc-authservice
+      protocol: TCP
+    - port: 10004
+      targetPort: 10004
+      name: grpc-health
+      protocol: TCP
+  selector:
+    app: authservice
+---
+apiVersion: v1
+kind: ServiceAccount
+metadata:
+  name: authservice
+  labels:
+    app: authservice
+---
+apiVersion: apps/v1
+kind: Deployment
+metadata:
+  name: authservice
+spec:
+  replicas: 1
+  selector:
+    matchLabels:
+      app: authservice
+      version: v1
+  template:
+    metadata:
+      labels:
+        app: authservice
+        version: v1
+    spec:
+      serviceAccountName: authservice
+      containers:
+        - name: authservice
+          image: ghcr.io/istio-ecosystem/authservice/authservice:1.0.0
+          imagePullPolicy: IfNotPresent
+          ports:
+            - name: authz
+              containerPort: 10003
+              protocol: TCP
+            - name: health
+              containerPort: 10004
+              protocol: TCP
+          volumeMounts:
+            - name: config
+              mountPath: /etc/authservice
+          livenessProbe:
+            initialDelaySeconds: 1
+            periodSeconds: 5
+            tcpSocket:
+              port: 10003
+          readinessProbe:
+            initialDelaySeconds: 5
+            periodSeconds: 5
+            httpGet:
+              port: 10004
+              path: /healthz
+      volumes:
+        - name: config
+          configMap:
+            name: authservice-config
diff --git a/examples/minimal/authz-policy.yaml b/examples/minimal/authz-policy.yaml
new file mode 100644
index 0000000..17c6cfd
--- /dev/null
+++ b/examples/minimal/authz-policy.yaml
@@ -0,0 +1,38 @@
+# Copyright 2024 Tetrate
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Set a policy to make sure all requests targeting services in the namespace where
+# this policy is applied, contain a token issued by the OIDC provider.
+apiVersion: security.istio.io/v1beta1
+kind: RequestAuthentication
+metadata:
+  name: example-authn
+spec:
+  jwtRules:
+    - issuer: "OIDC_PROVIDER_ISSUER"
+      jwksUri: "http://OIDC_PROVIDER_JWKS_URI" # can be omitted if the issuer has a well-known endpoint
+      forwardOriginalToken: true
+---
+# Set a policy to enforce that the token is present. The policy allows any subject, but it can be
+# further refined with constraints based on the JWT token claims.
+apiVersion: security.istio.io/v1beta1
+kind: AuthorizationPolicy
+metadata:
+  name: example-authz
+spec:
+  action: ALLOW
+  rules:
+    - from:
+        - source:
+            requestPrincipals: ["*"]
diff --git a/examples/minimal/istiod-values.yaml b/examples/minimal/istiod-values.yaml
new file mode 100644
index 0000000..1551a15
--- /dev/null
+++ b/examples/minimal/istiod-values.yaml
@@ -0,0 +1,24 @@
+# Copyright 2024 Tetrate
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+# Example Istiod values.yaml that configures the Authservice as an extension provider
+meshConfig:
+  extensionProviders:
+    # Configure the backend for the Auth Service provider that can be used in AuthorizationPolicies
+    # in CUSTOM mode.
+    - name: authservice-grpc
+      envoyExtAuthzGrpc:
+        # This must match the Kubernetes service and port where the authservice is listening.
+        service: "authservice.authservice.svc.cluster.local"
+        port: "10003"   # This port is the one to be set in the authservice config
diff --git a/examples/minimal/oidc-intercept-policy.yaml b/examples/minimal/oidc-intercept-policy.yaml
new file mode 100644
index 0000000..be28343
--- /dev/null
+++ b/examples/minimal/oidc-intercept-policy.yaml
@@ -0,0 +1,31 @@
+# Copyright 2024 Tetrate
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+apiVersion: security.istio.io/v1beta1
+kind: AuthorizationPolicy
+metadata:
+  name: authservice
+  # Applying this policy to the application namespace will intercept all requests
+  # that get to the sidecars in the namespace and forward them to the Authservice.
+spec:
+  action: CUSTOM
+  provider:
+    # Name defined in the extensionProviders property in the MeshConfig
+    # (the `istio` ConfigMap in the istio-system namespace)
+    name: authservice-grpc
+  # A single empty rule will force all requests to be forwarded to the external
+  # authorization backend, as long as the workload is captured by the selectors
+  # configured above.
+  rules:
+    - {}
diff --git a/examples/minimal/rbac.yaml b/examples/minimal/rbac.yaml
new file mode 100644
index 0000000..0d5d927
--- /dev/null
+++ b/examples/minimal/rbac.yaml
@@ -0,0 +1,37 @@
+# Copyright 2024 Tetrate
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+#     http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+
+apiVersion: rbac.authorization.k8s.io/v1
+kind: Role
+metadata:
+  name: authservice-secrets
+rules:
+  # Allow authservice to read the secrets in its namespace so it can read
+  # the OIDC client-secret from a Kubernetes secret instead of having it in clear text
+  # in the ConfigMap
+  - apiGroups: [""]
+    resources: ["secrets"]
+    verbs: ["get", "watch", "list"]
+---
+apiVersion: rbac.authorization.k8s.io/v1
+kind: RoleBinding
+metadata:
+  name: authservice-secrets
+roleRef:
+  apiGroup: rbac.authorization.k8s.io
+  kind: Role
+  name: authservice-secrets
+subjects:
+  - kind: ServiceAccount
+    name: authservice
diff --git a/go.mod b/go.mod
index 59d538f..5946259 100644
--- a/go.mod
+++ b/go.mod
@@ -14,7 +14,7 @@ require (
 	github.com/tetratelabs/log v0.2.3
 	github.com/tetratelabs/run v0.3.0
 	github.com/tetratelabs/telemetry v0.8.2
-	golang.org/x/net v0.22.0
+	golang.org/x/net v0.23.0
 	google.golang.org/genproto/googleapis/rpc v0.0.0-20240304212257-790db918fca8
 	google.golang.org/grpc v1.62.1
 	google.golang.org/protobuf v1.33.0
diff --git a/go.sum b/go.sum
index be5cedf..4e03780 100644
--- a/go.sum
+++ b/go.sum
@@ -175,8 +175,8 @@ golang.org/x/net v0.0.0-20200226121028-0de0cce0169b/go.mod h1:z5CRVTTTmAJ677TzLL
 golang.org/x/net v0.0.0-20201021035429-f5854403a974/go.mod h1:sp8m0HH+o8qH0wwXwYZr8TS3Oi6o0r6Gce1SSxlDquU=
 golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
 golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
-golang.org/x/net v0.22.0 h1:9sGLhx7iRIHEiX0oAJ3MRZMUCElJgy7Br1nO+AMN3Tc=
-golang.org/x/net v0.22.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
+golang.org/x/net v0.23.0 h1:7EYJ93RZ9vYSZAIb2x3lnuvqO5zneoD6IvWjuhfxjTs=
+golang.org/x/net v0.23.0/go.mod h1:JKghWKKOSdJwpW2GEx0Ja7fmaKnMsbu+MWVZTokSYmg=
 golang.org/x/oauth2 v0.18.0 h1:09qnuIAgzdx1XplqJvW6CQqMCtGZykZWcXzPMPUusvI=
 golang.org/x/oauth2 v0.18.0/go.mod h1:Wf7knwG0MPoWIMMBgFlEaSUDaKskp0dCfrlJRJXbBi8=
 golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
diff --git a/internal/authz/oidc.go b/internal/authz/oidc.go
index 7fa5407..d77ba90 100644
--- a/internal/authz/oidc.go
+++ b/internal/authz/oidc.go
@@ -17,6 +17,7 @@ package authz
 import (
 	"context"
 	"encoding/json"
+	"errors"
 	"fmt"
 	"io"
 	"net/http"
@@ -46,6 +47,10 @@ var (
 		{Header: &corev3.HeaderValue{Key: inthttp.HeaderCacheControl, Value: inthttp.HeaderCacheControlNoCache}},
 		{Header: &corev3.HeaderValue{Key: inthttp.HeaderPragma, Value: inthttp.HeaderPragmaNoCache}},
 	}
+
+	// ErrMissingLogoutRedirectURI is returned when the logout redirect uri is missing because it was not explicitly
+	// configured or the OIDC Discovery did not return it.
+	ErrMissingLogoutRedirectURI = errors.New("missing logout redirect uri")
 )
 
 // oidc handler is an implementation of the Handler interface that implements
@@ -65,7 +70,7 @@ type oidcHandler struct {
 func NewOIDCHandler(cfg *oidcv1.OIDCConfig, tlsPool internal.TLSConfigPool, jwks oidc.JWKSProvider,
 	sessions oidc.SessionStoreFactory, clock oidc.Clock, sessionGen oidc.SessionGenerator) (Handler, error) {
 
-	client, err := getHTTPClient(cfg, tlsPool)
+	client, err := inthttp.NewHTTPClient(cfg, tlsPool, internal.Logger(internal.IDP))
 	if err != nil {
 		return nil, err
 	}
@@ -86,23 +91,6 @@ func NewOIDCHandler(cfg *oidcv1.OIDCConfig, tlsPool internal.TLSConfigPool, jwks
 	}, nil
 }
 
-func getHTTPClient(cfg *oidcv1.OIDCConfig, tlsPool internal.TLSConfigPool) (*http.Client, error) {
-	transport := http.DefaultTransport.(*http.Transport).Clone()
-
-	var err error
-	if transport.TLSClientConfig, err = tlsPool.LoadTLSConfig(cfg); err != nil {
-		return nil, err
-	}
-
-	if cfg.ProxyUri != "" {
-		// config validation ensures that the proxy uri is valid
-		proxyURL, _ := url.Parse(cfg.ProxyUri)
-		transport.Proxy = http.ProxyURL(proxyURL)
-	}
-
-	return &http.Client{Transport: transport}, nil
-}
-
 // Process a CheckRequest and populate a CheckResponse according to the mockHandler configuration.
 func (o *oidcHandler) Process(ctx context.Context, req *envoy.CheckRequest, resp *envoy.CheckResponse) error {
 	log := o.log.Context(ctx)
@@ -138,7 +126,8 @@ func (o *oidcHandler) Process(ctx context.Context, req *envoy.CheckRequest, resp
 				return nil
 			}
 		}
-		log.Info("logout complete. Redirecting to logout redirect uri")
+		log.Info("logout complete. Redirecting to logout redirect uri",
+			"uri", o.config.GetLogout().GetRedirectUri())
 		deny := newDenyResponse()
 		// add IDP logout location
 		setRedirect(deny, o.config.GetLogout().GetRedirectUri())
@@ -151,7 +140,7 @@ func (o *oidcHandler) Process(ctx context.Context, req *envoy.CheckRequest, resp
 	// If the request does not have a session_id cookie,
 	// then generate a session id, put it in a header, and redirect for login.
 	if sessionID == "" {
-		log.Info("no session cookie detected. Generating new session and sending user to re-authenticate.")
+		log.Info("no session cookie detected. Generating new session and sending user to re-authenticate")
 		o.redirectToIDP(ctx, log, resp, req.GetAttributes().GetRequest().GetHttp(), "")
 		return nil
 	}
@@ -180,7 +169,7 @@ func (o *oidcHandler) Process(ctx context.Context, req *envoy.CheckRequest, resp
 	// If the user has a session_id cookie but there are no required tokens in the
 	// session store associated with it, then redirect for login.
 	if tokenResponse == nil {
-		log.Info("required tokens are not present. Sending user to re-authenticate.")
+		log.Info("required tokens are not present. Sending user to re-authenticate")
 		o.redirectToIDP(ctx, log, resp, req.GetAttributes().GetRequest().GetHttp(), sessionID)
 		return nil
 	}
@@ -188,14 +177,14 @@ func (o *oidcHandler) Process(ctx context.Context, req *envoy.CheckRequest, resp
 	// If both ID & Access token are still unexpired,
 	// then allow the request to proceed (no need to intervene).
 	log.Debug("checking tokens expiration")
-	expired, err := o.areRequiredTokensExpired(tokenResponse)
+	expired, err := o.areRequiredTokensExpired(log, tokenResponse)
 	if err != nil {
 		log.Error("error checking token expiration", err)
 		setDenyResponse(resp, newDenyResponse(), codes.Internal)
 		return nil
 	}
 	if !expired {
-		log.Info("tokens not expired. Allowing request to proceed.")
+		log.Info("tokens not expired. Allowing request to proceed")
 		o.allowResponse(resp, tokenResponse)
 		return nil
 	}
@@ -205,7 +194,7 @@ func (o *oidcHandler) Process(ctx context.Context, req *envoy.CheckRequest, resp
 	// If there is no refresh token,
 	// then direct the request to the identity provider for authentication
 	if tokenResponse.RefreshToken == "" {
-		log.Info("a token was expired, but session did not contain a refresh token. Sending user to re-authenticate.")
+		log.Info("a token was expired, but session did not contain a refresh token. Sending user to re-authenticate")
 		o.redirectToIDP(ctx, log, resp, req.GetAttributes().GetRequest().GetHttp(), sessionID)
 		return nil
 	}
@@ -216,7 +205,7 @@ func (o *oidcHandler) Process(ctx context.Context, req *envoy.CheckRequest, resp
 	log.Debug("attempting token refresh")
 	refreshedTokens := o.refreshToken(ctx, log, tokenResponse, tokenResponse.RefreshToken, sessionID)
 	if refreshedTokens == nil {
-		log.Info("token refresh failed. Sending user to re-authenticate.")
+		log.Info("token refresh failed. Sending user to re-authenticate")
 		o.redirectToIDP(ctx, log, resp, req.GetAttributes().GetRequest().GetHttp(), sessionID)
 		return nil
 	}
@@ -226,7 +215,7 @@ func (o *oidcHandler) Process(ctx context.Context, req *envoy.CheckRequest, resp
 		return nil
 	}
 
-	log.Info("token refresh successful. Allowing request to proceed.")
+	log.Info("token refresh successful. Allowing request to proceed")
 	o.allowResponse(resp, refreshedTokens)
 	return nil
 }
@@ -322,7 +311,7 @@ func (o *oidcHandler) retrieveTokens(ctx context.Context, log telemetry.Logger,
 	}
 
 	if stateFromStore == nil {
-		log.Info("missing state, nonce, and original url requested by user in the store. Cannot redirect.")
+		log.Info("missing state, nonce, and original url requested by user in the store. Cannot redirect")
 		deny := newDenyResponse()
 		deny.Body = "Oops, your session has expired. Please try again."
 		deny.Status = &typev3.HttpStatus{Code: typev3.StatusCode_BadRequest}
@@ -755,17 +744,24 @@ func encodeHeaderValue(preamble string, value string) string {
 }
 
 // areRequiredTokensExpired checks if the required tokens are expired.
-func (o *oidcHandler) areRequiredTokensExpired(tokens *oidc.TokenResponse) (bool, error) {
+func (o *oidcHandler) areRequiredTokensExpired(log telemetry.Logger, tokens *oidc.TokenResponse) (bool, error) {
 	idToken, err := tokens.ParseIDToken()
 	if err != nil {
 		return false, fmt.Errorf("parsing id token: %w", err)
 	}
 
-	if idToken.Expiration().Before(o.clock.Now()) {
+	now := o.clock.Now()
+	if idToken.Expiration().Before(now) {
+		log.Info("id token is expired", "exp", idToken.Expiration(), "expired_for", now.Sub(idToken.Expiration()))
 		return true, nil
 	}
-	if o.config.GetAccessToken() != nil && tokens.AccessToken != "" && !tokens.AccessTokenExpiresAt.IsZero() {
-		return tokens.AccessTokenExpiresAt.Before(o.clock.Now()), nil
+	if o.config.GetAccessToken() != nil &&
+		tokens.AccessToken != "" &&
+		!tokens.AccessTokenExpiresAt.IsZero() &&
+		tokens.AccessTokenExpiresAt.Before(now) {
+		log.Info("access token is expired", "exp", tokens.AccessTokenExpiresAt,
+			"expired_for", now.Sub(tokens.AccessTokenExpiresAt))
+		return true, nil
 	}
 	return false, nil
 }
@@ -839,5 +835,12 @@ func loadWellKnownConfig(client *http.Client, cfg *oidcv1.OIDCConfig) error {
 	}
 	cfg.GetJwksFetcher().JwksUri = wellKnownConfig.JWKSURL
 
+	if cfg.GetLogout() != nil && cfg.GetLogout().GetRedirectUri() == "" {
+		if wellKnownConfig.EndSessionEndpoint == "" {
+			return ErrMissingLogoutRedirectURI
+		}
+		cfg.GetLogout().RedirectUri = wellKnownConfig.EndSessionEndpoint
+	}
+
 	return nil
 }
diff --git a/internal/http/http.go b/internal/http/http.go
index 647b404..fbec176 100644
--- a/internal/http/http.go
+++ b/internal/http/http.go
@@ -16,7 +16,14 @@ package http
 
 import (
 	"encoding/base64"
+	"net/http"
+	"net/url"
 	"strings"
+
+	"github.com/tetratelabs/telemetry"
+
+	oidcv1 "github.com/istio-ecosystem/authservice/config/gen/go/v1/oidc"
+	"github.com/istio-ecosystem/authservice/internal"
 )
 
 // GetPathQueryFragment splits the given path into path, query, and fragment.
@@ -80,6 +87,36 @@ func EncodeCookieHeader(name string, value string, directives []string) string {
 	return b.String()
 }
 
+// BasicAuthHeader returns the value of the Authorization header for the given id and secret.
 func BasicAuthHeader(id string, secret string) string {
 	return "Basic " + base64.StdEncoding.EncodeToString([]byte(id+":"+secret))
 }
+
+// NewHTTPClient creates a new HTTP client with the given OIDC configuration and TLS pool.
+// If a logger is provided, it will log the requests and responses at debug level.
+func NewHTTPClient(cfg *oidcv1.OIDCConfig, tlsPool internal.TLSConfigPool, log telemetry.Logger) (*http.Client, error) {
+	transport := http.DefaultTransport.(*http.Transport).Clone()
+
+	var err error
+	if transport.TLSClientConfig, err = tlsPool.LoadTLSConfig(cfg); err != nil {
+		return nil, err
+	}
+
+	if cfg.ProxyUri != "" {
+		// config validation ensures that the proxy uri is valid
+		proxyURL, _ := url.Parse(cfg.ProxyUri)
+		transport.Proxy = http.ProxyURL(proxyURL)
+	}
+
+	if log != nil && log.Level() >= telemetry.LevelDebug {
+		return &http.Client{
+			Transport: &LoggingRoundTripper{
+				Log:      log,
+				Delegate: transport,
+			},
+		}, nil
+
+	}
+
+	return &http.Client{Transport: transport}, nil
+}
diff --git a/internal/http/logging.go b/internal/http/logging.go
new file mode 100644
index 0000000..aceaa4f
--- /dev/null
+++ b/internal/http/logging.go
@@ -0,0 +1,51 @@
+// Copyright 2024 Tetrate
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Copyright (c) Tetrate, Inc 2024 All Rights Reserved.
+
+package http
+
+import (
+	"net/http"
+	"net/http/httputil"
+
+	"github.com/tetratelabs/telemetry"
+)
+
+// LoggingRoundTripper is a http.RoundTripper that logs requests and responses.
+type LoggingRoundTripper struct {
+	Log      telemetry.Logger
+	Delegate http.RoundTripper
+}
+
+// RoundTrip logs all the requests and responses using the configured settings.
+func (l LoggingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
+	if l.Log.Level() < telemetry.LevelDebug {
+		return l.Delegate.RoundTrip(req)
+	}
+
+	if dump, derr := httputil.DumpRequest(req, true); derr == nil {
+		l.Log.Debug("request", "data", string(dump))
+	}
+
+	res, err := l.Delegate.RoundTrip(req)
+
+	if err == nil {
+		if dump, derr := httputil.DumpResponse(res, true); derr == nil {
+			l.Log.Debug("response", "data", string(dump))
+		}
+	}
+
+	return res, err
+}
diff --git a/internal/logging.go b/internal/logging.go
index 28f9926..5c18870 100644
--- a/internal/logging.go
+++ b/internal/logging.go
@@ -32,6 +32,7 @@ const (
 	Config   = "config"
 	Default  = "default"
 	Health   = "health"
+	IDP      = "idp"
 	JWKS     = "jwks"
 	Requests = "requests"
 	Server   = "server"
@@ -45,6 +46,7 @@ var scopes = map[string]string{
 	Config:   "Configuration messages",
 	Default:  "Default",
 	Health:   "Health server messages",
+	IDP:      "Identity provider requests/responses",
 	JWKS:     "JWKS update and parse messages",
 	Requests: "Logs all requests and responses received by the server",
 	Server:   "Server request handling messages",
@@ -158,14 +160,15 @@ func setLogLevels(log telemetry.Logger, logLevelMap map[string]telemetry.Level)
 		for _, logger := range scope.List() {
 			logger.SetLevel(level)
 		}
-	} else {
-		for k, l := range logLevelMap {
-			logger, ok := scope.Find(k)
-			if ok {
-				logger.SetLevel(l)
-			} else {
-				log.Info("invalid logger", "logger", k)
-			}
+		delete(logLevelMap, "all")
+	}
+
+	for k, l := range logLevelMap {
+		logger, ok := scope.Find(k)
+		if ok {
+			logger.SetLevel(l)
+		} else {
+			log.Info("invalid logger", "logger", k)
 		}
 	}
 }
diff --git a/internal/oidc/discovery.go b/internal/oidc/discovery.go
index 71f0682..8ea03e0 100644
--- a/internal/oidc/discovery.go
+++ b/internal/oidc/discovery.go
@@ -40,8 +40,20 @@ type WellKnownConfig struct {
 	TokenRevocationEndpoint  string   `json:"token_revocation_endpoint"`
 }
 
+var (
+	// wellKnownConfigs is a map of issuer URL to the OIDC well-known configuration
+	// It is used to cache well-known configurations as they usually don't change. URLs are usually stable, and the only
+	// things that are subject to change are the signing keys, but those are already watched periodically by the JWKS fetcher.
+	wellKnownConfigs = make(map[string]WellKnownConfig)
+)
+
 // GetWellKnownConfig retrieves the OIDC well-known configuration from the given issuer URL.
 func GetWellKnownConfig(client *http.Client, url string) (WellKnownConfig, error) {
+	cfg, ok := wellKnownConfigs[url]
+	if ok {
+		return cfg, nil
+	}
+
 	// Make a GET request to the well-known configuration endpoint
 	response, err := client.Get(url)
 	if err != nil {
@@ -55,10 +67,10 @@ func GetWellKnownConfig(client *http.Client, url string) (WellKnownConfig, error
 	}
 
 	// Decode the JSON response into the OIDCConfig struct
-	var cfg WellKnownConfig
 	if err = json.NewDecoder(response.Body).Decode(&cfg); err != nil {
 		return WellKnownConfig{}, err
 	}
 
+	wellKnownConfigs[url] = cfg
 	return cfg, nil
 }
diff --git a/internal/oidc/jwks.go b/internal/oidc/jwks.go
index d79b69f..5e9eaac 100644
--- a/internal/oidc/jwks.go
+++ b/internal/oidc/jwks.go
@@ -18,7 +18,6 @@ import (
 	"context"
 	"errors"
 	"fmt"
-	"net/http"
 	"time"
 
 	"github.com/lestrrat-go/httprc"
@@ -29,6 +28,7 @@ import (
 	configv1 "github.com/istio-ecosystem/authservice/config/gen/go/v1"
 	oidcv1 "github.com/istio-ecosystem/authservice/config/gen/go/v1/oidc"
 	"github.com/istio-ecosystem/authservice/internal"
+	inthttp "github.com/istio-ecosystem/authservice/internal/http"
 )
 
 var (
@@ -102,14 +102,11 @@ func (j *DefaultJWKSProvider) fetchDynamic(ctx context.Context, config *oidcv1.O
 	jwksConfig := config.GetJwksFetcher()
 
 	if !j.cache.IsRegistered(jwksConfig.JwksUri) {
-		transport := http.DefaultTransport.(*http.Transport).Clone()
-
-		var err error
-		if transport.TLSClientConfig, err = j.tlsPool.LoadTLSConfig(config); err != nil {
-			return nil, fmt.Errorf("error loading TLS config: %w", err)
+		client, err := inthttp.NewHTTPClient(config, j.tlsPool, internal.Logger(internal.IDP))
+		if err != nil {
+			return nil, err
 		}
 
-		client := &http.Client{Transport: transport}
 		refreshInterval := time.Duration(jwksConfig.PeriodicFetchIntervalSec) * time.Second
 		if refreshInterval == 0 {
 			refreshInterval = DefaultFetchInterval
diff --git a/internal/oidc/memory.go b/internal/oidc/memory.go
index d9b8756..b362350 100644
--- a/internal/oidc/memory.go
+++ b/internal/oidc/memory.go
@@ -70,6 +70,7 @@ func (m *memoryStore) GetTokenResponse(ctx context.Context, sessionID string) (*
 		return nil, nil
 	}
 
+	log.Debug("token response", "token_response", s.tokenResponse)
 	s.accessed = m.clock.Now()
 	return s.tokenResponse, nil
 }
@@ -96,6 +97,7 @@ func (m *memoryStore) GetAuthorizationState(ctx context.Context, sessionID strin
 		return nil, nil
 	}
 
+	log.Debug("authorization state", "state", s.authorizationState)
 	s.accessed = m.clock.Now()
 	return s.authorizationState, nil
 }
diff --git a/internal/oidc/redis.go b/internal/oidc/redis.go
index ce8c816..6033631 100644
--- a/internal/oidc/redis.go
+++ b/internal/oidc/redis.go
@@ -152,6 +152,8 @@ func (r *redisStore) GetTokenResponse(ctx context.Context, sessionID string) (*T
 		return nil, err
 	}
 
+	log.Debug("token response", "token_response", tokenResponse)
+
 	return tokenResponse, nil
 }
 
@@ -199,6 +201,8 @@ func (r *redisStore) GetAuthorizationState(ctx context.Context, sessionID string
 		return nil, err
 	}
 
+	log.Debug("authorization state", "state", state.AuthorizationState())
+
 	return state.AuthorizationState(), nil
 }
 


Test patch:
diff --git a/e2e/istio/istio_test.go b/e2e/istio/istio_test.go
index 04a3c2b..97c8165 100644
--- a/e2e/istio/istio_test.go
+++ b/e2e/istio/istio_test.go
@@ -39,7 +39,7 @@ func (i *IstioSuite) TestIstioEnforcement() {
 			// Initialize the test OIDC client that will keep track of the state of the OIDC login process
 			// Initialize it for each test to not reuse the session between them
 			client, err := e2e.NewOIDCTestClient(
-				e2e.WithLoggingOptions(i.T().Log, true),
+				e2e.WithLoggingOptions(i.T().Log),
 				e2e.WithCustomCA(testCAFile),
 				// Map the keycloak cluster DNS name to the local address where the service is exposed
 				e2e.WithCustomAddressMappings(map[string]string{
diff --git a/e2e/keycloak/authz-config.json b/e2e/keycloak/authz-config.json
index a999f9c..4f158cc 100644
--- a/e2e/keycloak/authz-config.json
+++ b/e2e/keycloak/authz-config.json
@@ -22,8 +22,7 @@
               "header": "x-access-token"
             },
             "logout": {
-              "path": "/logout",
-              "redirect_uri": "https://host.docker.internal:9443/realms/master/protocol/openid-connect/logout"
+              "path": "/logout"
             },
             "redis_session_store_config": {
               "server_uri": "redis://redis:6379"
diff --git a/e2e/keycloak/keycloak_test.go b/e2e/keycloak/keycloak_test.go
index 1f28443..ab5bd4b 100644
--- a/e2e/keycloak/keycloak_test.go
+++ b/e2e/keycloak/keycloak_test.go
@@ -51,7 +51,7 @@ var (
 func TestOIDCUsesTheConfiguredProxy(t *testing.T) {
 	client, err := e2e.NewOIDCTestClient(
 		e2e.WithCustomCA(testCAFile),
-		e2e.WithLoggingOptions(t.Log, true),
+		e2e.WithLoggingOptions(t.Log),
 		e2e.WithCustomAddressMappings(customAddressMappings),
 	)
 	require.NoError(t, err)
@@ -82,7 +82,7 @@ func TestOIDC(t *testing.T) {
 	// Initialize the test OIDC client that will keep track of the state of the OIDC login process
 	client, err := e2e.NewOIDCTestClient(
 		e2e.WithCustomCA(testCAFile),
-		e2e.WithLoggingOptions(t.Log, true),
+		e2e.WithLoggingOptions(t.Log),
 		e2e.WithCustomAddressMappings(customAddressMappings),
 	)
 	require.NoError(t, err)
@@ -109,7 +109,7 @@ func TestOIDCRefreshTokens(t *testing.T) {
 	// Initialize the test OIDC client that will keep track of the state of the OIDC login process
 	client, err := e2e.NewOIDCTestClient(
 		e2e.WithCustomCA(testCAFile),
-		e2e.WithLoggingOptions(t.Log, true),
+		e2e.WithLoggingOptions(t.Log),
 		e2e.WithCustomAddressMappings(customAddressMappings),
 	)
 	require.NoError(t, err)
@@ -163,7 +163,7 @@ func TestOIDCLogout(t *testing.T) {
 	// Initialize the test OIDC client that will keep track of the state of the OIDC login process
 	client, err := e2e.NewOIDCTestClient(
 		e2e.WithCustomCA(testCAFile),
-		e2e.WithLoggingOptions(t.Log, true),
+		e2e.WithLoggingOptions(t.Log),
 		e2e.WithBaseURL(idpBaseURLHost),
 		e2e.WithCustomAddressMappings(customAddressMappings),
 	)
diff --git a/e2e/legacy/legacy_test.go b/e2e/legacy/legacy_test.go
index 42a7d1d..d13c08a 100644
--- a/e2e/legacy/legacy_test.go
+++ b/e2e/legacy/legacy_test.go
@@ -51,7 +51,7 @@ func TestOIDC(t *testing.T) {
 	// Initialize the test OIDC client that will keep track of the state of the OIDC login process
 	client, err := e2e.NewOIDCTestClient(
 		e2e.WithCustomCA(testCAFile),
-		e2e.WithLoggingOptions(t.Log, true),
+		e2e.WithLoggingOptions(t.Log),
 		e2e.WithCustomAddressMappings(customAddressMappings),
 	)
 	require.NoError(t, err)
@@ -78,7 +78,7 @@ func TestOIDCRefreshTokens(t *testing.T) {
 	// Initialize the test OIDC client that will keep track of the state of the OIDC login process
 	client, err := e2e.NewOIDCTestClient(
 		e2e.WithCustomCA(testCAFile),
-		e2e.WithLoggingOptions(t.Log, true),
+		e2e.WithLoggingOptions(t.Log),
 		e2e.WithCustomAddressMappings(customAddressMappings),
 	)
 	require.NoError(t, err)
@@ -131,7 +131,7 @@ func TestOIDCLogout(t *testing.T) {
 	// Initialize the test OIDC client that will keep track of the state of the OIDC login process
 	client, err := e2e.NewOIDCTestClient(
 		e2e.WithCustomCA(testCAFile),
-		e2e.WithLoggingOptions(t.Log, true),
+		e2e.WithLoggingOptions(t.Log),
 		e2e.WithBaseURL(idpBaseURLHost),
 		e2e.WithCustomAddressMappings(customAddressMappings),
 	)
diff --git a/e2e/testclient.go b/e2e/testclient.go
index 950d3c4..7b2509e 100644
--- a/e2e/testclient.go
+++ b/e2e/testclient.go
@@ -22,38 +22,17 @@ import (
 	"io"
 	"net"
 	"net/http"
-	"net/http/httputil"
 	"net/url"
 	"os"
+	"slices"
 	"strings"
 
+	"github.com/tetratelabs/telemetry"
+	"github.com/tetratelabs/telemetry/function"
 	"golang.org/x/net/html"
-)
-
-// LoggingRoundTripper is a http.RoundTripper that logs requests and responses.
-type LoggingRoundTripper struct {
-	LogFunc  func(...any)
-	LogBody  bool
-	Delegate http.RoundTripper
-}
-
-// RoundTrip logs all the requests and responses using the configured settings.
-func (l LoggingRoundTripper) RoundTrip(req *http.Request) (*http.Response, error) {
-	if l.LogFunc != nil {
-		if dump, derr := httputil.DumpRequest(req, l.LogBody); derr == nil {
-			l.LogFunc(string(dump))
-		}
-	}
 
-	res, err := l.Delegate.RoundTrip(req)
-	if err == nil && l.LogFunc != nil {
-		if dump, derr := httputil.DumpResponse(res, l.LogBody); derr == nil {
-			l.LogFunc(string(dump))
-		}
-	}
-
-	return res, err
-}
+	inthttp "github.com/istio-ecosystem/authservice/internal/http"
+)
 
 // CookieTracker is a http.RoundTripper that tracks cookies received from the server.
 type CookieTracker struct {
@@ -91,7 +70,6 @@ type OIDCTestClient struct {
 	customCA     *x509.CertPool   // Custom TLS configuration, if needed
 	mappings     *AddressMappings // Custom address mappings
 	logFunc      func(...any)     // Logging function to log all requests and responses
-	logBody      bool             // Whether to log the request and response bodies
 }
 
 // Option is a functional option for configuring the OIDCTestClient.
@@ -113,10 +91,9 @@ func WithCustomCA(caCert string) Option {
 }
 
 // WithLoggingOptions configures the OIDCTestClient to log requests and responses.
-func WithLoggingOptions(logFunc func(...any), logBody bool) Option {
+func WithLoggingOptions(logFunc func(...any)) Option {
 	return func(o *OIDCTestClient) error {
 		o.logFunc = logFunc
-		o.logBody = logBody
 		return nil
 	}
 }
@@ -171,7 +148,7 @@ func WithBaseURL(idpBaseURL string) Option {
 func NewOIDCTestClient(opts ...Option) (*OIDCTestClient, error) {
 	var (
 		defaultTransport = http.DefaultTransport.(*http.Transport).Clone()
-		logging          = &LoggingRoundTripper{Delegate: defaultTransport}
+		logging          = &inthttp.LoggingRoundTripper{Delegate: defaultTransport}
 		cookieTracker    = &CookieTracker{Cookies: make(map[string]*http.Cookie), Delegate: logging}
 		client           = &OIDCTestClient{http: &http.Client{Transport: cookieTracker}}
 	)
@@ -182,8 +159,11 @@ func NewOIDCTestClient(opts ...Option) (*OIDCTestClient, error) {
 		}
 	}
 
-	logging.LogFunc = client.logFunc
-	logging.LogBody = client.logBody
+	logging.Log = function.NewLogger(func(_ telemetry.Level, msg string, _ error, values function.Values) {
+		args := append([]any{msg}, slices.Concat(values.FromMethod, values.FromContext, values.FromLogger)...)
+		client.logFunc(args...)
+	})
+	logging.Log.SetLevel(telemetry.LevelDebug)
 
 	defaultTransport.TLSClientConfig = &tls.Config{RootCAs: client.customCA}
 	if client.mappings != nil {
diff --git a/internal/authz/oidc_test.go b/internal/authz/oidc_test.go
index 2cfe65e..c54a4e1 100644
--- a/internal/authz/oidc_test.go
+++ b/internal/authz/oidc_test.go
@@ -36,6 +36,7 @@ import (
 	"github.com/tetratelabs/telemetry"
 	"google.golang.org/grpc/codes"
 	"google.golang.org/grpc/test/bufconn"
+	"google.golang.org/protobuf/proto"
 
 	configv1 "github.com/istio-ecosystem/authservice/config/gen/go/v1"
 	oidcv1 "github.com/istio-ecosystem/authservice/config/gen/go/v1/oidc"
@@ -168,9 +169,19 @@ var (
 			ClientSecret: "test-client-secret",
 		},
 		Scopes: []string{"openid", "email"},
+		Logout: &oidcv1.LogoutConfig{Path: "/logout"},
 	}
 
 	wellKnownURIs = `
+{
+	"issuer": "http://idp-test-server",
+	"authorization_endpoint": "http://idp-test-server/authorize",
+	"end_session_endpoint": "http://idp-test-server/endsession",
+	"token_endpoint": "http://idp-test-server/token",
+	"jwks_uri": "http://idp-test-server/jwks"
+}`
+
+	wellKnownURIsNoEndSessionEndpoint = `
 {
 	"issuer": "http://idp-test-server",
 	"authorization_endpoint": "http://idp-test-server/authorize",
@@ -343,7 +354,7 @@ func TestOIDCProcess(t *testing.T) {
 
 	// The following subset of tests is testing the callback requests, so there's expected communication with the IDP server.
 
-	idpServer := newServer()
+	idpServer := newServer(wellKnownURIs)
 	h.(*oidcHandler).httpClient = idpServer.newHTTPClient()
 
 	callbackTests := []struct {
@@ -964,7 +975,7 @@ func TestOIDCProcessWithFailingSessionStore(t *testing.T) {
 		})
 	}
 
-	idpServer := newServer()
+	idpServer := newServer(wellKnownURIs)
 	idpServer.statusCode = http.StatusOK
 	idpServer.tokensResponse = &idpTokensResponse{
 		IDToken:     newJWT(t, jwkPriv, jwt.NewBuilder().Audience([]string{"test-client-id"}).Claim("nonce", newNonce)),
@@ -1068,7 +1079,7 @@ func TestOIDCProcessWithFailingJWKSProvider(t *testing.T) {
 	h, err := NewOIDCHandler(basicOIDCConfig, tlsPool, funcJWKSProvider, sessions, clock, oidc.NewStaticGenerator(newSessionID, newNonce, newState))
 	require.NoError(t, err)
 
-	idpServer := newServer()
+	idpServer := newServer(wellKnownURIs)
 	h.(*oidcHandler).httpClient = idpServer.newHTTPClient()
 
 	ctx := context.Background()
@@ -1359,7 +1370,7 @@ func TestAreTokensExpired(t *testing.T) {
 				tokResp.AccessTokenExpiresAt = tt.accessTokenExpiration
 			}
 
-			got, err := h.(*oidcHandler).areRequiredTokensExpired(tokResp)
+			got, err := h.(*oidcHandler).areRequiredTokensExpired(h.(*oidcHandler).log, tokResp)
 			require.NoError(t, err)
 			require.Equal(t, tt.want, got)
 		})
@@ -1367,21 +1378,35 @@ func TestAreTokensExpired(t *testing.T) {
 }
 
 func TestLoadWellKnownConfig(t *testing.T) {
-	idpServer := newServer()
+	idpServer := newServer(wellKnownURIs)
+	idpServer.Start()
+	t.Cleanup(idpServer.Stop)
+
+	cfg := proto.Clone(dynamicOIDCConfig).(*oidcv1.OIDCConfig)
+	require.NoError(t, loadWellKnownConfig(idpServer.newHTTPClient(), cfg))
+	require.Equal(t, cfg.AuthorizationUri, "http://idp-test-server/authorize")
+	require.Equal(t, cfg.TokenUri, "http://idp-test-server/token")
+	require.Equal(t, cfg.GetJwksFetcher().GetJwksUri(), "http://idp-test-server/jwks")
+	require.Equal(t, cfg.GetLogout().GetRedirectUri(), "http://idp-test-server/endsession")
+}
+
+func TestLoadWellKnownConfigMissingLogoutRedirectURI(t *testing.T) {
+	idpServer := newServer(wellKnownURIsNoEndSessionEndpoint)
 	idpServer.Start()
 	t.Cleanup(idpServer.Stop)
 
-	require.NoError(t, loadWellKnownConfig(idpServer.newHTTPClient(), dynamicOIDCConfig))
-	require.Equal(t, dynamicOIDCConfig.AuthorizationUri, "http://idp-test-server/authorize")
-	require.Equal(t, dynamicOIDCConfig.TokenUri, "http://idp-test-server/token")
-	require.Equal(t, dynamicOIDCConfig.GetJwksFetcher().GetJwksUri(), "http://idp-test-server/jwks")
+	cfg := proto.Clone(dynamicOIDCConfig).(*oidcv1.OIDCConfig)
+	cfg.ConfigurationUri = "http://missing-logout/.well-known/openid-configuration"
+	require.ErrorIs(t, loadWellKnownConfig(idpServer.newHTTPClient(), cfg), ErrMissingLogoutRedirectURI)
 }
 
 func TestLoadWellKnownConfigError(t *testing.T) {
 	clock := oidc.Clock{}
 	tlsPool := internal.NewTLSConfigPool(context.Background())
+	cfg := proto.Clone(dynamicOIDCConfig).(*oidcv1.OIDCConfig)
+	cfg.ConfigurationUri = "http://stopped-server/.well-known/openid-configuration"
 	sessions := &mockSessionStoreFactory{store: oidc.NewMemoryStore(&clock, time.Hour, time.Hour)}
-	_, err := NewOIDCHandler(dynamicOIDCConfig, tlsPool, oidc.NewJWKSProvider(newConfigFor(basicOIDCConfig), tlsPool),
+	_, err := NewOIDCHandler(cfg, tlsPool, oidc.NewJWKSProvider(newConfigFor(basicOIDCConfig), tlsPool),
 		sessions, clock, oidc.NewStaticGenerator(newSessionID, newNonce, newState))
 	require.Error(t, err) // Fail to retrieve the dynamic config since the test server is not running
 }
@@ -1597,7 +1622,7 @@ type idpServer struct {
 	statusCode     int
 }
 
-func newServer() *idpServer {
+func newServer(wellKnownPayload string) *idpServer {
 	s := &http.Server{}
 	idpServer := &idpServer{server: s, listener: bufconn.Listen(1024)}
 
@@ -1616,7 +1641,7 @@ func newServer() *idpServer {
 	handler.HandleFunc("/.well-known/openid-configuration", func(w http.ResponseWriter, r *http.Request) {
 		w.Header().Set("Content-Type", "application/json")
 		w.WriteHeader(http.StatusOK)
-		_, _ = w.Write([]byte(wellKnownURIs))
+		_, _ = w.Write([]byte(wellKnownPayload))
 	})
 	s.Handler = handler
 	return idpServer
diff --git a/internal/http/http_test.go b/internal/http/http_test.go
index 2a79502..4e2c6fe 100644
--- a/internal/http/http_test.go
+++ b/internal/http/http_test.go
@@ -15,10 +15,17 @@
 package http
 
 import (
+	"context"
 	"encoding/base64"
+	"net/http"
 	"testing"
 
 	"github.com/stretchr/testify/require"
+	"github.com/tetratelabs/telemetry"
+	"google.golang.org/protobuf/types/known/structpb"
+
+	oidcv1 "github.com/istio-ecosystem/authservice/config/gen/go/v1/oidc"
+	"github.com/istio-ecosystem/authservice/internal"
 )
 
 func TestGetPathQueryFragment(t *testing.T) {
@@ -132,3 +139,53 @@ func TestBasicAuthHeader(t *testing.T) {
 		})
 	}
 }
+
+func TestNewHTTPClient(t *testing.T) {
+	t.Run("proxy-skip-verify", func(t *testing.T) {
+		cfg := &oidcv1.OIDCConfig{
+			ProxyUri:           "http://localhost:8080",
+			SkipVerifyPeerCert: &structpb.Value{Kind: &structpb.Value_BoolValue{BoolValue: true}},
+		}
+		pool := internal.NewTLSConfigPool(context.Background())
+
+		client, err := NewHTTPClient(cfg, pool, nil)
+		require.NoError(t, err)
+		require.IsType(t, &http.Transport{}, client.Transport)
+		require.NotNil(t, client.Transport.(*http.Transport).Proxy)
+		require.True(t, client.Transport.(*http.Transport).TLSClientConfig.InsecureSkipVerify)
+	})
+
+	t.Run("invalid-tls", func(t *testing.T) {
+		cfg := &oidcv1.OIDCConfig{
+			TrustedCaConfig: &oidcv1.OIDCConfig_TrustedCertificateAuthorityFile{
+				TrustedCertificateAuthorityFile: "unexisting",
+			},
+		}
+		pool := internal.NewTLSConfigPool(context.Background())
+
+		_, err := NewHTTPClient(cfg, pool, nil)
+		require.Error(t, err)
+	})
+
+	t.Run("disabled-logger", func(t *testing.T) {
+		cfg := &oidcv1.OIDCConfig{}
+		pool := internal.NewTLSConfigPool(context.Background())
+		log := telemetry.NoopLogger()
+		log.SetLevel(telemetry.LevelInfo)
+
+		client, err := NewHTTPClient(cfg, pool, log)
+		require.NoError(t, err)
+		require.IsType(t, &http.Transport{}, client.Transport)
+	})
+
+	t.Run("enabled-logger", func(t *testing.T) {
+		cfg := &oidcv1.OIDCConfig{}
+		pool := internal.NewTLSConfigPool(context.Background())
+		log := telemetry.NoopLogger()
+		log.SetLevel(telemetry.LevelDebug)
+
+		client, err := NewHTTPClient(cfg, pool, log)
+		require.NoError(t, err)
+		require.IsType(t, &LoggingRoundTripper{}, client.Transport)
+	})
+}
diff --git a/internal/http/logging_test.go b/internal/http/logging_test.go
new file mode 100644
index 0000000..8a3c9d6
--- /dev/null
+++ b/internal/http/logging_test.go
@@ -0,0 +1,101 @@
+// Copyright 2024 Tetrate
+//
+// Licensed under the Apache License, Version 2.0 (the "License");
+// you may not use this file except in compliance with the License.
+// You may obtain a copy of the License at
+//
+//     http://www.apache.org/licenses/LICENSE-2.0
+//
+// Unless required by applicable law or agreed to in writing, software
+// distributed under the License is distributed on an "AS IS" BASIS,
+// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+// See the License for the specific language governing permissions and
+// limitations under the License.
+
+// Copyright (c) Tetrate, Inc 2024 All Rights Reserved.
+
+package http
+
+import (
+	"bufio"
+	"net/http"
+	"strings"
+	"testing"
+
+	"github.com/stretchr/testify/require"
+	"github.com/tetratelabs/telemetry"
+	"github.com/tetratelabs/telemetry/function"
+)
+
+const (
+	request  = "POST / HTTP/1.1\r\nHost: example.com\r\n\r\nreq body"
+	response = "HTTP/1.1 200 OK\r\nConnection: close\r\n\r\nresp body"
+)
+
+func TestLoggingRoundTripper(t *testing.T) {
+	var (
+		rt  = &recordedRoundTrip{logs: make([]logEntry, 0)}
+		lrt = LoggingRoundTripper{
+			Log: function.NewLogger(rt.log),
+			Delegate: roundTripperFunc(func(req *http.Request) (*http.Response, error) {
+				return http.ReadResponse(bufio.NewReader(strings.NewReader(response)), req)
+			}),
+		}
+	)
+
+	tests := []struct {
+		name  string
+		level telemetry.Level
+		want  []logEntry
+	}{
+		{"no-debug", telemetry.LevelInfo, []logEntry{}},
+		{"debug", telemetry.LevelDebug, []logEntry{
+			{
+				level:  telemetry.LevelDebug,
+				msg:    "request",
+				values: []any{"data", request},
+			},
+			{
+				level:  telemetry.LevelDebug,
+				msg:    "response",
+				values: []any{"data", response},
+			},
+		}},
+	}
+
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			lrt.Log.SetLevel(tt.level)
+			req, err := http.NewRequest("POST", "http://example.com", strings.NewReader("req body"))
+			require.NoError(t, err)
+
+			_, err = lrt.RoundTrip(req)
+			require.NoError(t, err)
+			require.Equal(t, tt.want, rt.logs)
+		})
+	}
+}
+
+type roundTripperFunc func(req *http.Request) (*http.Response, error)
+
+func (fn roundTripperFunc) RoundTrip(req *http.Request) (*http.Response, error) {
+	return fn(req)
+}
+
+type recordedRoundTrip struct {
+	logs []logEntry
+}
+
+type logEntry struct {
+	level  telemetry.Level
+	msg    string
+	values []any
+}
+
+func (r *recordedRoundTrip) log(level telemetry.Level, msg string, _ error, values function.Values) {
+	r.logs = append(r.logs, logEntry{
+		level:  level,
+		msg:    msg,
+		values: values.FromMethod,
+	})
+}
diff --git a/internal/logging_test.go b/internal/logging_test.go
index 5a15d97..8d2b788 100644
--- a/internal/logging_test.go
+++ b/internal/logging_test.go
@@ -59,7 +59,7 @@ func TestLoggingSetup(t *testing.T) {
 		{"l1:debug", telemetry.LevelDebug, telemetry.LevelInfo, false},
 		{"l1:debug,l2:debug", telemetry.LevelDebug, telemetry.LevelDebug, false},
 		{"invalid:debug,l2:error", telemetry.LevelInfo, telemetry.LevelError, false},
-		{"all:none,l1:debug", telemetry.LevelNone, telemetry.LevelNone, false},
+		{"all:none,l1:debug", telemetry.LevelDebug, telemetry.LevelNone, false},
 		{"", telemetry.LevelInfo, telemetry.LevelInfo, false},
 		{",", telemetry.LevelInfo, telemetry.LevelInfo, true},
 		{":", telemetry.LevelInfo, telemetry.LevelInfo, true},
diff --git a/internal/oidc/discovery_test.go b/internal/oidc/discovery_test.go
index c966578..10bc981 100644
--- a/internal/oidc/discovery_test.go
+++ b/internal/oidc/discovery_test.go
@@ -53,7 +53,7 @@ func TestWellKnownConfig(t *testing.T) {
 		{"ok", "http://example.com/.well-known/openid-configuration", validWellKnownJSON, false, validWellKnown},
 		{"not-found", "http://example.com/not-found", validWellKnownJSON, true, WellKnownConfig{}},
 		{"invalid-url", "invalid", validWellKnownJSON, true, WellKnownConfig{}},
-		{"invalid-json", "http://example.com/.well-known/openid-configuration", invalidWellKnownJSON, true, WellKnownConfig{}},
+		{"invalid-json", "http://example2.com/.well-known/openid-configuration", invalidWellKnownJSON, true, WellKnownConfig{}},
 	}
 
 	for _, tt := range tests {
@@ -74,6 +74,25 @@ func TestWellKnownConfig(t *testing.T) {
 	}
 }
 
+func TestWellKnownConfigCache(t *testing.T) {
+	s := newServer()
+	s.wellKnownConfig = validWellKnownJSON
+	s.Start()
+	c := s.newHTTPClient()
+
+	got, err := GetWellKnownConfig(c, "http://example.com/.well-known/openid-configuration")
+	require.NoError(t, err)
+	require.Equal(t, validWellKnown, got)
+
+	// Stop the server and run the well-known request again.
+	// It should succeed and return the cached value.
+	s.Stop()
+
+	got, err = GetWellKnownConfig(c, "http://example.com/.well-known/openid-configuration")
+	require.NoError(t, err)
+	require.Equal(t, validWellKnown, got)
+}
+
 type idpServer struct {
 	server          *http.Server
 	listener        *bufconn.Listener