git.iko.soy/typeable/oauth2-proxy
on master help sign in
.devcontainer/Dockerfile+1−1
since splitsnapshots
unifiedsplit
@@ -1,4 +1,4 @@
1−FROM mcr.microsoft.com/vscode/devcontainers/go:1-1.25
1+FROM mcr.microsoft.com/vscode/devcontainers/go:1-1.26
2
2
3SHELL ["/bin/bash", "-o", "pipefail", "-c"]
3SHELL ["/bin/bash", "-o", "pipefail", "-c"]
4
4
.github/ISSUE_TEMPLATE/config.yml+2−2
@@ -1,6 +1,6 @@
1blank_issues_enabled: false
1blank_issues_enabled: false
2contact_links:
2contact_links:
3− - name: OAuth2-Proxy Slack
3+ - name: OAuth2 Proxy Slack
4− url: https://gophers.slack.com/messages/CM2RSS25N
4+ url: https://cloud-native.slack.com/archives/C098Y5URZ2N
5 about: Feel free to ask any questions here.
5 about: Feel free to ask any questions here.
6
6
.github/renovate.json5+9
@@ -46,6 +46,15 @@
46 depNameTemplate: "alpine",
46 depNameTemplate: "alpine",
47 datasourceTemplate: "docker",
47 datasourceTemplate: "docker",
48 },
48 },
49+ {
50+ customType: "regex",
51+ fileMatch: [
52+ "^Makefile",
53+ ],
54+ matchStrings: [
55+ "# renovate: datasource=(?<datasource>.*?) depName=(?<depName>.*?)\\s+?GOLANGCILINT_VERSION \\?= (?<currentValue>.*)\\s",
56+ ],
57+ },
49 {
58 {
50 customType: "regex",
59 customType: "regex",
51 fileMatch: ["(^|/)\\.github/workflows/[^/]+\\.ya?ml$"],
60 fileMatch: ["(^|/)\\.github/workflows/[^/]+\\.ya?ml$"],
.github/workflows/ci.yml+8−14
@@ -19,7 +19,7 @@ jobs:
19 runs-on: ubuntu-latest
19 runs-on: ubuntu-latest
20 steps:
20 steps:
21 - name: Check out code
21 - name: Check out code
22− uses: actions/checkout@v6
22+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
23 with:
23 with:
24 fetch-depth: 0
24 fetch-depth: 0
25
25
@@ -30,17 +30,11 @@ jobs:
30 id: go-version
30 id: go-version
31
31
32 - name: Set up Go
32 - name: Set up Go
33− uses: actions/setup-go@v6
33+ uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
34 with:
34 with:
35 go-version: ${{ steps.go-version.outputs.version }}
35 go-version: ${{ steps.go-version.outputs.version }}
36 check-latest: true
36 check-latest: true
37
37
38− - name: Install golangci-lint
39− uses: golangci/golangci-lint-action@v9
40− with:
41− install-only: true
42− version: v2.11.4 # renovate: datasource=github-tags depName=golangci/golangci-lint
43−
44 - name: Verify Code Generation
38 - name: Verify Code Generation
45 run: |
39 run: |
46 make verify-generate
40 make verify-generate
@@ -74,14 +68,14 @@ jobs:
74
68
75 - name: Upload Coverage Report
69 - name: Upload Coverage Report
76 if: github.event_name == 'push'
70 if: github.event_name == 'push'
77− uses: qltysh/qlty-action/coverage@v2
71+ uses: qltysh/qlty-action/coverage@08a0a862c159eae9b9003081da6663d96efef637 # v2.3.0
78 with:
72 with:
79 oidc: true
73 oidc: true
80 files: lcov.info
74 files: lcov.info
81
75
82 - name: Run Trivy vulnerability scanner
76 - name: Run Trivy vulnerability scanner
83 if: (!startsWith(github.head_ref, 'release'))
77 if: (!startsWith(github.head_ref, 'release'))
84− uses: aquasecurity/trivy-action@0.35.0
78+ uses: aquasecurity/trivy-action@ed142fd0673e97e23eac54620cfb913e5ce36c25 # v0.36.0
85 with:
79 with:
86 scan-type: 'rootfs'
80 scan-type: 'rootfs'
87 scan-ref: './oauth2-proxy'
81 scan-ref: './oauth2-proxy'
@@ -93,7 +87,7 @@ jobs:
93
87
94 - name: Upload Trivy scan results
88 - name: Upload Trivy scan results
95 if: (!startsWith(github.head_ref, 'release'))
89 if: (!startsWith(github.head_ref, 'release'))
96− uses: github/codeql-action/upload-sarif@v4
90+ uses: github/codeql-action/upload-sarif@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
97 with:
91 with:
98 sarif_file: 'trivy-results.sarif'
92 sarif_file: 'trivy-results.sarif'
99
93
@@ -101,16 +95,16 @@ jobs:
101 runs-on: ubuntu-latest
95 runs-on: ubuntu-latest
102 steps:
96 steps:
103 - name: Check out code
97 - name: Check out code
104− uses: actions/checkout@v6
98+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
105 with:
99 with:
106 fetch-depth: 0
100 fetch-depth: 0
107
101
108 - name: Set up QEMU
102 - name: Set up QEMU
109− uses: docker/setup-qemu-action@v3
103+ uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
110
104
111 - name: Set up Docker Buildx
105 - name: Set up Docker Buildx
112 id: buildx
106 id: buildx
113− uses: docker/setup-buildx-action@v3
107+ uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
114
108
115 - name: Docker Build
109 - name: Docker Build
116 if: (!startsWith(github.head_ref, 'release'))
110 if: (!startsWith(github.head_ref, 'release'))
.github/workflows/codeql.yml+4−4
@@ -32,18 +32,18 @@ jobs:
32
32
33 steps:
33 steps:
34 - name: Checkout repository
34 - name: Checkout repository
35− uses: actions/checkout@v6
35+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
36
36
37 # Initializes the CodeQL tools for scanning.
37 # Initializes the CodeQL tools for scanning.
38 - name: Initialize CodeQL
38 - name: Initialize CodeQL
39− uses: github/codeql-action/init@v3
39+ uses: github/codeql-action/init@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
40 with:
40 with:
41 languages: go
41 languages: go
42
42
43 # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
43 # Autobuild attempts to build any compiled languages (C/C++, C#, or Java).
44 # If this step fails, then you should remove it and run the build manually (see below)
44 # If this step fails, then you should remove it and run the build manually (see below)
45 - name: Autobuild
45 - name: Autobuild
46− uses: github/codeql-action/autobuild@v3
46+ uses: github/codeql-action/autobuild@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
47
47
48 # ℹ️ Command-line programs to run using the OS shell.
48 # ℹ️ Command-line programs to run using the OS shell.
49 # 📚 https://git.io/JvXDl
49 # 📚 https://git.io/JvXDl
@@ -57,4 +57,4 @@ jobs:
57 # make release
57 # make release
58
58
59 - name: Perform CodeQL Analysis
59 - name: Perform CodeQL Analysis
60− uses: github/codeql-action/analyze@v3
60+ uses: github/codeql-action/analyze@ff2f1c621b7f889edc0d3c761ac2e6a3f8cdb0dd # v4.37.7
.github/workflows/create-release.yml+2−2
@@ -17,7 +17,7 @@ jobs:
17 runs-on: ubuntu-latest
17 runs-on: ubuntu-latest
18 steps:
18 steps:
19 - name: Check out code
19 - name: Check out code
20− uses: actions/checkout@v6
20+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
21 with:
21 with:
22 ref: master
22 ref: master
23 fetch-depth: 0
23 fetch-depth: 0
@@ -54,7 +54,7 @@ jobs:
54 git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
54 git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
55
55
56 - name: Setup node
56 - name: Setup node
57− uses: actions/setup-node@v6
57+ uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
58 with:
58 with:
59 node-version-file: docs/package.json
59 node-version-file: docs/package.json
60
60
.github/workflows/docs.yml+7−7
@@ -14,13 +14,13 @@ jobs:
14 if: github.event_name == 'pull_request'
14 if: github.event_name == 'pull_request'
15 runs-on: ubuntu-latest
15 runs-on: ubuntu-latest
16 steps:
16 steps:
17− - uses: actions/checkout@v6
17+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
18
18
19 - name: Setup Pages
19 - name: Setup Pages
20 id: pages
20 id: pages
21− uses: actions/configure-pages@v5
21+ uses: actions/configure-pages@45bfe0192ca1faeb007ade9deae92b16b8254a0d # v6.0.0
22
22
23− - uses: actions/setup-node@v6
23+ - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
24 with:
24 with:
25 # renovate: datasource=node-version depName=node
25 # renovate: datasource=node-version depName=node
26 node-version: 24
26 node-version: 24
@@ -35,9 +35,9 @@ jobs:
35 if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
35 if: github.event_name == 'push' || github.event_name == 'workflow_dispatch'
36 runs-on: ubuntu-latest
36 runs-on: ubuntu-latest
37 steps:
37 steps:
38− - uses: actions/checkout@v6
38+ - uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
39
39
40− - uses: actions/setup-node@v6
40+ - uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
41 with:
41 with:
42 # renovate: datasource=node-version depName=node
42 # renovate: datasource=node-version depName=node
43 node-version: 24
43 node-version: 24
@@ -49,7 +49,7 @@ jobs:
49 npm run build
49 npm run build
50
50
51 - name: Upload artifact
51 - name: Upload artifact
52− uses: actions/upload-pages-artifact@v3
52+ uses: actions/upload-pages-artifact@fc324d3547104276b827a68afc52ff2a11cc49c9 # v5.0.0
53 with:
53 with:
54 path: ./docs/build
54 path: ./docs/build
55
55
@@ -65,4 +65,4 @@ jobs:
65 steps:
65 steps:
66 - name: Deploy to GitHub Pages
66 - name: Deploy to GitHub Pages
67 id: deployment
67 id: deployment
68− uses: actions/deploy-pages@v4
68+ uses: actions/deploy-pages@cd2ce8fcbc39b97be8ca5fce6e763baed58fa128 # v5.0.0
.github/workflows/labeler.yml+1−1
@@ -9,7 +9,7 @@ jobs:
9 pull-requests: write
9 pull-requests: write
10 runs-on: ubuntu-latest
10 runs-on: ubuntu-latest
11 steps:
11 steps:
12− - uses: actions/labeler@v6
12+ - uses: actions/labeler@bf12e9b00b37c5c0ca2b87b79b2daf7891dbda13 # v7.0.0
13 with:
13 with:
14 sync-labels: true
14 sync-labels: true
15 dot: true
15 dot: true
.github/workflows/nightly.yml+4−4
@@ -12,21 +12,21 @@ jobs:
12 if: github.repository == 'oauth2-proxy/oauth2-proxy'
12 if: github.repository == 'oauth2-proxy/oauth2-proxy'
13 steps:
13 steps:
14 - name: Check out code
14 - name: Check out code
15− uses: actions/checkout@v6
15+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
16 with:
16 with:
17 ref: master
17 ref: master
18 fetch-depth: 0
18 fetch-depth: 0
19 fetch-tags: true
19 fetch-tags: true
20
20
21 - name: Set up QEMU
21 - name: Set up QEMU
22− uses: docker/setup-qemu-action@v3
22+ uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
23
23
24 - name: Set up Docker Buildx
24 - name: Set up Docker Buildx
25 id: buildx
25 id: buildx
26− uses: docker/setup-buildx-action@v3
26+ uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
27
27
28 - name: Login to quay.io
28 - name: Login to quay.io
29− uses: docker/login-action@v3
29+ uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
30 with:
30 with:
31 registry: quay.io/oauth2-proxy
31 registry: quay.io/oauth2-proxy
32 username: ${{ secrets.REGISTRY_USERNAME_NIGHTLY }}
32 username: ${{ secrets.REGISTRY_USERNAME_NIGHTLY }}
.github/workflows/publish-release.yml+7−13
@@ -20,7 +20,7 @@ jobs:
20 tag: ${{ steps.tag.outputs.version }}
20 tag: ${{ steps.tag.outputs.version }}
21 steps:
21 steps:
22 - name: Check out code
22 - name: Check out code
23− uses: actions/checkout@v6
23+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
24 with:
24 with:
25 ref: ${{ github.event.pull_request.merge_commit_sha }}
25 ref: ${{ github.event.pull_request.merge_commit_sha }}
26 fetch-depth: 0
26 fetch-depth: 0
@@ -50,17 +50,11 @@ jobs:
50 id: go-version
50 id: go-version
51
51
52 - name: Set up Go
52 - name: Set up Go
53− uses: actions/setup-go@v6
53+ uses: actions/setup-go@b7ad1dad31e06c5925ef5d2fc7ad053ef454303e # v7.0.0
54 with:
54 with:
55 go-version: ${{ steps.go-version.outputs.version }}
55 go-version: ${{ steps.go-version.outputs.version }}
56 check-latest: true
56 check-latest: true
57
57
58− - name: Install golangci-lint
59− uses: golangci/golangci-lint-action@v9
60− with:
61− install-only: true
62− version: v2.11.4 # renovate: datasource=github-tags depName=golangci/golangci-lint
63−
64 - name: Get go dependencies
58 - name: Get go dependencies
65 run: |
59 run: |
66 go mod download
60 go mod download
@@ -70,7 +64,7 @@ jobs:
70
64
71 # Upload artifacts in case of workflow failure
65 # Upload artifacts in case of workflow failure
72 - name: Upload Artifacts
66 - name: Upload Artifacts
73− uses: actions/upload-artifact@v7
67+ uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
74 with:
68 with:
75 name: oauth2-proxy-artifacts
69 name: oauth2-proxy-artifacts
76 path: |
70 path: |
@@ -106,21 +100,21 @@ jobs:
106 runs-on: ubuntu-latest
100 runs-on: ubuntu-latest
107 steps:
101 steps:
108 - name: Check out code
102 - name: Check out code
109− uses: actions/checkout@v6
103+ uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
110 with:
104 with:
111 ref: ${{ needs.publish.outputs.tag }}
105 ref: ${{ needs.publish.outputs.tag }}
112 fetch-depth: 0
106 fetch-depth: 0
113 fetch-tags: true
107 fetch-tags: true
114
108
115 - name: Set up QEMU
109 - name: Set up QEMU
116− uses: docker/setup-qemu-action@v3
110+ uses: docker/setup-qemu-action@96fe6ef7f33517b61c61be40b68a1882f3264fb8 # v4.2.0
117
111
118 - name: Set up Docker Buildx
112 - name: Set up Docker Buildx
119 id: buildx
113 id: buildx
120− uses: docker/setup-buildx-action@v3
114+ uses: docker/setup-buildx-action@bb05f3f5519dd87d3ba754cc423b652a5edd6d2c # v4.2.0
121
115
122 - name: Login to quay.io
116 - name: Login to quay.io
123− uses: docker/login-action@v3
117+ uses: docker/login-action@dbcb813823bdd20940b903addbd779551569679f # v4.6.0
124 with:
118 with:
125 registry: quay.io/oauth2-proxy
119 registry: quay.io/oauth2-proxy
126 username: ${{ secrets.REGISTRY_USERNAME }}
120 username: ${{ secrets.REGISTRY_USERNAME }}
.github/workflows/stale.yml+1−1
@@ -10,7 +10,7 @@ jobs:
10 runs-on: ubuntu-latest
10 runs-on: ubuntu-latest
11
11
12 steps:
12 steps:
13− - uses: actions/stale@v10
13+ - uses: actions/stale@4391f3da665fdf50b6810c1a66712fb9ba21aa93 # v11.0.0
14 with:
14 with:
15 repo-token: ${{ secrets.GITHUB_TOKEN }}
15 repo-token: ${{ secrets.GITHUB_TOKEN }}
16 days-before-stale: 180
16 days-before-stale: 180
CHANGELOG.md+54
@@ -6,8 +6,62 @@
6
6
7## Breaking Changes
7## Breaking Changes
8
8
9+## Changes since v7.15.4
10+
11+# V7.15.4
12+
13+## Release Highlights
14+
15+- 🔵 Golang version upgrade to v1.26.7
16+ - Upgrade of all dependencies to their latest versions
17+- 🕵️‍♀️ Vulnerabilities have been addressed
18+ - [CVE-2026-33818](https://nvd.nist.gov/vuln/detail/CVE-2026-33818)
19+ - [CVE-2026-39821](https://nvd.nist.gov/vuln/detail/CVE-2026-39821)
20+ - [CVE-2026-39822](https://nvd.nist.gov/vuln/detail/CVE-2026-39822)
21+ - [CVE-2026-42505](https://nvd.nist.gov/vuln/detail/CVE-2026-42505)
22+ - [CVE-2026-46600](https://nvd.nist.gov/vuln/detail/CVE-2026-46600)
23+ - [CVE-2026-56852](https://nvd.nist.gov/vuln/detail/CVE-2026-56852)
24+ - [CVE-2026-56853](https://nvd.nist.gov/vuln/detail/CVE-2026-56853)
25+ - [CVE-2026-56858](https://nvd.nist.gov/vuln/detail/CVE-2026-56858)
26+ - [CVE-2026-56859](https://nvd.nist.gov/vuln/detail/CVE-2026-56859)
27+ - [CVE-2026-56860](https://nvd.nist.gov/vuln/detail/CVE-2026-56860)
28+ - [CVE-2026-56862](https://nvd.nist.gov/vuln/detail/CVE-2026-56862)
29+
30+## Important Notes
31+
32+## Breaking Changes
33+
34+## Changes since v7.15.3
35+
36+- [#3493](https://github.com/oauth2-proxy/oauth2-proxy/pull/3493) chore(deps): update gomod @tuunit
37+
38+# V7.15.3
39+
40+## Release Highlights
41+
42+- 🔵 Golang version upgrade to v1.26.4
43+ - Upgrade of all dependencies to their latest versions
44+- 🕵️‍♀️ Vulnerabilities have ben addressed
45+ - [CVE-2026-33811](https://nvd.nist.gov/vuln/detail/)
46+ - [CVE-2026-33814](https://nvd.nist.gov/vuln/detail/)
47+ - [CVE-2026-39820](https://nvd.nist.gov/vuln/detail/)
48+ - [CVE-2026-39836](https://nvd.nist.gov/vuln/detail/)
49+ - [CVE-2026-42499](https://nvd.nist.gov/vuln/detail/)
50+ - [CVE-2026-42504](https://nvd.nist.gov/vuln/detail/)
51+ - [CVE-2026-39823](https://nvd.nist.gov/vuln/detail/)
52+ - [CVE-2026-39826](https://nvd.nist.gov/vuln/detail/)
53+ - [CVE-2026-39825](https://nvd.nist.gov/vuln/detail/)
54+ - [CVE-2026-27145](https://nvd.nist.gov/vuln/detail/)
55+ - [CVE-2026-42507](https://nvd.nist.gov/vuln/detail/)
56+
57+## Important Notes
58+
59+## Breaking Changes
60+
9## Changes since v7.15.2
61## Changes since v7.15.2
10
62
63+- [#3477](https://github.com/oauth2-proxy/oauth2-proxy/pull/3477) chore(dep): bump go to 1.26 and migrate of reverse proxy handling
64+
11# V7.15.2
65# V7.15.2
12
66
13## Release Highlights
67## Release Highlights
MAINTAINERS.md+1−1
@@ -6,7 +6,7 @@ by our [project governance](GOVERNANCE.md).
6| Name | GitHub Handle | Domains of reponsibility | Email Alias | Affiliation |
6| Name | GitHub Handle | Domains of reponsibility | Email Alias | Affiliation |
7| ---------------- | ------------------------------------------------------ | ------------------------ | -------------------------- | ----------- |
7| ---------------- | ------------------------------------------------------ | ------------------------ | -------------------------- | ----------- |
8| Joel Speed | [@JoelSpeed](https://github.com/joelspeed) | Governance, Core | joel@oauth2-proxy.dev | Red Hat |
8| Joel Speed | [@JoelSpeed](https://github.com/joelspeed) | Governance, Core | joel@oauth2-proxy.dev | Red Hat |
9−| Jan Larwig | [@tuunit](https://github.com/tuunit) | Governance, Core | jan@oauth2-proxy.dev | IONOS Cloud |
9+| Jan Larwig | [@tuunit](https://github.com/tuunit) | Governance, Core | jan@oauth2-proxy.dev | STACKIT |
10| JJ Łakis | [@jjlakis](https://github.com/jjlakis) | Provider | jj@oauth2-proxy.dev | - |
10| JJ Łakis | [@jjlakis](https://github.com/jjlakis) | Provider | jj@oauth2-proxy.dev | - |
11| Koen van Zuijlen | [@kvanzuijlen](https://github.com/kvanzuijlen) | CI | koen@oauth2-proxy.dev | - |
11| Koen van Zuijlen | [@kvanzuijlen](https://github.com/kvanzuijlen) | CI | koen@oauth2-proxy.dev | - |
12| Pierluigi Lenoci | [@pierluigilenoci](https://github.com/pierluigilenoci) | Helm | pierluigi@oauth2-proxy.dev | SAP |
12| Pierluigi Lenoci | [@pierluigilenoci](https://github.com/pierluigilenoci) | Helm | pierluigi@oauth2-proxy.dev | SAP |
Makefile+9−4
@@ -25,7 +25,8 @@ help: ## Display this help
25
25
26
26
27GO ?= go
27GO ?= go
28−GOLANGCILINT ?= golangci-lint
28+# renovate: datasource=github-tags depName=golangci/golangci-lint
29+GOLANGCILINT_VERSION ?= v2.11.4
29
30
30BINARY := oauth2-proxy
31BINARY := oauth2-proxy
31VERSION ?= $(shell git describe --always --dirty --tags 2>/dev/null || echo "undefined")
32VERSION ?= $(shell git describe --always --dirty --tags 2>/dev/null || echo "undefined")
@@ -66,7 +67,7 @@ DOCKER_BUILDX_PUSH := $(DOCKER_BUILDX) --push
66DOCKER_BUILDX_PUSH_X_PLATFORM := $(DOCKER_BUILDX_PUSH) --platform ${DOCKER_BUILD_PLATFORM}
67DOCKER_BUILDX_PUSH_X_PLATFORM := $(DOCKER_BUILDX_PUSH) --platform ${DOCKER_BUILD_PLATFORM}
67
68
68DOCKER_BUILD_PLATFORM_ALPINE ?= linux/amd64,linux/arm64,linux/ppc64le,linux/arm/v6,linux/arm/v7,linux/s390x
69DOCKER_BUILD_PLATFORM_ALPINE ?= linux/amd64,linux/arm64,linux/ppc64le,linux/arm/v6,linux/arm/v7,linux/s390x
69−DOCKER_BUILD_RUNTIME_IMAGE_ALPINE ?= alpine:3.23.3
70+DOCKER_BUILD_RUNTIME_IMAGE_ALPINE ?= alpine:3.24.1
70DOCKER_BUILDX_ARGS_ALPINE ?= --build-arg RUNTIME_IMAGE=${DOCKER_BUILD_RUNTIME_IMAGE_ALPINE} ${DOCKER_BUILDX_COMMON_ARGS}
71DOCKER_BUILDX_ARGS_ALPINE ?= --build-arg RUNTIME_IMAGE=${DOCKER_BUILD_RUNTIME_IMAGE_ALPINE} ${DOCKER_BUILDX_COMMON_ARGS}
71DOCKER_BUILDX_X_PLATFORM_ALPINE := docker buildx build ${DOCKER_BUILDX_ARGS_ALPINE} --platform ${DOCKER_BUILD_PLATFORM_ALPINE}
72DOCKER_BUILDX_X_PLATFORM_ALPINE := docker buildx build ${DOCKER_BUILDX_ARGS_ALPINE} --platform ${DOCKER_BUILD_PLATFORM_ALPINE}
72DOCKER_BUILDX_PUSH_X_PLATFORM_ALPINE := $(DOCKER_BUILDX_X_PLATFORM_ALPINE) --push
73DOCKER_BUILDX_PUSH_X_PLATFORM_ALPINE := $(DOCKER_BUILDX_X_PLATFORM_ALPINE) --push
@@ -143,7 +144,7 @@ verify-generate: generate ## Verify command to check if alpha config docs are in
143##@ Miscellaneous
144##@ Miscellaneous
144
145
145.PHONY: test
146.PHONY: test
146−test: lint ## Run all Go tests
147+test: ## Run all Go tests
147 GO111MODULE=on $(GO) test $(TESTCOVER) -v -race ./...
148 GO111MODULE=on $(GO) test $(TESTCOVER) -v -race ./...
148
149
149.PHONY: release
150.PHONY: release
@@ -157,7 +158,11 @@ clean: ## Cleanup release and build files
157
158
158.PHONY: lint
159.PHONY: lint
159lint: validate-go-version ## Lint all files using golangci-lint
160lint: validate-go-version ## Lint all files using golangci-lint
160− GO111MODULE=on $(GOLANGCILINT) run
161+ GO111MODULE=on $(GO) run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCILINT_VERSION) run --timeout 10m
162+
163+.PHONY: lint-fix
164+lint-fix: validate-go-version ## Lint all files and apply fixes using golangci-lint
165+ GO111MODULE=on $(GO) run github.com/golangci/golangci-lint/v2/cmd/golangci-lint@$(GOLANGCILINT_VERSION) run --timeout 10m --fix
161
166
162.PHONY: validate-go-version
167.PHONY: validate-go-version
163validate-go-version: ## Validate Go environment requirements
168validate-go-version: ## Validate Go environment requirements
README.md+4−4
@@ -12,7 +12,7 @@
12
12
13OAuth2 Proxy is a flexible, open-source tool that can act as either a standalone reverse proxy or a middleware component integrated into existing reverse proxy or load balancer setups. It provides a simple and secure way to protect your web applications with OAuth2 / OIDC authentication. As a reverse proxy, it intercepts requests to your application and redirects users to an OAuth2 provider for authentication. As a middleware, it can be seamlessly integrated into your existing infrastructure to handle authentication for multiple applications.
13OAuth2 Proxy is a flexible, open-source tool that can act as either a standalone reverse proxy or a middleware component integrated into existing reverse proxy or load balancer setups. It provides a simple and secure way to protect your web applications with OAuth2 / OIDC authentication. As a reverse proxy, it intercepts requests to your application and redirects users to an OAuth2 provider for authentication. As a middleware, it can be seamlessly integrated into your existing infrastructure to handle authentication for multiple applications.
14
14
15−OAuth2 Proxy supports a lot of OAuth2 as well as OIDC providers. Either through a generic OIDC client or a specific implementation for Google, Microsoft Entra ID, GitHub, login.gov and others. Through specialised provider implementations oauth2-proxy can extract more details about the user like preferred usernames and groups. Those details can then be forwarded as HTTP headers to your upstream applications.
15+OAuth2 Proxy supports a lot of OAuth2 as well as OIDC providers. Either through a generic OIDC client or a specific implementation for Google, Microsoft Entra ID, GitHub, login.gov and others. Through specialised provider implementations OAuth2 Proxy can extract more details about the user like preferred usernames and groups. Those details can then be forwarded as HTTP headers to your upstream applications.
16
16
17![Simplified Architecture](docs/static/img/simplified-architecture.svg)
17![Simplified Architecture](docs/static/img/simplified-architecture.svg)
18
18
@@ -23,7 +23,7 @@ OAuth2 Proxy's [Installation Docs](https://oauth2-proxy.github.io/oauth2-proxy/i
23## Releases
23## Releases
24
24
25### Binaries
25### Binaries
26−We publish oauth2-proxy as compiled binaries on GitHub for all major architectures as well as more exotic ones like `ppc64le` as well as `s390x`.
26+We publish OAuth2 Proxy as compiled binaries on GitHub for all major architectures as well as more exotic ones like `ppc64le` as well as `s390x`.
27
27
28Check out the [latest release](https://github.com/oauth2-proxy/oauth2-proxy/releases/latest).
28Check out the [latest release](https://github.com/oauth2-proxy/oauth2-proxy/releases/latest).
29
29
@@ -49,9 +49,9 @@ SAP Open Source Program
49Microsoft Azure credits for open source projects
49Microsoft Azure credits for open source projects
50
50
51## Getting Involved
51## Getting Involved
52−[![Slack](https://img.shields.io/badge/slack-Gopher_%23oauth2--proxy-red?logo=slack)](https://gophers.slack.com/archives/CM2RSS25N)
52+[![Slack](https://img.shields.io/badge/slack-CNCF_%23oauth2--proxy-blue?logo=slack)](https://cloud-native.slack.com/archives/C098Y5URZ2N)
53
53
54−Join the #oauth2-proxy [Slack channel](https://gophers.slack.com/archives/CM2RSS25N) to chat with other users of oauth2-proxy or reach out to the maintainers directly. Use the [public invite link](https://invite.slack.golangbridge.org/) to get an invite for the Gopher Slack space.
54+Join the #oauth2-proxy [Slack channel](https://cloud-native.slack.com/archives/C098Y5URZ2N) to chat with other users of OAuth2 Proxy or reach out to the maintainers directly. Use the [public invite link](https://slack.cncf.io/) to get an invite for the CNCF space.
55
55
56OAuth2 Proxy is a community-driven project. We rely on the contribut️ions of our users to continually improve it. While review times can vary, we appreciate your patience and understanding. As a volunteer-driven project, we strive to keep this project stable and might take longer to merge changes.
56OAuth2 Proxy is a community-driven project. We rely on the contribut️ions of our users to continually improve it. While review times can vary, we appreciate your patience and understanding. As a volunteer-driven project, we strive to keep this project stable and might take longer to merge changes.
57
57
contrib/local-environment/Makefile+2−2
@@ -49,8 +49,8 @@ kubernetes-down:
49
49
50.PHONY: traefik-up
50.PHONY: traefik-up
51traefik-up:
51traefik-up:
52− docker compose -f docker-compose.yaml -f docker-compose-traefik.yaml up -d
52+ docker compose -f docker-compose-traefik.yaml up -d
53
53
54.PHONY: traefik-%
54.PHONY: traefik-%
55traefik-%:
55traefik-%:
56− docker compose -f docker-compose.yaml -f docker-compose-traefik.yaml $*
56+ docker compose -f docker-compose-traefik.yaml $*
contrib/local-environment/dex-traefik.yamlnew file+25
@@ -0,0 +1,25 @@
1+# This configuration is intended to be used with the Traefik local environment.
2+# Dex uses in-memory storage because this environment is disposable.
3+issuer: http://dex.localtest.me/dex
4+storage:
5+ type: memory
6+web:
7+ http: 0.0.0.0:5556
8+oauth2:
9+ skipApprovalScreen: true
10+expiry:
11+ signingKeys: "4h"
12+ idTokens: "1h"
13+staticClients:
14+ - id: oauth2-proxy
15+ redirectURIs:
16+ - "http://oauth2-proxy.localtest.me/oauth2/callback"
17+ name: "OAuth2 Proxy"
18+ secret: b2F1dGgyLXByb3h5LWNsaWVudC1zZWNyZXQK
19+enablePasswordDB: true
20+staticPasswords:
21+ - email: "admin@example.com"
22+ # bcrypt hash of the string "password"
23+ hash: "$2a$10$2b2cU8CPhOTaGrs1HRQuAueS7JTT5ZHsHSzYiFPm1leZck7Mc8T4W"
24+ username: "admin"
25+ userID: "08a8684b-db88-4b73-90a9-3cd1661f5466"
contrib/local-environment/dex.yaml+1−6
@@ -3,11 +3,7 @@
3# This should configure Dex to run on port 5556 and provides a static login
3# This should configure Dex to run on port 5556 and provides a static login
4issuer: http://dex.localtest.me:5556/dex
4issuer: http://dex.localtest.me:5556/dex
5storage:
5storage:
6− type: etcd
6+ type: memory
7− config:
8− endpoints:
9− - http://etcd:2379
10− namespace: dex/
11web:
7web:
12 http: 0.0.0.0:5556
8 http: 0.0.0.0:5556
13oauth2:
9oauth2:
@@ -21,7 +17,6 @@ staticClients:
21 # These redirect URIs point to the `--redirect-url` for OAuth2 proxy.
17 # These redirect URIs point to the `--redirect-url` for OAuth2 proxy.
22 - "http://oauth2-proxy.localtest.me:4180/oauth2/callback" # For basic proxy example.
18 - "http://oauth2-proxy.localtest.me:4180/oauth2/callback" # For basic proxy example.
23 - "http://oauth2-proxy.localtest.me:8080/oauth2/callback" # For nginx example.
19 - "http://oauth2-proxy.localtest.me:8080/oauth2/callback" # For nginx example.
24− - "http://oauth2-proxy.oauth2-proxy.localhost/oauth2/callback" # For traefik example.
25 name: "OAuth2 Proxy"
20 name: "OAuth2 Proxy"
26 secret: b2F1dGgyLXByb3h5LWNsaWVudC1zZWNyZXQK
21 secret: b2F1dGgyLXByb3h5LWNsaWVudC1zZWNyZXQK
27enablePasswordDB: true
22enablePasswordDB: true
contrib/local-environment/docker-compose-alpha-config.yaml+2−15
@@ -1,7 +1,7 @@
1# This docker-compose file can be used to bring up an example instance of oauth2-proxy
1# This docker-compose file can be used to bring up an example instance of oauth2-proxy
2# for manual testing and exploration of features.
2# for manual testing and exploration of features.
3# Alongside OAuth2-Proxy, this file also starts Dex to act as the identity provider,
3# Alongside OAuth2-Proxy, this file also starts Dex to act as the identity provider,
4−# etcd for storage for Dex and HTTPBin as an example upstream.
4+# HTTPBin as an example upstream.
5# This file also uses alpha configuration when configuring OAuth2 Proxy.
5# This file also uses alpha configuration when configuring OAuth2 Proxy.
6#
6#
7# This file is an extension of the main compose file and must be used with it
7# This file is an extension of the main compose file and must be used with it
@@ -14,7 +14,7 @@ version: "3.0"
14services:
14services:
15 oauth2-proxy:
15 oauth2-proxy:
16 container_name: oauth2-proxy
16 container_name: oauth2-proxy
17− image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.2
17+ image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.4
18 command: --config /oauth2-proxy.cfg --alpha-config /oauth2-proxy-alpha-config.yaml
18 command: --config /oauth2-proxy.cfg --alpha-config /oauth2-proxy-alpha-config.yaml
19 hostname: oauth2-proxy
19 hostname: oauth2-proxy
20 volumes:
20 volumes:
@@ -43,25 +43,12 @@ services:
43 dex:
43 dex:
44 aliases:
44 aliases:
45 - dex.localtest.me
45 - dex.localtest.me
46− etcd: {}
47− depends_on:
48− - etcd
49 httpbin:
46 httpbin:
50 container_name: httpbin
47 container_name: httpbin
51 image: kennethreitz/httpbin
48 image: kennethreitz/httpbin
52 ports: []
49 ports: []
53 networks:
50 networks:
54 httpbin: {}
51 httpbin: {}
55− etcd:
56− container_name: etcd
57− image: gcr.io/etcd-development/etcd:v3.6.8
58− entrypoint: /usr/local/bin/etcd
59− command:
60− - --listen-client-urls=http://0.0.0.0:2379
61− - --advertise-client-urls=http://etcd:2379
62− networks:
63− etcd: {}
64networks:
52networks:
65 dex: {}
53 dex: {}
66− etcd: {}
67 httpbin: {}
54 httpbin: {}
contrib/local-environment/docker-compose-gitea.yaml+2−2
@@ -14,7 +14,7 @@ version: '3.0'
14services:
14services:
15 oauth2-proxy:
15 oauth2-proxy:
16 container_name: oauth2-proxy
16 container_name: oauth2-proxy
17− image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.2
17+ image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.4
18 command: --config /oauth2-proxy.cfg
18 command: --config /oauth2-proxy.cfg
19 hostname: oauth2-proxy
19 hostname: oauth2-proxy
20 volumes:
20 volumes:
@@ -39,7 +39,7 @@ services:
39 httpbin: {}
39 httpbin: {}
40
40
41 gitea:
41 gitea:
42− image: gitea/gitea:1.25.5
42+ image: gitea/gitea:1.27.2
43 container_name: gitea
43 container_name: gitea
44 environment:
44 environment:
45 - USER_UID=1000
45 - USER_UID=1000
contrib/local-environment/docker-compose-keycloak.yaml+2−2
@@ -14,7 +14,7 @@ version: "3.0"
14services:
14services:
15 oauth2-proxy:
15 oauth2-proxy:
16 container_name: oauth2-proxy
16 container_name: oauth2-proxy
17− image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.2
17+ image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.4
18 command: --config /oauth2-proxy.cfg
18 command: --config /oauth2-proxy.cfg
19 hostname: oauth2-proxy
19 hostname: oauth2-proxy
20 volumes:
20 volumes:
@@ -40,7 +40,7 @@ services:
40
40
41 keycloak:
41 keycloak:
42 container_name: keycloak
42 container_name: keycloak
43− image: keycloak/keycloak:25.0
43+ image: keycloak/keycloak:26.7
44 hostname: keycloak
44 hostname: keycloak
45 command:
45 command:
46 - "start-dev"
46 - "start-dev"
contrib/local-environment/docker-compose-nginx.yaml+4−29
@@ -1,28 +1,18 @@
1# This docker-compose file can be used to bring up an example instance of oauth2-proxy
1# This docker-compose file can be used to bring up an example instance of oauth2-proxy
2# for manual testing and exploration of features.
2# for manual testing and exploration of features.
3# Alongside OAuth2-Proxy, this file also starts Dex to act as the identity provider,
3# Alongside OAuth2-Proxy, this file also starts Dex to act as the identity provider,
4−# etcd for storage for Dex, nginx as a reverse proxy and other http services for upstreams
4+# nginx as a reverse proxy and other HTTP services for upstreams.
5#
5#
6# This file is an extension of the main compose file and must be used with it
6# This file is an extension of the main compose file and must be used with it
7# docker-compose -f docker-compose.yaml -f docker-compose-nginx.yaml <command>
7# docker-compose -f docker-compose.yaml -f docker-compose-nginx.yaml <command>
8# Alternatively:
8# Alternatively:
9# make nginx-<command> (eg make nginx-up, make nginx-down)
9# make nginx-<command> (eg make nginx-up, make nginx-down)
10#
10#
11−# Access one of the following URLs to initiate a login flow:
11+# Access http://oauth2-proxy.localtest.me:8080 to initiate a login flow.
12−# - http://oauth2-proxy.localhost
13−# - http://httpbin.oauth2-proxy.localhost
14−#
15−# The OAuth2 Proxy itself is hosted at http://oauth2-proxy.oauth2-proxy.localhost
16−#
17−# Note, the above URLs should work with Chrome, but you may need to add hosts
18−# entries for other browsers
19−# 127.0.0.1 oauth2-proxy.localhost
20−# 127.0.0.1 httpbin.oauth2-proxy.localhost
21−# 127.0.0.1 oauth2-proxy.oauth2-proxy.localhost
22version: "3.0"
12version: "3.0"
23services:
13services:
24 oauth2-proxy:
14 oauth2-proxy:
25− image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.2
15+ image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.4
26 ports: []
16 ports: []
27 hostname: oauth2-proxy
17 hostname: oauth2-proxy
28 container_name: oauth2-proxy
18 container_name: oauth2-proxy
@@ -33,7 +23,6 @@ services:
33 networks:
23 networks:
34 oauth2-proxy: {}
24 oauth2-proxy: {}
35 dex: {}
25 dex: {}
36− etcd: {}
37 httpbin: {}
26 httpbin: {}
38 depends_on:
27 depends_on:
39 - dex
28 - dex
@@ -42,7 +31,7 @@ services:
42 depends_on:
31 depends_on:
43 - oauth2-proxy
32 - oauth2-proxy
44 container_name: nginx
33 container_name: nginx
45− image: nginx:1.29
34+ image: nginx:1.31
46 restart: unless-stopped
35 restart: unless-stopped
47 ports:
36 ports:
48 - 8080:8080/tcp
37 - 8080:8080/tcp
@@ -52,7 +41,6 @@ services:
52 networks:
41 networks:
53 oauth2-proxy: {}
42 oauth2-proxy: {}
54 dex: {}
43 dex: {}
55− etcd: {}
56 httpbin: {}
44 httpbin: {}
57 dex:
45 dex:
58 container_name: dex
46 container_name: dex
@@ -68,26 +56,13 @@ services:
68 dex:
56 dex:
69 aliases:
57 aliases:
70 - dex.localtest.me
58 - dex.localtest.me
71− etcd: {}
72− depends_on:
73− - etcd
74 httpbin:
59 httpbin:
75 container_name: httpbin
60 container_name: httpbin
76 image: kennethreitz/httpbin
61 image: kennethreitz/httpbin
77 ports: []
62 ports: []
78 networks:
63 networks:
79 httpbin: {}
64 httpbin: {}
80− etcd:
81− container_name: etcd
82− image: gcr.io/etcd-development/etcd:v3.6.8
83− entrypoint: /usr/local/bin/etcd
84− command:
85− - --listen-client-urls=http://0.0.0.0:2379
86− - --advertise-client-urls=http://etcd:2379
87− networks:
88− etcd: {}
89networks:
65networks:
90 dex: {}
66 dex: {}
91− etcd: {}
92 httpbin: {}
67 httpbin: {}
93 oauth2-proxy: {}
68 oauth2-proxy: {}
contrib/local-environment/docker-compose-traefik.yaml+42−18
@@ -1,7 +1,7 @@
1# This docker-compose file can be used to bring up an example instance of oauth2-proxy
1# This docker-compose file can be used to bring up an example instance of oauth2-proxy
2# for manual testing and exploration of features.
2# for manual testing and exploration of features.
3−# Alongside OAuth2-Proxy, this file also starts Dex to act as the identity provider,
3+# Alongside OAuth2-Proxy, this file also starts Dex to act as the identity provider
4−# HTTPBin as an example upstream.
4+# and HTTPBin as an example upstream.
5#
5#
6# This can either be created using docker-compose
6# This can either be created using docker-compose
7# docker-compose -f docker-compose-traefik.yaml <command>
7# docker-compose -f docker-compose-traefik.yaml <command>
@@ -9,32 +9,51 @@
9# make traefik-<command> (eg. make traefik-up, make traefik-down)
9# make traefik-<command> (eg. make traefik-up, make traefik-down)
10#
10#
11# Access one of the following URLs to initiate a login flow:
11# Access one of the following URLs to initiate a login flow:
12−# - http://oauth2-proxy.localhost
12+# - http://oauth2-proxy.localtest.me
13−# - http://httpbin.oauth2-proxy.localhost
13+# - http://httpbin.localtest.me
14+# - http://dex.localtest.me/dex
14#
15#
15−# The OAuth2 Proxy itself is hosted at http://oauth2-proxy.oauth2-proxy.localhost
16+# The OAuth2 Proxy itself is hosted at http://oauth2-proxy.localtest.me
16−#
17−# Note, the above URLs should work with Chrome, but you may need to add hosts
18−# entries for other browsers
19−# 127.0.0.1 oauth2-proxy.localhost
20−# 127.0.0.1 httpbin.oauth2-proxy.localhost
21−# 127.0.0.1 oauth2-proxy.oauth2-proxy.localhost
22−version: '3.0'
23services:
17services:
24
18
25 oauth2-proxy:
19 oauth2-proxy:
20+ container_name: oauth2-proxy
26− image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.2
21+ image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.4
22+ command: --config /oauth2-proxy.cfg
27 ports: []
23 ports: []
28 hostname: oauth2-proxy
24 hostname: oauth2-proxy
29 volumes:
25 volumes:
30 - "./oauth2-proxy-traefik.cfg:/oauth2-proxy.cfg"
26 - "./oauth2-proxy-traefik.cfg:/oauth2-proxy.cfg"
27+ restart: unless-stopped
28+ networks:
29+ oauth2-proxy: {}
30+ dex: {}
31+ depends_on:
32+ - dex
33+ - httpbin
34+
35+ dex:
36+ container_name: dex
37+ image: ghcr.io/dexidp/dex:v2.45.1
38+ command: dex serve /dex.yaml
39+ hostname: dex
40+ volumes:
41+ - "./dex-traefik.yaml:/dex.yaml"
42+ restart: unless-stopped
31 networks:
43 networks:
32− oauth2-proxy:
44+ dex: {}
45+
46+ httpbin:
47+ container_name: httpbin
48+ image: kennethreitz/httpbin
49+ ports: []
50+ networks:
51+ httpbin: {}
33
52
34 # Reverse proxy
53 # Reverse proxy
35 gateway:
54 gateway:
36 container_name: traefik
55 container_name: traefik
37− image: traefik:v2.11.40
56+ image: traefik:v3.7.10
38 volumes:
57 volumes:
39 - "./traefik:/etc/traefik"
58 - "./traefik:/etc/traefik"
40 ports:
59 ports:
@@ -43,8 +62,13 @@ services:
43 depends_on:
62 depends_on:
44 - oauth2-proxy
63 - oauth2-proxy
45 networks:
64 networks:
46− oauth2-proxy:
65+ oauth2-proxy: {}
47− httpbin:
66+ httpbin: {}
67+ dex:
68+ aliases:
69+ - dex.localtest.me
48
70
49networks:
71networks:
72+ dex: {}
73+ httpbin: {}
50− oauth2-proxy:
74+ oauth2-proxy: {}
contrib/local-environment/docker-compose.yaml+2−15
@@ -1,7 +1,7 @@
1# This docker-compose file can be used to bring up an example instance of oauth2-proxy
1# This docker-compose file can be used to bring up an example instance of oauth2-proxy
2# for manual testing and exploration of features.
2# for manual testing and exploration of features.
3# Alongside OAuth2-Proxy, this file also starts Dex to act as the identity provider,
3# Alongside OAuth2-Proxy, this file also starts Dex to act as the identity provider,
4−# etcd for storage for Dex and HTTPBin as an example upstream.
4+# HTTPBin as an example upstream.
5#
5#
6# This can either be created using docker-compose
6# This can either be created using docker-compose
7# docker-compose -f docker-compose.yaml <command>
7# docker-compose -f docker-compose.yaml <command>
@@ -13,7 +13,7 @@ version: "3.0"
13services:
13services:
14 oauth2-proxy:
14 oauth2-proxy:
15 container_name: oauth2-proxy
15 container_name: oauth2-proxy
16− image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.2
16+ image: quay.io/oauth2-proxy/oauth2-proxy:v7.15.4
17 command: --config /oauth2-proxy.cfg
17 command: --config /oauth2-proxy.cfg
18 hostname: oauth2-proxy
18 hostname: oauth2-proxy
19 volumes:
19 volumes:
@@ -41,25 +41,12 @@ services:
41 dex:
41 dex:
42 aliases:
42 aliases:
43 - dex.localtest.me
43 - dex.localtest.me
44− etcd: {}
45− depends_on:
46− - etcd
47 httpbin:
44 httpbin:
48 container_name: httpbin
45 container_name: httpbin
49 image: kennethreitz/httpbin
46 image: kennethreitz/httpbin
50 ports: []
47 ports: []
51 networks:
48 networks:
52 httpbin: {}
49 httpbin: {}
53− etcd:
54− container_name: etcd
55− image: gcr.io/etcd-development/etcd:v3.6.8
56− entrypoint: /usr/local/bin/etcd
57− command:
58− - --listen-client-urls=http://0.0.0.0:2379
59− - --advertise-client-urls=http://etcd:2379
60− networks:
61− etcd: {}
62networks:
50networks:
63 dex: {}
51 dex: {}
64− etcd: {}
65 httpbin: {}
52 httpbin: {}
contrib/local-environment/kubernetes/Chart.lock+3−3
@@ -1,7 +1,7 @@
1dependencies:
1dependencies:
2- name: dex
2- name: dex
3 repository: https://charts.dexidp.io
3 repository: https://charts.dexidp.io
4− version: 0.24.0
4+ version: 0.24.1
5- name: oauth2-proxy
5- name: oauth2-proxy
6 repository: https://oauth2-proxy.github.io/manifests
6 repository: https://oauth2-proxy.github.io/manifests
7 version: 7.18.0
7 version: 7.18.0
@@ -11,5 +11,5 @@ dependencies:
11- name: hello-world
11- name: hello-world
12 repository: https://conservis.github.io/helm-charts
12 repository: https://conservis.github.io/helm-charts
13 version: 1.1.0
13 version: 1.1.0
14−digest: sha256:fee913531bfb67e5555e995d8fb040c330e6eb4b5a8c777ceb9841135ea9bb84
14+digest: sha256:3fff8bf99cebcd20a73c984423d5f1c2dfa1e5a394f82a5dc60d9aaffe5361b7
15−generated: "2025-10-28T06:46:35.38300324Z"
15+generated: "2026-08-18T07:33:03.791274914Z"
contrib/local-environment/kubernetes/Chart.yaml+1−1
@@ -3,7 +3,7 @@ description: K8S example based on https://kind.sigs.k8s.io
3name: kubernetes
3name: kubernetes
4dependencies:
4dependencies:
5 - name: dex
5 - name: dex
6− version: 0.24.0
6+ version: 0.24.1
7 repository: https://charts.dexidp.io
7 repository: https://charts.dexidp.io
8 - name: oauth2-proxy
8 - name: oauth2-proxy
9 version: &chartVersion 7.18.0
9 version: &chartVersion 7.18.0
contrib/local-environment/oauth2-proxy-traefik.cfg+4−4
@@ -2,14 +2,14 @@ http_address="0.0.0.0:4180"
2cookie_secret="OQINaROshtE9TcZkNAm-5Zs2Pv3xaWytBmc5W7sPX7w="
2cookie_secret="OQINaROshtE9TcZkNAm-5Zs2Pv3xaWytBmc5W7sPX7w="
3provider="oidc"
3provider="oidc"
4email_domains=["example.com"]
4email_domains=["example.com"]
5−oidc_issuer_url="http://dex.localhost:5556/dex"
5+oidc_issuer_url="http://dex.localtest.me/dex"
6client_secret="b2F1dGgyLXByb3h5LWNsaWVudC1zZWNyZXQK"
6client_secret="b2F1dGgyLXByb3h5LWNsaWVudC1zZWNyZXQK"
7client_id="oauth2-proxy"
7client_id="oauth2-proxy"
8cookie_secure="false"
8cookie_secure="false"
9
9
10−redirect_url="http://oauth2-proxy.oauth2-proxy.localhost/oauth2/callback"
10+redirect_url="http://oauth2-proxy.localtest.me/oauth2/callback"
11−cookie_domains=".oauth2-proxy.localhost" # Required so cookie can be read on all subdomains.
11+cookie_domains=".localtest.me" # Required so cookie can be read on all subdomains.
12−whitelist_domains=".oauth2-proxy.localhost" # Required to allow redirection back to original requested target.
12+whitelist_domains=".localtest.me" # Required to allow redirection back to original requested target.
13
13
14# Mandatory option when using oauth2-proxy with traefik
14# Mandatory option when using oauth2-proxy with traefik
15reverse_proxy="true"
15reverse_proxy="true"
contrib/local-environment/traefik/dynamic.yaml+11−4
@@ -1,22 +1,25 @@
1http:
1http:
2 routers:
2 routers:
3 oauth2-proxy-route:
3 oauth2-proxy-route:
4− rule: "Host(`oauth2-proxy.oauth2-proxy.localhost`)"
4+ rule: "Host(`oauth2-proxy.localtest.me`)"
5 middlewares:
5 middlewares:
6 - auth-headers
6 - auth-headers
7 service: oauth-backend
7 service: oauth-backend
8+ dex-route:
9+ rule: "Host(`dex.localtest.me`)"
10+ service: dex-service
8 httpbin-route:
11 httpbin-route:
9− rule: "Host(`httpbin.oauth2-proxy.localhost`)"
12+ rule: "Host(`httpbin.localtest.me`)"
10 service: httpbin-service
13 service: httpbin-service
11 middlewares:
14 middlewares:
12 - oauth-auth-redirect # redirects all unauthenticated to oauth2 signin
15 - oauth-auth-redirect # redirects all unauthenticated to oauth2 signin
13 httpbin-route-2:
16 httpbin-route-2:
14− rule: "Host(`httpbin.oauth2-proxy.localhost`) && PathPrefix(`/no-auto-redirect`)"
17+ rule: "Host(`httpbin.localtest.me`) && PathPrefix(`/no-auto-redirect`)"
15 service: httpbin-service
18 service: httpbin-service
16 middlewares:
19 middlewares:
17 - oauth-auth-wo-redirect # unauthenticated session will return a 401
20 - oauth-auth-wo-redirect # unauthenticated session will return a 401
18 services-oauth2-route:
21 services-oauth2-route:
19− rule: "Host(`httpbin.oauth2-proxy.localhost`) && PathPrefix(`/oauth2/`)"
22+ rule: "Host(`httpbin.localtest.me`) && PathPrefix(`/oauth2/`)"
20 middlewares:
23 middlewares:
21 - auth-headers
24 - auth-headers
22 service: oauth-backend
25 service: oauth-backend
@@ -30,6 +33,10 @@ http:
30 loadBalancer:
33 loadBalancer:
31 servers:
34 servers:
32 - url: http://oauth2-proxy:4180
35 - url: http://oauth2-proxy:4180
36+ dex-service:
37+ loadBalancer:
38+ servers:
39+ - url: http://dex:5556
33
40
34 middlewares:
41 middlewares:
35 auth-headers:
42 auth-headers:
contrib/local-environment/traefik/traefik.yaml+2
@@ -5,3 +5,5 @@ log:
5providers:
5providers:
6 file:
6 file:
7 filename: /etc/traefik/dynamic.yaml
7 filename: /etc/traefik/dynamic.yaml
8+ # Routes are static for this example; disabling watch avoids host inotify limits.
9+ watch: false
docs/docs/configuration/alpha_config.md+12−1
@@ -167,6 +167,17 @@ injectResponseHeaders:
167* `claimSource` - `claim` (session claims either from id token or from profile URL)
167* `claimSource` - `claim` (session claims either from id token or from profile URL)
168* `secretSource` - `value` (base64), `fromFile` (file path)
168* `secretSource` - `value` (base64), `fromFile` (file path)
169
169
170+#### Claim value serialization
171+
172+`claimSource` converts the selected claim into one or more string header values. Scalars
173+produce one value, while arrays produce one value per element. Values that cannot be
174+converted directly to strings, such as objects, are JSON-encoded individually.
175+
176+Each non-empty value is added separately under the configured header name. An array of
177+objects therefore produces multiple header values containing one JSON object each, not a
178+single JSON array. HTTP libraries and intermediaries may expose repeated values as a
179+comma-joined string, so consumers should retrieve and parse each header value separately.
180+
170**Request option:** `preserveRequestValue: true` retains existing header values
181**Request option:** `preserveRequestValue: true` retains existing header values
171
182
172**Incompatibility:** Remove legacy flags `pass-user-headers`, `set-xauthrequest`
183**Incompatibility:** Remove legacy flags `pass-user-headers`, `set-xauthrequest`
@@ -678,7 +689,7 @@ Requests will be proxied to this upstream if the path matches the request path.
678| Field | Type | Description |
689| Field | Type | Description |
679| ----- | ---- | ----------- |
690| ----- | ---- | ----------- |
680| `id` | _string_ | ID should be a unique identifier for the upstream.<br/>This value is required for all upstreams. |
691| `id` | _string_ | ID should be a unique identifier for the upstream.<br/>This value is required for all upstreams. |
681−| `path` | _string_ | Path is used to map requests to the upstream server.<br/>The closest match will take precedence and all Paths must be unique.<br/>Path can also take a pattern when used with RewriteTarget.<br/>Path segments can be captured and matched using regular experessions.<br/>Eg:<br/>- `^/foo$`: Match only the explicit path `/foo`<br/>- `^/bar/$`: Match any path prefixed with `/bar/`<br/>- `^/baz/(.*)$`: Match any path prefixed with `/baz` and capture the remaining path for use with RewriteTarget |
692+| `path` | _string_ | Path is used to map requests to the upstream server.<br/>The closest match will take precedence and all Paths must be unique.<br/>Without RewriteTarget, Path follows gorilla/mux route syntax.<br/>Eg:<br/>- `/foo`: Match only the explicit path `/foo`<br/>- `/bar/`: Match any path prefixed with `/bar/`<br/>- `/baz/{path:.*}`: Match any path prefixed with `/baz/`<br/>When RewriteTarget is set, Path is treated as a regular expression.<br/>Eg: `^/baz/(.*)$` matches any path prefixed with `/baz` and captures the<br/>remaining path for use with RewriteTarget. |
682| `rewriteTarget` | _string_ | RewriteTarget allows users to rewrite the request path before it is sent to<br/>the upstream server (for an HTTP/HTTPS upstream) or mapped to the filesystem<br/>(for a `file:` upstream).<br/>Use the Path to capture segments for reuse within the rewrite target.<br/>Eg: With a Path of `^/baz/(.*)`, a RewriteTarget of `/foo/$1` would rewrite<br/>the request `/baz/abc/123` to `/foo/abc/123` before proxying to the<br/>upstream server. Or if the upstream were `file:///app`, a request for<br/>`/baz/info.html` would return the contents of the file `/app/foo/info.html`. |
693| `rewriteTarget` | _string_ | RewriteTarget allows users to rewrite the request path before it is sent to<br/>the upstream server (for an HTTP/HTTPS upstream) or mapped to the filesystem<br/>(for a `file:` upstream).<br/>Use the Path to capture segments for reuse within the rewrite target.<br/>Eg: With a Path of `^/baz/(.*)`, a RewriteTarget of `/foo/$1` would rewrite<br/>the request `/baz/abc/123` to `/foo/abc/123` before proxying to the<br/>upstream server. Or if the upstream were `file:///app`, a request for<br/>`/baz/info.html` would return the contents of the file `/app/foo/info.html`. |
683| `uri` | _string_ | The URI of the upstream server. This may be an HTTP(S) server of a File<br/>based URL. It may include a path, in which case all requests will be served<br/>under that path.<br/>Eg:<br/>- http://localhost:8080<br/>- https://service.localhost<br/>- https://service.localhost/path<br/>- file://host/path<br/>If the URI's path is "/base" and the incoming request was for "/dir",<br/>the upstream request will be for "/base/dir". |
694| `uri` | _string_ | The URI of the upstream server. This may be an HTTP(S) server of a File<br/>based URL. It may include a path, in which case all requests will be served<br/>under that path.<br/>Eg:<br/>- http://localhost:8080<br/>- https://service.localhost<br/>- https://service.localhost/path<br/>- file://host/path<br/>If the URI's path is "/base" and the incoming request was for "/dir",<br/>the upstream request will be for "/base/dir". |
684| `insecureSkipTLSVerify` | _bool_ | InsecureSkipTLSVerify will skip TLS verification of upstream HTTPS hosts.<br/>This option is insecure and will allow potential Man-In-The-Middle attacks<br/>between OAuth2 Proxy and the upstream server.<br/>Defaults to false. |
695| `insecureSkipTLSVerify` | _bool_ | InsecureSkipTLSVerify will skip TLS verification of upstream HTTPS hosts.<br/>This option is insecure and will allow potential Man-In-The-Middle attacks<br/>between OAuth2 Proxy and the upstream server.<br/>Defaults to false. |
docs/docs/configuration/alpha_config.md.tmpl+11
@@ -167,6 +167,17 @@ injectResponseHeaders:
167* `claimSource` - `claim` (session claims either from id token or from profile URL)
167* `claimSource` - `claim` (session claims either from id token or from profile URL)
168* `secretSource` - `value` (base64), `fromFile` (file path)
168* `secretSource` - `value` (base64), `fromFile` (file path)
169
169
170+#### Claim value serialization
171+
172+`claimSource` converts the selected claim into one or more string header values. Scalars
173+produce one value, while arrays produce one value per element. Values that cannot be
174+converted directly to strings, such as objects, are JSON-encoded individually.
175+
176+Each non-empty value is added separately under the configured header name. An array of
177+objects therefore produces multiple header values containing one JSON object each, not a
178+single JSON array. HTTP libraries and intermediaries may expose repeated values as a
179+comma-joined string, so consumers should retrieve and parse each header value separately.
180+
170**Request option:** `preserveRequestValue: true` retains existing header values
181**Request option:** `preserveRequestValue: true` retains existing header values
171
182
172**Incompatibility:** Remove legacy flags `pass-user-headers`, `set-xauthrequest`
183**Incompatibility:** Remove legacy flags `pass-user-headers`, `set-xauthrequest`
docs/docs/configuration/integrations/nginx.md+27−11
@@ -60,23 +60,23 @@ server {
60 auth_request_set $auth_cookie $upstream_http_set_cookie;
60 auth_request_set $auth_cookie $upstream_http_set_cookie;
61 add_header Set-Cookie $auth_cookie;
61 add_header Set-Cookie $auth_cookie;
62
62
63− # When using the --set-authorization-header flag, some provider's cookies can exceed the 4kb
63+ # Large OIDC tokens can make the session cookie exceed the 4KB limit,
64− # limit and so the OAuth2 Proxy splits these into multiple parts.
64+ # causing OAuth2 Proxy to split the cookie into multiple parts.
65 # Nginx normally only copies the first `Set-Cookie` header from the auth_request to the response,
65 # Nginx normally only copies the first `Set-Cookie` header from the auth_request to the response,
66− # so if your cookies are larger than 4kb, you will need to extract additional cookies manually.
66+ # so extract the second part when using the default cookie name, `_oauth2_proxy`.
67− auth_request_set $auth_cookie_name_upstream_1 $upstream_cookie_auth_cookie_name_1;
67+ auth_request_set $auth_cookie_upstream_1 $upstream_cookie__oauth2_proxy_1;
68
68
69 # Extract the Cookie attributes from the first Set-Cookie header and append them
69 # Extract the Cookie attributes from the first Set-Cookie header and append them
70 # to the second part ($upstream_cookie_* variables only contain the raw cookie content)
70 # to the second part ($upstream_cookie_* variables only contain the raw cookie content)
71 if ($auth_cookie ~* "(; .*)") {
71 if ($auth_cookie ~* "(; .*)") {
72− set $auth_cookie_name_0 $auth_cookie;
72+ set $auth_cookie_0 $auth_cookie;
73− set $auth_cookie_name_1 "auth_cookie_name_1=$auth_cookie_name_upstream_1$1";
73+ set $auth_cookie_1 "_oauth2_proxy_1=$auth_cookie_upstream_1$1";
74 }
74 }
75
75
76 # Send both Set-Cookie headers now if there was a second part
76 # Send both Set-Cookie headers now if there was a second part
77− if ($auth_cookie_name_upstream_1) {
77+ if ($auth_cookie_upstream_1) {
78− add_header Set-Cookie $auth_cookie_name_0;
78+ add_header Set-Cookie $auth_cookie_0;
79− add_header Set-Cookie $auth_cookie_name_1;
79+ add_header Set-Cookie $auth_cookie_1;
80 }
80 }
81
81
82 proxy_pass http://backend/;
82 proxy_pass http://backend/;
@@ -91,6 +91,24 @@ server {
91}
91}
92```
92```
93
93
94+### Multi-part session cookies
95+
96+The example above is ready to use with the default `--cookie-name=_oauth2_proxy` setting and handles a session split into two cookies. If you configure a custom cookie name, update both references to the second cookie part:
97+
98+| Cookie configuration | Nginx upstream cookie variable | `Set-Cookie` name |
99+| --- | --- | --- |
100+| Default (`_oauth2_proxy`) | `$upstream_cookie__oauth2_proxy_1` | `_oauth2_proxy_1=` |
101+| `--cookie-name=my_cookie` | `$upstream_cookie_my_cookie_1` | `my_cookie_1=` |
102+
103+For example, with `--cookie-name=my_cookie`, change the relevant lines to:
104+
105+```nginx
106+auth_request_set $auth_cookie_upstream_1 $upstream_cookie_my_cookie_1;
107+set $auth_cookie_1 "my_cookie_1=$auth_cookie_upstream_1$1";
108+```
109+
110+If a session is split into more than two cookies, repeat the extraction for each additional numeric suffix. For consistently large sessions, prefer `--session-store-type=redis` so only a small session ticket is stored in the browser cookie.
111+
94### Understanding the `error_page` redirect pattern
112### Understanding the `error_page` redirect pattern
95
113
96The `auth_request` directive expects the authentication endpoint (`/oauth2/auth`) to return:
114The `auth_request` directive expects the authentication endpoint (`/oauth2/auth`) to return:
@@ -167,8 +185,6 @@ It is recommended to use `--session-store-type=redis` when expecting large sessi
167For a complete example of integrating oauth2-proxy with Kubernetes Dashboard on AKS using Azure Entra ID, including RBAC configuration and troubleshooting, see the [Kubernetes Dashboard on AKS](../providers/ms_entra_id.md#kubernetes-dashboard-on-aks) section in the Microsoft Entra ID provider documentation.
185For a complete example of integrating oauth2-proxy with Kubernetes Dashboard on AKS using Azure Entra ID, including RBAC configuration and troubleshooting, see the [Kubernetes Dashboard on AKS](../providers/ms_entra_id.md#kubernetes-dashboard-on-aks) section in the Microsoft Entra ID provider documentation.
168:::
186:::
169
187
170−You have to substitute *name* with the actual cookie name you configured via --cookie-name parameter. If you don't set a custom cookie name the variable should be "$upstream_cookie__oauth2_proxy_1" instead of "$upstream_cookie_name_1" and the new cookie-name should be "_oauth2_proxy_1=" instead of "name_1=".
171−
172:::note
188:::note
173If you set up your OAuth2 provider to rotate your client secret, you can use the `client-secret-file` option to reload the secret when it is updated.
189If you set up your OAuth2 provider to rotate your client secret, you can use the `client-secret-file` option to reload the secret when it is updated.
174:::
190:::
docs/docs/configuration/providers/atlassian.mdnew file+26
@@ -0,0 +1,26 @@
1+---
2+id: atlassian
3+title: Atlassian Cloud
4+---
5+
6+1. Create an OAuth 2.0 integration in the [Atlassian developer console](https://developer.atlassian.com/console/myapps/).
7+2. Under **Permissions**, add the User identity API with the `read:me` scope.
8+3. Under **Authorization**, set the callback URL to `https://<proxy_host>/oauth2/callback`.
9+4. Copy the client ID and secret from **Settings**.
10+5. Run the proxy with:
11+
12+```shell
13+ --provider=atlassian
14+ --client-id=<client ID>
15+ --client-secret=<secret>
16+ --redirect-url=https://<proxy_host>/oauth2/callback
17+ --cookie-secret=<cookie secret>
18+```
19+
20+A new app works only for the account that created it. Enable sharing under **Distribution** to let other users sign in.
21+Atlassian then admits only accounts with access to the Atlassian site the app is installed on; others are refused at
22+the consent screen.
23+
24+The proxy reads the user's email from `https://api.atlassian.com/me`. Restricting access further is supported by
25+[email](index.md#email-authentication). With `--cookie-refresh` set, the proxy re-validates the token against `/me` on
26+that interval.
docs/docs/configuration/providers/dtt_sso.mdnew file+45
@@ -0,0 +1,45 @@
1+---
2+id: dtt_sso
3+title: DTT SSO
4+---
5+
6+Signs users in with the DTT SSO (auth-server) using the authorization code flow and its HS256 id_token.
7+
8+1. Register a client in the auth-server config, one per protected service, with the proxy's callback as its
9+ only redirect URI:
10+
11+ ```yaml
12+ clients:
13+ redash-gate:
14+ secret: <random secret>
15+ redirect_uri: https://redash.example.com/oauth2/callback
16+ display_name: Redash
17+ ```
18+
19+2. Run the proxy with:
20+
21+ ```shell
22+ --provider=dtt-sso
23+ --client-id=redash-gate
24+ --client-secret-file=/run/secrets/client_secret
25+ --redirect-url=https://redash.example.com/oauth2/callback
26+ --cookie-secret-file=/run/secrets/cookie_secret
27+ --cookie-name=_dtt_sso_redash
28+ --cookie-refresh=5m
29+ ```
30+
31+ Give each instance its own `--cookie-name`: another oauth2-proxy setting its cookie on a parent domain
32+ (the Atlassian proxy uses `_oauth2_proxy` on `.thebestagent.pro`) would otherwise shadow this one.
33+
34+The endpoints default to `https://auth.thebestagent.pro`. For another SSO host set `--login-url`, `--redeem-url`
35+and `--validate-url` to its `/oauth/authorize`, `/oauth/token` and `/oauth/check_token`; the id_token issuer must
36+equal the login URL's origin (the SSO's `site_host`).
37+
38+The scope must stay `openid`, the only scope the SSO accepts.
39+
40+The session carries the SSO `email`, the user UUID as the user, and the SSO `role` as the only group, so
41+`--allowed-group=dtt_admin` restricts access by role. `--authenticated-emails-file` and `--email-domain` restrict
42+by email as usual.
43+
44+With `--cookie-refresh` set, the proxy refreshes the tokens on that interval and re-reads the email and role; a user
45+deleted or signed out in the SSO loses access at the next refresh.
docs/docs/configuration/providers/index.md+2
@@ -9,10 +9,12 @@ with Redirect URI(s) for the domain you intend to run `oauth2-proxy` on.
9Valid providers are :
9Valid providers are :
10
10
11- [ADFS](adfs.md)
11- [ADFS](adfs.md)
12+- [Atlassian Cloud](atlassian.md)
12- [Bitbucket](bitbucket.md)
13- [Bitbucket](bitbucket.md)
13- [Cidaas](cidaas.md)
14- [Cidaas](cidaas.md)
14- [CiscoDuo](cisco_duo.md)
15- [CiscoDuo](cisco_duo.md)
15- [DigitalOcean](digitalocean.md)
16- [DigitalOcean](digitalocean.md)
17+- [DTT SSO](dtt_sso.md)
16- [Facebook](facebook.md)
18- [Facebook](facebook.md)
17- [Gitea](gitea.md)
19- [Gitea](gitea.md)
18- [GitHub](github.md)
20- [GitHub](github.md)
docs/docs/installation.md+1−1
@@ -5,7 +5,7 @@ title: Installation
5
5
61. Choose how to deploy:
61. Choose how to deploy:
7
7
8− a. Using a [Prebuilt Binary](https://github.com/oauth2-proxy/oauth2-proxy/releases) (current release is `v7.15.2`)
8+ a. Using a [Prebuilt Binary](https://github.com/oauth2-proxy/oauth2-proxy/releases) (current release is `v7.15.4`)
9
9
10 b. Using Go to install the latest release
10 b. Using Go to install the latest release
11 ```bash
11 ```bash
docs/docusaurus.config.js+1−1
@@ -105,7 +105,7 @@ const config = {
105 dropdownActiveClassDisabled: true,
105 dropdownActiveClassDisabled: true,
106 },
106 },
107 {
107 {
108− href: 'https://gophers.slack.com/messages/CM2RSS25N',
108+ href: 'https://cloud-native.slack.com/archives/C098Y5URZ2N',
109 label: 'Slack',
109 label: 'Slack',
110 position: 'right',
110 position: 'right',
111 },
111 },
docs/sidebars.js+2
@@ -46,11 +46,13 @@ const sidebars = {
46 },
46 },
47 items: [
47 items: [
48 "configuration/providers/adfs",
48 "configuration/providers/adfs",
49+ "configuration/providers/atlassian",
49 "configuration/providers/azure",
50 "configuration/providers/azure",
50 "configuration/providers/bitbucket",
51 "configuration/providers/bitbucket",
51 "configuration/providers/cidaas",
52 "configuration/providers/cidaas",
52 "configuration/providers/cisco_duo",
53 "configuration/providers/cisco_duo",
53 "configuration/providers/digitalocean",
54 "configuration/providers/digitalocean",
55+ "configuration/providers/dtt_sso",
54 "configuration/providers/facebook",
56 "configuration/providers/facebook",
55 "configuration/providers/gitea",
57 "configuration/providers/gitea",
56 "configuration/providers/github",
58 "configuration/providers/github",
docs/versioned_docs/version-7.15.x/installation.md+1−1
@@ -5,7 +5,7 @@ title: Installation
5
5
61. Choose how to deploy:
61. Choose how to deploy:
7
7
8− a. Using a [Prebuilt Binary](https://github.com/oauth2-proxy/oauth2-proxy/releases) (current release is `v7.15.2`)
8+ a. Using a [Prebuilt Binary](https://github.com/oauth2-proxy/oauth2-proxy/releases) (current release is `v7.15.4`)
9
9
10 b. Using Go to install the latest release
10 b. Using Go to install the latest release
11 ```bash
11 ```bash
go.mod+39−43
@@ -1,17 +1,17 @@
1module github.com/oauth2-proxy/oauth2-proxy/v7
1module github.com/oauth2-proxy/oauth2-proxy/v7
2
2
3−go 1.25.0
3+go 1.26.0
4
4
5require (
5require (
6 cloud.google.com/go/compute/metadata v0.9.0
6 cloud.google.com/go/compute/metadata v0.9.0
7 github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb
7 github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb
8 github.com/a8m/envsubst v1.4.3
8 github.com/a8m/envsubst v1.4.3
9− github.com/alicebob/miniredis/v2 v2.37.0
9+ github.com/alicebob/miniredis/v2 v2.38.0
10 github.com/bitly/go-simplejson v0.5.1
10 github.com/bitly/go-simplejson v0.5.1
11− github.com/bsm/redislock v0.9.4
11+ github.com/bsm/redislock v0.10.0
12− github.com/coreos/go-oidc/v3 v3.18.0
12+ github.com/coreos/go-oidc/v3 v3.20.0
13 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
13 github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf
14− github.com/fsnotify/fsnotify v1.9.0
14+ github.com/fsnotify/fsnotify v1.10.1
15 github.com/go-jose/go-jose/v3 v3.0.5
15 github.com/go-jose/go-jose/v3 v3.0.5
16 github.com/go-jose/go-jose/v4 v4.1.4
16 github.com/go-jose/go-jose/v4 v4.1.4
17 github.com/go-viper/mapstructure/v2 v2.5.0
17 github.com/go-viper/mapstructure/v2 v2.5.0
@@ -21,67 +21,63 @@ require (
21 github.com/gorilla/mux v1.8.1
21 github.com/gorilla/mux v1.8.1
22 github.com/justinas/alice v1.2.0
22 github.com/justinas/alice v1.2.0
23 github.com/oauth2-proxy/mockoidc v0.0.0-20240214162133-caebfff84d25
23 github.com/oauth2-proxy/mockoidc v0.0.0-20240214162133-caebfff84d25
24− github.com/onsi/ginkgo/v2 v2.28.1
24+ github.com/onsi/ginkgo/v2 v2.32.1
25− github.com/onsi/gomega v1.39.1
25+ github.com/onsi/gomega v1.42.1
26− github.com/pierrec/lz4/v4 v4.1.26
26+ github.com/pierrec/lz4/v4 v4.1.28
27− github.com/prometheus/client_golang v1.23.2
27+ github.com/prometheus/client_golang v1.24.1
28− github.com/redis/go-redis/v9 v9.18.0
28+ github.com/redis/go-redis/v9 v9.22.0
29 github.com/spf13/cast v1.10.0
29 github.com/spf13/cast v1.10.0
30 github.com/spf13/pflag v1.0.10
30 github.com/spf13/pflag v1.0.10
31 github.com/spf13/viper v1.21.0
31 github.com/spf13/viper v1.21.0
32− github.com/stretchr/testify v1.11.1
32+ github.com/stretchr/testify v1.12.0
33 github.com/vmihailenco/msgpack/v5 v5.4.1
33 github.com/vmihailenco/msgpack/v5 v5.4.1
34− go.yaml.in/yaml/v3 v3.0.4
34+ go.yaml.in/yaml/v3 v3.0.5
35− golang.org/x/crypto v0.50.0
35+ golang.org/x/crypto v0.55.0
36− golang.org/x/net v0.53.0
36+ golang.org/x/net v0.58.0
37 golang.org/x/oauth2 v0.36.0
37 golang.org/x/oauth2 v0.36.0
38− golang.org/x/sync v0.20.0
38+ golang.org/x/sync v0.22.0
39− google.golang.org/api v0.275.0
39+ google.golang.org/api v0.293.0
40 gopkg.in/natefinch/lumberjack.v2 v2.2.1
40 gopkg.in/natefinch/lumberjack.v2 v2.2.1
41− k8s.io/apimachinery v0.35.3
41+ k8s.io/apimachinery v0.36.3
42)
42)
43
43
44require (
44require (
45− cloud.google.com/go/auth v0.20.0 // indirect
45+ cloud.google.com/go/auth v0.23.1 // indirect
46 cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
46 cloud.google.com/go/auth/oauth2adapt v0.2.8 // indirect
47− github.com/Masterminds/semver/v3 v3.4.0 // indirect
47+ github.com/Masterminds/semver/v3 v3.5.0 // indirect
48 github.com/beorn7/perks v1.0.1 // indirect
48 github.com/beorn7/perks v1.0.1 // indirect
49 github.com/cespare/xxhash/v2 v2.3.0 // indirect
49 github.com/cespare/xxhash/v2 v2.3.0 // indirect
50− github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc // indirect
51− github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f // indirect
52− github.com/felixge/httpsnoop v1.0.4 // indirect
50+ github.com/felixge/httpsnoop v1.1.0 // indirect
53− github.com/go-logr/logr v1.4.3 // indirect
51+ github.com/go-logr/logr v1.4.4 // indirect
54 github.com/go-logr/stdr v1.2.2 // indirect
52 github.com/go-logr/stdr v1.2.2 // indirect
55 github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
53 github.com/go-task/slim-sprig/v3 v3.0.0 // indirect
56− github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 // indirect
54+ github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3 // indirect
57 github.com/google/s2a-go v0.1.9 // indirect
55 github.com/google/s2a-go v0.1.9 // indirect
58− github.com/googleapis/enterprise-certificate-proxy v0.3.14 // indirect
56+ github.com/googleapis/enterprise-certificate-proxy v0.3.21 // indirect
59− github.com/googleapis/gax-go/v2 v2.21.0 // indirect
57+ github.com/googleapis/gax-go/v2 v2.23.0 // indirect
60 github.com/kylelemons/godebug v1.1.0 // indirect
58 github.com/kylelemons/godebug v1.1.0 // indirect
61 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
59 github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 // indirect
62− github.com/pelletier/go-toml/v2 v2.3.0 // indirect
60+ github.com/pelletier/go-toml/v2 v2.4.3 // indirect
63− github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 // indirect
64 github.com/prometheus/client_model v0.6.2 // indirect
61 github.com/prometheus/client_model v0.6.2 // indirect
65− github.com/prometheus/common v0.67.5 // indirect
62+ github.com/prometheus/common v0.70.1 // indirect
66− github.com/prometheus/procfs v0.20.1 // indirect
63+ github.com/prometheus/procfs v0.21.1 // indirect
67 github.com/sagikazarmark/locafero v0.12.0 // indirect
64 github.com/sagikazarmark/locafero v0.12.0 // indirect
68 github.com/spf13/afero v1.15.0 // indirect
65 github.com/spf13/afero v1.15.0 // indirect
69 github.com/subosito/gotenv v1.6.0 // indirect
66 github.com/subosito/gotenv v1.6.0 // indirect
70 github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
67 github.com/vmihailenco/tagparser/v2 v2.0.0 // indirect
71− github.com/yuin/gopher-lua v1.1.1 // indirect
68+ github.com/yuin/gopher-lua v1.1.2 // indirect
72 go.opentelemetry.io/auto/sdk v1.2.1 // indirect
69 go.opentelemetry.io/auto/sdk v1.2.1 // indirect
73− go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 // indirect
70+ go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0 // indirect
74− go.opentelemetry.io/otel v1.43.0 // indirect
71+ go.opentelemetry.io/otel v1.45.0 // indirect
75− go.opentelemetry.io/otel/metric v1.43.0 // indirect
72+ go.opentelemetry.io/otel/metric v1.45.0 // indirect
76− go.opentelemetry.io/otel/trace v1.43.0 // indirect
73+ go.opentelemetry.io/otel/trace v1.45.0 // indirect
77 go.uber.org/atomic v1.11.0 // indirect
74 go.uber.org/atomic v1.11.0 // indirect
78− go.yaml.in/yaml/v2 v2.4.4 // indirect
79− golang.org/x/mod v0.35.0 // indirect
75+ golang.org/x/mod v0.40.0 // indirect
80− golang.org/x/sys v0.43.0 // indirect
76+ golang.org/x/sys v0.47.0 // indirect
81− golang.org/x/text v0.36.0 // indirect
77+ golang.org/x/text v0.41.0 // indirect
82− golang.org/x/tools v0.44.0 // indirect
78+ golang.org/x/tools v0.49.0 // indirect
83− google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d // indirect
79+ google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1 // indirect
84− google.golang.org/grpc v1.80.0 // indirect
80+ google.golang.org/grpc v1.83.0 // indirect
85− google.golang.org/protobuf v1.36.11 // indirect
81+ google.golang.org/protobuf v1.36.12 // indirect
86 gopkg.in/yaml.v3 v3.0.1 // indirect
82 gopkg.in/yaml.v3 v3.0.1 // indirect
87)
83)
go.sum+91−143
@@ -1,7 +1,5 @@
1−cloud.google.com/go/auth v0.18.2 h1:+Nbt5Ev0xEqxlNjd6c+yYUeosQ5TtEUaNcN/3FozlaM=
1+cloud.google.com/go/auth v0.23.1 h1:1tPpBPG02lQHmoiAvs9egyCASqXP0xgobptjZzov/Jg=
2−cloud.google.com/go/auth v0.18.2/go.mod h1:xD+oY7gcahcu7G2SG2DsBerfFxgPAJz17zz2joOFF3M=
2+cloud.google.com/go/auth v0.23.1/go.mod h1:4DhBRcqvtljQN3dJ57qtqbib5ZGCYE5f2crfiiC2EM0=
3−cloud.google.com/go/auth v0.20.0 h1:kXTssoVb4azsVDoUiF8KvxAqrsQcQtB53DcSgta74CA=
4−cloud.google.com/go/auth v0.20.0/go.mod h1:942/yi/itH1SsmpyrbnTMDgGfdy2BUqIKyd0cyYLc5Q=
5cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
3cloud.google.com/go/auth/oauth2adapt v0.2.8 h1:keo8NaayQZ6wimpNSmW5OPc283g65QNIiLpZnkHRbnc=
6cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
4cloud.google.com/go/auth/oauth2adapt v0.2.8/go.mod h1:XQ9y31RkqZCcwJWNSx2Xvric3RrU88hAYYbjDWYDL+c=
7cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
5cloud.google.com/go/compute/metadata v0.9.0 h1:pDUj4QMoPejqq20dK0Pg2N4yG9zIkYGdBtwLoEkH9Zs=
@@ -10,14 +8,14 @@ github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb h1:ZVN4Iat3runWO
10github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb/go.mod h1:WsAABbY4HQBgd3mGuG4KMNTbHJCPvx9IVBHzysbknss=
8github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb/go.mod h1:WsAABbY4HQBgd3mGuG4KMNTbHJCPvx9IVBHzysbknss=
11github.com/FZambia/sentinel v1.0.0 h1:KJ0ryjKTZk5WMp0dXvSdNqp3lFaW1fNFuEYfrkLOYIc=
9github.com/FZambia/sentinel v1.0.0 h1:KJ0ryjKTZk5WMp0dXvSdNqp3lFaW1fNFuEYfrkLOYIc=
12github.com/FZambia/sentinel v1.0.0/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI=
10github.com/FZambia/sentinel v1.0.0/go.mod h1:ytL1Am/RLlAoAXG6Kj5LNuw/TRRQrv2rt2FT26vP5gI=
13−github.com/Masterminds/semver/v3 v3.4.0 h1:Zog+i5UMtVoCU8oKka5P7i9q9HgrJeGzI9SA1Xbatp0=
11+github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE=
14−github.com/Masterminds/semver/v3 v3.4.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
12+github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM=
15github.com/a8m/envsubst v1.4.3 h1:kDF7paGK8QACWYaQo6KtyYBozY2jhQrTuNNuUxQkhJY=
13github.com/a8m/envsubst v1.4.3 h1:kDF7paGK8QACWYaQo6KtyYBozY2jhQrTuNNuUxQkhJY=
16github.com/a8m/envsubst v1.4.3/go.mod h1:4jjHWQlZoaXPoLQUb7H2qT4iLkZDdmEQiOUogdUmqVU=
14github.com/a8m/envsubst v1.4.3/go.mod h1:4jjHWQlZoaXPoLQUb7H2qT4iLkZDdmEQiOUogdUmqVU=
17github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
15github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGnFV6hVsYE877CKEZ6tDNTjaSXYUk6QqoIK6PrAtcc=
18github.com/alicebob/miniredis/v2 v2.11.1/go.mod h1:UA48pmi7aSazcGAvcdKcBB49z521IC9VjTTRz2nIaJE=
16github.com/alicebob/miniredis/v2 v2.11.1/go.mod h1:UA48pmi7aSazcGAvcdKcBB49z521IC9VjTTRz2nIaJE=
19−github.com/alicebob/miniredis/v2 v2.37.0 h1:RheObYW32G1aiJIj81XVt78ZHJpHonHLHW7OLIshq68=
17+github.com/alicebob/miniredis/v2 v2.38.0 h1:nZAzCR+Lj+Vxk4ZXzm2NuKq2O33RXj1XxJ2e2uP9jiw=
20−github.com/alicebob/miniredis/v2 v2.37.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM=
18+github.com/alicebob/miniredis/v2 v2.38.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM=
21github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
19github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM=
22github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
20github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw=
23github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow=
21github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow=
@@ -26,51 +24,41 @@ github.com/bsm/ginkgo/v2 v2.12.0 h1:Ny8MWAHyOepLGlLKYmXG4IEkioBysk6GpaRTLC8zwWs=
26github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
24github.com/bsm/ginkgo/v2 v2.12.0/go.mod h1:SwYbGRRDovPVboqFv0tPTcG1sN61LM1Z4ARdbAV9g4c=
27github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
25github.com/bsm/gomega v1.27.10 h1:yeMWxP2pV2fG3FgAODIY8EiRE3dy0aeFYt4l7wh6yKA=
28github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
26github.com/bsm/gomega v1.27.10/go.mod h1:JyEr/xRbxbtgWNi8tIEVPUYZ5Dzef52k01W3YH0H+O0=
29−github.com/bsm/redislock v0.9.4 h1:X/Wse1DPpiQgHbVYRE9zv6m070UcKoOGekgvpNhiSvw=
27+github.com/bsm/redislock v0.10.0 h1:NAe1OHDnwPmWEzD+LpWBZXAdZrWld/xj/wmqEcQednQ=
30−github.com/bsm/redislock v0.9.4/go.mod h1:Epf7AJLiSFwLCiZcfi6pWFO/8eAYrYpQXFxEDPoDeAk=
28+github.com/bsm/redislock v0.10.0/go.mod h1:M05WZGjPbX/8ne7LmB7AbOsa2kv7Jzx4fqkLpDFZvJs=
31github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
29github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs=
32github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
30github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs=
33github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
31github.com/chzyer/logex v1.1.10/go.mod h1:+Ywpsq7O8HXn0nuIou7OrIPyXbp3wmkHB+jjWRnGsAI=
34github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
32github.com/chzyer/readline v0.0.0-20180603132655-2972be24d48e/go.mod h1:nSuG5e5PlCu98SY8svDHJxuZscDgtXS6KTTbou5AhLI=
35github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
33github.com/chzyer/test v0.0.0-20180213035817-a1ea475d72b1/go.mod h1:Q3SI9o4m/ZMnBNeIyt5eFwwo7qiLfzFZmjNmxjkiQlU=
36−github.com/coreos/go-oidc/v3 v3.17.0 h1:hWBGaQfbi0iVviX4ibC7bk8OKT5qNr4klBaCHVNvehc=
34+github.com/coreos/go-oidc/v3 v3.20.0 h1:EtE0WIBHk03N+DqGkY4+UONzzZHk7amKt6IyNd7OsZE=
37−github.com/coreos/go-oidc/v3 v3.17.0/go.mod h1:wqPbKFrVnE90vty060SB40FCJ8fTHTxSwyXJqZH+sI8=
38−github.com/coreos/go-oidc/v3 v3.18.0 h1:V9orjXynvu5wiC9SemFTWnG4F45v403aIcjWo0d41+A=
39−github.com/coreos/go-oidc/v3 v3.18.0/go.mod h1:DYCf24+ncYi+XkIH97GY1+dqoRlbaSI26KVTCI9SrY4=
35+github.com/coreos/go-oidc/v3 v3.20.0/go.mod h1:DYCf24+ncYi+XkIH97GY1+dqoRlbaSI26KVTCI9SrY4=
40github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
36github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf h1:iW4rZ826su+pqaw19uhpSCzhj44qo35pNgKFGqzDKkU=
41github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
37github.com/coreos/go-systemd v0.0.0-20191104093116-d3cd4ed1dbcf/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4=
42github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
38github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
43−github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc h1:U9qPSI2PIWSS1VwoXQT9A3Wy9MM3WgvqSxFWenqJduM=
39+github.com/felixge/httpsnoop v1.1.0 h1:3YtUj32ZZkqZtt3sZZsClsymw/QDuVfpNhoA31zeORc=
44−github.com/davecgh/go-spew v1.1.2-0.20180830191138-d8f796af33cc/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
40+github.com/felixge/httpsnoop v1.1.0/go.mod h1:Zqxgdd+1Rkcz8euOqdr7lqgCRJztwr5hp9vDSi5UZCE=
45−github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f h1:lO4WD4F/rVNCu3HqELle0jiPLLBs70cWOduZpkS1E78=
46−github.com/dgryski/go-rendezvous v0.0.0-20200823014737-9f7001d12a5f/go.mod h1:cuUVRXasLTGF7a8hSLbxyZXjz+1KgoB3wDUb6vlszIc=
47−github.com/felixge/httpsnoop v1.0.4 h1:NFTV2Zj1bL4mc9sqWACXbQFVBBg2W3GPvqp8/ESS2Wg=
48−github.com/felixge/httpsnoop v1.0.4/go.mod h1:m8KPJKqk1gH5J9DgRY2ASl2lWCfGKXixSwevea8zH2U=
49github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
41github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8=
50github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
42github.com/frankban/quicktest v1.14.6/go.mod h1:4ptaffx2x8+WTWXmUCuVU6aPUX1/Mz7zb5vbUoiM6w0=
51−github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S9k=
43+github.com/fsnotify/fsnotify v1.10.1 h1:b0/UzAf9yR5rhf3RPm9gf3ehBPpf0oZKIjtpKrx59Ho=
52−github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0=
44+github.com/fsnotify/fsnotify v1.10.1/go.mod h1:TLheqan6HD6GBK6PrDWyDPBaEV8LspOxvPSjC+bVfgo=
53github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs=
45github.com/gkampitakis/ciinfo v0.3.2 h1:JcuOPk8ZU7nZQjdUhctuhQofk7BGHuIy0c9Ez8BNhXs=
54github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
46github.com/gkampitakis/ciinfo v0.3.2/go.mod h1:1NIwaOcFChN4fa/B0hEBdAb6npDlFL8Bwx4dfRLRqAo=
55github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
47github.com/gkampitakis/go-diff v1.3.2 h1:Qyn0J9XJSDTgnsgHRdz9Zp24RaJeKMUHg2+PDZZdC4M=
56github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
48github.com/gkampitakis/go-diff v1.3.2/go.mod h1:LLgOrpqleQe26cte8s36HTWcTmMEur6OPYerdAAS9tk=
57github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE=
49github.com/gkampitakis/go-snaps v0.5.15 h1:amyJrvM1D33cPHwVrjo9jQxX8g/7E2wYdZ+01KS3zGE=
58github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc=
50github.com/gkampitakis/go-snaps v0.5.15/go.mod h1:HNpx/9GoKisdhw9AFOBT1N7DBs9DiHo/hGheFGBZ+mc=
59−github.com/go-jose/go-jose/v3 v3.0.4 h1:Wp5HA7bLQcKnf6YYao/4kpRpVMp/yf6+pJKV8WFSaNY=
60−github.com/go-jose/go-jose/v3 v3.0.4/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
61github.com/go-jose/go-jose/v3 v3.0.5 h1:BLLJWbC4nMZOfuPVxoZIxeYsn6Nl2r1fITaJ78UQlVQ=
51github.com/go-jose/go-jose/v3 v3.0.5 h1:BLLJWbC4nMZOfuPVxoZIxeYsn6Nl2r1fITaJ78UQlVQ=
62github.com/go-jose/go-jose/v3 v3.0.5/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
52github.com/go-jose/go-jose/v3 v3.0.5/go.mod h1:5b+7YgP7ZICgJDBdfjZaIt+H/9L9T/YQrVfLAMboGkQ=
63github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
53github.com/go-jose/go-jose/v4 v4.1.4 h1:moDMcTHmvE6Groj34emNPLs/qtYXRVcd6S7NHbHz3kA=
64github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
54github.com/go-jose/go-jose/v4 v4.1.4/go.mod h1:x4oUasVrzR7071A4TnHLGSPpNOm2a21K9Kf04k1rs08=
65github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
55github.com/go-logr/logr v1.2.2/go.mod h1:jdQByPbusPIv2/zmleS9BjJVeZ6kBagPoEUsqbVz/1A=
66−github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI=
56+github.com/go-logr/logr v1.4.4 h1:tG4xh9yMsRCAiodLVTxyrkzSZ9+o0L1Kg/+cPVcbP/8=
67−github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
57+github.com/go-logr/logr v1.4.4/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY=
68github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
58github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag=
69github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
59github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE=
70github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
60github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI=
71github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
61github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8=
72−github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs=
73−github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
74github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
62github.com/go-viper/mapstructure/v2 v2.5.0 h1:vM5IJoUAy3d7zRSVtIwQgBj7BiWtMPfmPEgAXnvj1Ro=
75github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
63github.com/go-viper/mapstructure/v2 v2.5.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM=
76github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
64github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw=
@@ -84,31 +72,27 @@ github.com/gomodule/redigo v1.7.1-0.20190322064113-39e2c31b7ca3/go.mod h1:B4C85q
84github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
72github.com/google/go-cmp v0.5.9/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
85github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
73github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8=
86github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
74github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU=
87−github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc h1:VBbFa1lDYWEeV5FZKUiYKYT0VxCp9twUmmaq9eb8sXw=
75+github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3 h1:LMLX+LgTNWpfvCBdFebv6EsYotImrt/Ppc5cXIriCSo=
88−github.com/google/pprof v0.0.0-20260302011040-a15ffb7f9dcc/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI=
76+github.com/google/pprof v0.0.0-20260802141513-ef3492d7dac3/go.mod h1:jl5iWTm0/hd5PjEYEOuwAJ57L/CibdZfrqZ5XA5GrCk=
89−github.com/google/pprof v0.0.0-20260402051712-545e8a4df936 h1:EwtI+Al+DeppwYX2oXJCETMO23COyaKGP6fHVpkpWpg=
90−github.com/google/pprof v0.0.0-20260402051712-545e8a4df936/go.mod h1:MxpfABSjhmINe3F1It9d+8exIHFvUqtLIRCdOGNXqiI=
91github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
77github.com/google/s2a-go v0.1.9 h1:LGD7gtMgezd8a/Xak7mEWL0PjoTQFvpRudN895yqKW0=
92github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
78github.com/google/s2a-go v0.1.9/go.mod h1:YA0Ei2ZQL3acow2O62kdp9UlnvMmU7kA6Eutn0dXayM=
93github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
79github.com/google/uuid v1.1.1/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
94github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
80github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
95github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
81github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
96−github.com/googleapis/enterprise-certificate-proxy v0.3.14 h1:yh8ncqsbUY4shRD5dA6RlzjJaT4hi3kII+zYw8wmLb8=
82+github.com/googleapis/enterprise-certificate-proxy v0.3.21 h1:OFdQ3tnCX/zaQ0Cedur3D3z7kI6HiLX9g3TiAN4/DFU=
97−github.com/googleapis/enterprise-certificate-proxy v0.3.14/go.mod h1:vqVt9yG9480NtzREnTlmGSBmFrA+bzb0yl0TxoBQXOg=
83+github.com/googleapis/enterprise-certificate-proxy v0.3.21/go.mod h1:L3D/IQExI6LqEjBdXcZQ1WluSgigQmSwBboFstVPM4w=
98−github.com/googleapis/gax-go/v2 v2.19.0 h1:fYQaUOiGwll0cGj7jmHT/0nPlcrZDFPrZRhTsoCr8hE=
84+github.com/googleapis/gax-go/v2 v2.23.0 h1:Tchl7qkvE7Ip3y+ztvNufYFvkfqTe7NfLTYGIdJRLuE=
99−github.com/googleapis/gax-go/v2 v2.19.0/go.mod h1:w2ROXVdfGEVFXzmlciUU4EdjHgWvB5h2n6x/8XSTTJA=
100−github.com/googleapis/gax-go/v2 v2.21.0 h1:h45NjjzEO3faG9Lg/cFrBh2PgegVVgzqKzuZl/wMbiI=
101−github.com/googleapis/gax-go/v2 v2.21.0/go.mod h1:But/NJU6TnZsrLai/xBAQLLz+Hc7fHZJt/hsCz3Fih4=
85+github.com/googleapis/gax-go/v2 v2.23.0/go.mod h1:rBQKOVJCdb8IFEzg+FCwlt1LP/xMDGuqUXhUG+XMXEg=
102github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
86github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY=
103github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
87github.com/gorilla/mux v1.8.1/go.mod h1:AKf9I4AEqPTmMytcMc0KkNouC66V3BtZ4qD5fmWSiMQ=
104github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE=
88github.com/joshdk/go-junit v1.0.0 h1:S86cUKIdwBHWwA6xCmFlf3RTLfVXYQfvanM5Uh+K6GE=
105github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung=
89github.com/joshdk/go-junit v1.0.0/go.mod h1:TiiV0PqkaNfFXjEiyjWM3XXrhVyCa1K4Zfga6W52ung=
106github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo=
90github.com/justinas/alice v1.2.0 h1:+MHSA/vccVCF4Uq37S42jwlkvI2Xzl7zTPCN5BnZNVo=
107github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA=
91github.com/justinas/alice v1.2.0/go.mod h1:fN5HRH/reO/zrUflLfTN43t3vXvKzvZIENsNEe7i7qA=
108−github.com/klauspost/compress v1.18.0 h1:c/Cqfb0r+Yi+JtIEq73FWXVkRonBlf0CRNYc8Zttxdo=
92+github.com/klauspost/compress v1.19.1 h1:VsB4HPswih7mmZ8WleSFQ75c/Ui1M4trX5oAsJnhSlk=
109−github.com/klauspost/compress v1.18.0/go.mod h1:2Pp+KzxcywXVXMr50+X0Q/Lsb43OQHYWRCY2AiWywWQ=
93+github.com/klauspost/compress v1.19.1/go.mod h1:cwPg85FWrGar70rWktvGQj8/hthj3wpl0PGDogxkrSQ=
110−github.com/klauspost/cpuid/v2 v2.0.9 h1:lgaqFMSdTdQYdZ04uHyN2d/eKdOMyi2YLSvlQIBFYa4=
94+github.com/klauspost/cpuid/v2 v2.2.10 h1:tBs3QSyvjDyFTq3uoc/9xFpCuOsJQFNPiAhYdw2skhE=
111−github.com/klauspost/cpuid/v2 v2.0.9/go.mod h1:FInQzS24/EEf25PyTYn52gqo7WaD8xa0213Md/qVLRg=
95+github.com/klauspost/cpuid/v2 v2.2.10/go.mod h1:hqwkgyIinND0mEev00jJYCxPNVRVXFQeu1XKlok6oO0=
112github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
96github.com/kr/pretty v0.3.1 h1:flRD4NNwYAUpkphVc1HcthR4KEIFJ65n8Mw5qdRn3LE=
113github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
97github.com/kr/pretty v0.3.1/go.mod h1:hoEshYVHaxMs3cyo3Yncou5ZscifuDolrwPKZanG3xk=
114github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
98github.com/kr/text v0.2.0 h1:5Nx0Ya0ZqY2ygV366QzturHI13Jq95ApcVaJBhpS+AY=
@@ -125,29 +109,25 @@ github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822 h1:C3w9PqII01/Oq
125github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
109github.com/munnerz/goautoneg v0.0.0-20191010083416-a7dc8b61c822/go.mod h1:+n7T8mK8HuQTcFwEeznm/DIxMOiR9yIdICNftLE1DvQ=
126github.com/oauth2-proxy/mockoidc v0.0.0-20240214162133-caebfff84d25 h1:9bCMuD3TcnjeqjPT2gSlha4asp8NvgcFRYExCaikCxk=
110github.com/oauth2-proxy/mockoidc v0.0.0-20240214162133-caebfff84d25 h1:9bCMuD3TcnjeqjPT2gSlha4asp8NvgcFRYExCaikCxk=
127github.com/oauth2-proxy/mockoidc v0.0.0-20240214162133-caebfff84d25/go.mod h1:eDjgYHYDJbPLBLsyZ6qRaugP0mX8vePOhZ5id1fdzJw=
111github.com/oauth2-proxy/mockoidc v0.0.0-20240214162133-caebfff84d25/go.mod h1:eDjgYHYDJbPLBLsyZ6qRaugP0mX8vePOhZ5id1fdzJw=
128−github.com/onsi/ginkgo/v2 v2.28.1 h1:S4hj+HbZp40fNKuLUQOYLDgZLwNUVn19N3Atb98NCyI=
112+github.com/onsi/ginkgo/v2 v2.32.1 h1:6tlvcDm/3sE8lGJbZ4+d4mO3RLy24/tQWOFzVSQNIfw=
129−github.com/onsi/ginkgo/v2 v2.28.1/go.mod h1:CLtbVInNckU3/+gC8LzkGUb9oF+e8W8TdUsxPwvdOgE=
113+github.com/onsi/ginkgo/v2 v2.32.1/go.mod h1:+aXOY+vzZ5mu2iI2HpTZUPmM//oQfsNFX6gU9kNcA44=
130−github.com/onsi/gomega v1.39.1 h1:1IJLAad4zjPn2PsnhH70V4DKRFlrCzGBNrNaru+Vf28=
114+github.com/onsi/gomega v1.42.1 h1:iN1rCUX+44NZ1Dc97MPoeFYbFR0vh8zxoxMFwKdyZ6I=
131−github.com/onsi/gomega v1.39.1/go.mod h1:hL6yVALoTOxeWudERyfppUcZXjMwIMLnuSfruD2lcfg=
115+github.com/onsi/gomega v1.42.1/go.mod h1:REff/hsDsodHoKlWsP2mAPhu1+5/6hVYNf9rIEBpeSg=
132−github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0t5Ec4=
133−github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
116+github.com/pelletier/go-toml/v2 v2.4.3 h1:GTRvJQutkOSftxIFD5xw9aepkYNuPWmVJpffdDPYVpY=
134−github.com/pelletier/go-toml/v2 v2.3.0 h1:k59bC/lIZREW0/iVaQR8nDHxVq8OVlIzYCOJf421CaM=
135−github.com/pelletier/go-toml/v2 v2.3.0/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
117+github.com/pelletier/go-toml/v2 v2.4.3/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY=
136−github.com/pierrec/lz4/v4 v4.1.26 h1:GrpZw1gZttORinvzBdXPUXATeqlJjqUG/D87TKMnhjY=
118+github.com/pierrec/lz4/v4 v4.1.28 h1:pPEPwRJ4kybBTfGt28q7lQsRJQHhC08axprdLD5Ppio=
137−github.com/pierrec/lz4/v4 v4.1.26/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
119+github.com/pierrec/lz4/v4 v4.1.28/go.mod h1:EoQMVJgeeEOMsCqCzqFm2O0cJvljX2nGZjcRIPL34O4=
138github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
120github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
139−github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2 h1:Jamvg5psRIccs7FGNTlIRMkT8wgtp5eCXdBlqhYGL6U=
121+github.com/prometheus/client_golang v1.24.1 h1:JnJkREXzWxUdCuPFpIWZiPispT9xVV59uiuyR2bPlnU=
140−github.com/pmezard/go-difflib v1.0.1-0.20181226105442-5d4384ee4fb2/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
141−github.com/prometheus/client_golang v1.23.2 h1:Je96obch5RDVy3FDMndoUsjAhG5Edi49h0RJWRi/o0o=
142−github.com/prometheus/client_golang v1.23.2/go.mod h1:Tb1a6LWHB3/SPIzCoaDXI4I8UHKeFTEQ1YCr+0Gyqmg=
122+github.com/prometheus/client_golang v1.24.1/go.mod h1:F+oSRECHg4sse5ucfYpYDeIv/hu68Zo0uoHKetWnzcE=
143github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
123github.com/prometheus/client_model v0.6.2 h1:oBsgwpGs7iVziMvrGhE53c/GrLUsZdHnqNwqPLxwZyk=
144github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
124github.com/prometheus/client_model v0.6.2/go.mod h1:y3m2F6Gdpfy6Ut/GBsUqTWZqCUvMVzSfMLjcu6wAwpE=
145−github.com/prometheus/common v0.67.5 h1:pIgK94WWlQt1WLwAC5j2ynLaBRDiinoAb86HZHTUGI4=
125+github.com/prometheus/common v0.70.1 h1:1HvjP4D5oL3t8RsPlwxA9onvvStjtIHYE5XuuwOi/PY=
146−github.com/prometheus/common v0.67.5/go.mod h1:SjE/0MzDEEAyrdr5Gqc6G+sXI67maCxzaT3A2+HqjUw=
126+github.com/prometheus/common v0.70.1/go.mod h1:VdFUQDMZK3VLkurFUVhia6uys/0suUp86TJz5qbJRhc=
147−github.com/prometheus/procfs v0.20.1 h1:XwbrGOIplXW/AU3YhIhLODXMJYyC1isLFfYCsTEycfc=
127+github.com/prometheus/procfs v0.21.1 h1:GljZCt+zSTS+NZq88cyQ1LjZ+RCHp3uVuabBWA5+OJI=
148−github.com/prometheus/procfs v0.20.1/go.mod h1:o9EMBZGRyvDrSPH1RqdxhojkuXstoe4UlK79eF5TGGo=
128+github.com/prometheus/procfs v0.21.1/go.mod h1:aB55Cww9pdSJVHk0hUf0inxWyyjPogFIjmHKYgMKmtY=
149−github.com/redis/go-redis/v9 v9.18.0 h1:pMkxYPkEbMPwRdenAzUNyFNrDgHx9U+DrBabWNfSRQs=
129+github.com/redis/go-redis/v9 v9.22.0 h1:laDvpYXTJtZLloinw1fA5Kqd6HAEH2XKxOkG/PDq2F0=
150−github.com/redis/go-redis/v9 v9.18.0/go.mod h1:k3ufPphLU5YXwNTUcCRXGxUoF1fqxnhFQmscfkCoDA0=
130+github.com/redis/go-redis/v9 v9.22.0/go.mod h1:y2g0Wj8rQvuK0ELM+oxSudcLtC09JScs98I/X9gRWY4=
151github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
131github.com/rogpeppe/go-internal v1.14.1 h1:UQB4HGPB6osV0SQTLymcB4TgvyWu6ZyliaW0tI/otEQ=
152github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
132github.com/rogpeppe/go-internal v1.14.1/go.mod h1:MaRKkUm5W0goXpeCfT7UZI6fk/L7L7so1lCWt35ZSgc=
153github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
133github.com/sagikazarmark/locafero v0.12.0 h1:/NQhBAkUb4+fH1jivKHWusDYFjMOOKU88eegjfxfHb4=
@@ -162,8 +142,8 @@ github.com/spf13/viper v1.21.0 h1:x5S+0EU27Lbphp4UKm1C+1oQO+rKx36vfCoaVebLFSU=
162github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
142github.com/spf13/viper v1.21.0/go.mod h1:P0lhsswPGWD/1lZJ9ny3fYnVqxiegrlNrEmgLjbTCAY=
163github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
143github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
164github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
144github.com/stretchr/testify v1.7.0/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
165−github.com/stretchr/testify v1.11.1 h1:7s2iGBzp5EwR7/aIZr8ao5+dra3wiQyKjjFuvgVKu7U=
145+github.com/stretchr/testify v1.12.0 h1:K6Mr6jO9JICuend/5xzTM03ydSV3vdNRYAdPSukj8uI=
166−github.com/stretchr/testify v1.11.1/go.mod h1:wZwfW3scLgRK+23gO65QZefKpKQRnfz6sD981Nm4B6U=
146+github.com/stretchr/testify v1.12.0/go.mod h1:bOYBZb5qJ00vPzWfIqBUZPaxK8jWiXc6d3ErP4Ca9Gw=
167github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
147github.com/subosito/gotenv v1.6.0 h1:9NlTDc1FTs4qu0DDq7AEtTPNw6SVm7uBMsUCUjABIf8=
168github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
148github.com/subosito/gotenv v1.6.0/go.mod h1:Dk4QP5c2W3ibzajGcXpNraDfq2IrhjMIvMSWPKKo0FU=
169github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
149github.com/tidwall/gjson v1.18.0 h1:FIDeeyB800efLX89e5a8Y0BNH+LOngJyGrIWxG2FKQY=
@@ -181,74 +161,57 @@ github.com/vmihailenco/tagparser/v2 v2.0.0/go.mod h1:Wri+At7QHww0WTrCBeu4J6bNtoV
181github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
161github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
182github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
162github.com/yuin/gopher-lua v0.0.0-20190206043414-8bfc7677f583/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
183github.com/yuin/gopher-lua v0.0.0-20191213034115-f46add6fdb5c/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
163github.com/yuin/gopher-lua v0.0.0-20191213034115-f46add6fdb5c/go.mod h1:gqRgreBUhTSL0GeU64rtZ3Uq3wtjOa/TB2YfrtkCbVQ=
184−github.com/yuin/gopher-lua v1.1.1 h1:kYKnWBjvbNP4XLT3+bPEwAXJx262OhaHDWDVOPjL46M=
164+github.com/yuin/gopher-lua v1.1.2 h1:yF/FjE3hD65tBbt0VXLE13HWS9h34fdzJmrWRXwobGA=
185−github.com/yuin/gopher-lua v1.1.1/go.mod h1:GBR0iDaNXjAgGg9zfCvksxSRnQx76gclCIb7kdAd1Pw=
165+github.com/yuin/gopher-lua v1.1.2/go.mod h1:7aRmXIWl37SqRf0koeyylBEzJ+aPt8A+mmkQ4f1ntR8=
186−github.com/zeebo/xxh3 v1.0.2 h1:xZmwmqxHZA8AI603jOQ0tMqmBr9lPeFwGg6d+xy9DC0=
166+github.com/zeebo/xxh3 v1.1.0 h1:s7DLGDK45Dyfg7++yxI0khrfwq9661w9EN78eP/UZVs=
187−github.com/zeebo/xxh3 v1.0.2/go.mod h1:5NWz9Sef7zIDm2JHfFlcQvNekmcEl9ekUZQQKCYaDcA=
167+github.com/zeebo/xxh3 v1.1.0/go.mod h1:IisAie1LELR4xhVinxWS5+zf1lA4p0MW4T+w+W07F5s=
188go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
168go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64=
189go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
169go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y=
190−go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0 h1:q4XOmH/0opmeuJtPsbFNivyl7bCt7yRBbeEm2sC/XtQ=
191−go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.61.0/go.mod h1:snMWehoOh2wsEwnvvwtDyFCxVeDAODenXHtn5vzrKjo=
192go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 h1:yI1/OhfEPy7J9eoa6Sj051C7n5dvpj0QX8g4sRchg04=
170go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0 h1:yI1/OhfEPy7J9eoa6Sj051C7n5dvpj0QX8g4sRchg04=
193−go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0 h1:OyrsyzuttWTSur2qN/Lm0m2a8yqyIjUVBZcxFPuXq2o=
171+go.opentelemetry.io/contrib/instrumentation/google.golang.org/grpc/otelgrpc v0.67.0/go.mod h1:NoUCKYWK+3ecatC4HjkRktREheMeEtrXoQxrqYFeHSc=
194−go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.67.0/go.mod h1:C2NGBr+kAB4bk3xtMXfZ94gqFDtg/GkI7e9zqGh5Beg=
195−go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0 h1:CqXxU8VOmDefoh0+ztfGaymYbhdB/tT3zs79QaZTNGY=
172+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0 h1:LMuyCAyfalSjDyjdC65nK6N0zoTT63+E/u95X0JovZI=
196−go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.68.0/go.mod h1:BuhAPThV8PBHBvg8ZzZ/Ok3idOdhWIodywz2xEcRbJo=
173+go.opentelemetry.io/contrib/instrumentation/net/http/otelhttp v0.70.0/go.mod h1:085m8qbm4hgc8rZWGDEa4vmyyo2c3nPxUslYUKUIU04=
197−go.opentelemetry.io/otel v1.42.0 h1:lSQGzTgVR3+sgJDAU/7/ZMjN9Z+vUip7leaqBKy4sho=
174+go.opentelemetry.io/otel v1.45.0 h1:pdrWmLHofpubmArBv1LgFSv1Z0Ie/ppdZzu+kUN5EeU=
198−go.opentelemetry.io/otel v1.42.0/go.mod h1:lJNsdRMxCUIWuMlVJWzecSMuNjE7dOYyWlqOXWkdqCc=
175+go.opentelemetry.io/otel v1.45.0/go.mod h1:XZxIqPapzEYnhNSScF5DIqXhm/rYi0FzCe2XddAwZfQ=
199−go.opentelemetry.io/otel v1.43.0 h1:mYIM03dnh5zfN7HautFE4ieIig9amkNANT+xcVxAj9I=
176+go.opentelemetry.io/otel/metric v1.45.0 h1:7Eg1uH7CJ5cXv9is6tnBe1FI6rj1nwUdbFypRm3br/M=
200−go.opentelemetry.io/otel v1.43.0/go.mod h1:JuG+u74mvjvcm8vj8pI5XiHy1zDeoCS2LB1spIq7Ay0=
201−go.opentelemetry.io/otel/metric v1.42.0 h1:2jXG+3oZLNXEPfNmnpxKDeZsFI5o4J+nz6xUlaFdF/4=
202−go.opentelemetry.io/otel/metric v1.42.0/go.mod h1:RlUN/7vTU7Ao/diDkEpQpnz3/92J9ko05BIwxYa2SSI=
177+go.opentelemetry.io/otel/metric v1.45.0/go.mod h1:HAPbm1nd3p1PmFH7v2dR+6BjXxw+Lq4a2+pndMAm08s=
203−go.opentelemetry.io/otel/metric v1.43.0 h1:d7638QeInOnuwOONPp4JAOGfbCEpYb+K6DVWvdxGzgM=
204−go.opentelemetry.io/otel/metric v1.43.0/go.mod h1:RDnPtIxvqlgO8GRW18W6Z/4P462ldprJtfxHxyKd2PY=
205−go.opentelemetry.io/otel/sdk v1.42.0 h1:LyC8+jqk6UJwdrI/8VydAq/hvkFKNHZVIWuslJXYsDo=
178+go.opentelemetry.io/otel/sdk v1.45.0 h1:4VVSMgQ83dUgW2aoX5f6JgLvHwIvzcuLnF9lUdCSpCw=
206−go.opentelemetry.io/otel/sdk v1.42.0/go.mod h1:rGHCAxd9DAph0joO4W6OPwxjNTYWghRWmkHuGbayMts=
179+go.opentelemetry.io/otel/sdk v1.45.0/go.mod h1:Sr40LgXV7DsKMMJMKOhUWOgMWTfAaqvm2kF0g7ilwuA=
207−go.opentelemetry.io/otel/sdk v1.43.0 h1:pi5mE86i5rTeLXqoF/hhiBtUNcrAGHLKQdhg4h4V9Dg=
208−go.opentelemetry.io/otel/sdk/metric v1.42.0 h1:D/1QR46Clz6ajyZ3G8SgNlTJKBdGp84q9RKCAZ3YGuA=
180+go.opentelemetry.io/otel/sdk/metric v1.45.0 h1:oVFszMfyj1Am6s24Vtc7wBb8BKLcwepJjNEYILuiE3o=
209−go.opentelemetry.io/otel/sdk/metric v1.42.0/go.mod h1:Ua6AAlDKdZ7tdvaQKfSmnFTdHx37+J4ba8MwVCYM5hc=
181+go.opentelemetry.io/otel/sdk/metric v1.45.0/go.mod h1:vUWUxDZvu1WVRj8JA8S0AdhsPrZoDpA2DdZauIh4mDA=
210−go.opentelemetry.io/otel/sdk/metric v1.43.0 h1:S88dyqXjJkuBNLeMcVPRFXpRw2fuwdvfCGLEo89fDkw=
211−go.opentelemetry.io/otel/trace v1.42.0 h1:OUCgIPt+mzOnaUTpOQcBiM/PLQ/Op7oq6g4LenLmOYY=
182+go.opentelemetry.io/otel/trace v1.45.0 h1:l/mP6Uv7oNO7/TblbhpbgMidxhq1uO/rPsikOyVhxag=
212−go.opentelemetry.io/otel/trace v1.42.0/go.mod h1:f3K9S+IFqnumBkKhRJMeaZeNk9epyhnCmQh/EysQCdc=
183+go.opentelemetry.io/otel/trace v1.45.0/go.mod h1:qoJJA2xNMnxRrdISU/kLtfUH2wNeQbiv+jhs/CxI8bc=
213−go.opentelemetry.io/otel/trace v1.43.0 h1:BkNrHpup+4k4w+ZZ86CZoHHEkohws8AY+WTX09nk+3A=
214−go.opentelemetry.io/otel/trace v1.43.0/go.mod h1:/QJhyVBUUswCphDVxq+8mld+AvhXZLhe+8WVFxiFff0=
215go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
184go.uber.org/atomic v1.11.0 h1:ZvwS0R+56ePWxUNi+Atn9dWONBPp/AUETXlHW0DxSjE=
216go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
185go.uber.org/atomic v1.11.0/go.mod h1:LUxbIzbOniOlMKjJjyPfpl4v+PKK2cNJn91OQbhoJI0=
217go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
186go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
218go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
187go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
219go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
188go.yaml.in/yaml/v2 v2.4.4 h1:tuyd0P+2Ont/d6e2rl3be67goVK4R6deVxCUX5vyPaQ=
220go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
189go.yaml.in/yaml/v2 v2.4.4/go.mod h1:gMZqIpDtDqOfM0uNfy0SkpRhvUryYH0Z6wdMYcacYXQ=
221−go.yaml.in/yaml/v3 v3.0.4 h1:tfq32ie2Jv2UxXFdLJdh3jXuOzWiL1fo0bu/FbuKpbc=
190+go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw=
222−go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg=
191+go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg=
223golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
192golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
224golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
193golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
225golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
194golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
226−golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
195+golang.org/x/crypto v0.55.0 h1:+KWHjbgOaAQ66dh/YlkZKHlz9ZUlq61AFirAR9ntP8M=
227−golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
196+golang.org/x/crypto v0.55.0/go.mod h1:uq0V9dE/fzQuJtbnL+2EhWOE63vo164FY8xqEnV9xis=
228−golang.org/x/crypto v0.50.0 h1:zO47/JPrL6vsNkINmLoo/PH1gcxpls50DNogFvB5ZGI=
229−golang.org/x/crypto v0.50.0/go.mod h1:3muZ7vA7PBCE6xgPX7nkzzjiUq87kRItoJQM1Yo8S+Q=
230golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
197golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
231golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
198golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
232−golang.org/x/mod v0.34.0 h1:xIHgNUUnW6sYkcM5Jleh05DvLOtwc6RitGHbDk4akRI=
199+golang.org/x/mod v0.40.0 h1:hUv+3cXcdRHz08UmSiOob7sadHig73uo5bkXxQ/tvUs=
233−golang.org/x/mod v0.34.0/go.mod h1:ykgH52iCZe79kzLLMhyCUzhMci+nQj+0XkbXpNYtVjY=
200+golang.org/x/mod v0.40.0/go.mod h1:0/weTWkPWGBikyTWAX3dkjVztMmBA5hM0DH6BElSupE=
234−golang.org/x/mod v0.35.0 h1:Ww1D637e6Pg+Zb2KrWfHQUnH2dQRLBQyAtpr/haaJeM=
235−golang.org/x/mod v0.35.0/go.mod h1:+GwiRhIInF8wPm+4AoT6L0FA1QWAad3OMdTRx4tFYlU=
236golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
201golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
237golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
202golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
238golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
203golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
239golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
204golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
240golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
205golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
241−golang.org/x/net v0.52.0 h1:He/TN1l0e4mmR3QqHMT2Xab3Aj3L9qjbhRm78/6jrW0=
206+golang.org/x/net v0.58.0 h1:ynWG7rqYi4ccpTEuPZ2QGWHktVEM9DMCj9yzDE0Q7To=
242−golang.org/x/net v0.52.0/go.mod h1:R1MAz7uMZxVMualyPXb+VaqGSa3LIaUqk0eEt3w36Sw=
207+golang.org/x/net v0.58.0/go.mod h1:YwCddHnFlT7eLQqVprV19OnhLGtc5xOKgE0RyqgfWAU=
243−golang.org/x/net v0.53.0 h1:d+qAbo5L0orcWAr0a9JweQpjXF19LMXJE8Ey7hwOdUA=
244−golang.org/x/net v0.53.0/go.mod h1:JvMuJH7rrdiCfbeHoo3fCQU24Lf5JJwT9W3sJFulfgs=
245golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
208golang.org/x/oauth2 v0.36.0 h1:peZ/1z27fi9hUOFCAZaHyrpWG5lwe0RJEEEeH0ThlIs=
246golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
209golang.org/x/oauth2 v0.36.0/go.mod h1:YDBUJMTkDnJS+A4BP4eZBjCqtokkg1hODuPjwiGPO7Q=
247golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
210golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
248golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
211golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
249golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
212golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
250−golang.org/x/sync v0.20.0 h1:e0PTpb7pjO8GAtTs2dQ6jYa5BWYlMuX047Dco/pItO4=
213+golang.org/x/sync v0.22.0 h1:SZjpbeLmrCk4xhRSZFNZW5gFUeCeFgjekvI/+gfScek=
251−golang.org/x/sync v0.20.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
214+golang.org/x/sync v0.22.0/go.mod h1:9xrNwdLfx4jkKbNva9FpL6vEN7evnE43NNNJQ2LF3+0=
252golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
215golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
253golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
216golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
254golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
217golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
@@ -258,10 +221,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc
258golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
221golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
259golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
222golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
260golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
223golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
261−golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
224+golang.org/x/sys v0.47.0 h1:o7XGOvZQCADBQQ4Y7VNq2dRWQR7JmOUW8Kxx4ZsNgWs=
262−golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
263−golang.org/x/sys v0.43.0 h1:Rlag2XtaFTxp19wS8MXlJwTvoh8ArU6ezoyFsMyCTNI=
264−golang.org/x/sys v0.43.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
225+golang.org/x/sys v0.47.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
265golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
226golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
266golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
227golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
267golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
228golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
@@ -273,44 +234,31 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
273golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
234golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
274golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
235golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
275golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
236golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
276−golang.org/x/text v0.35.0 h1:JOVx6vVDFokkpaq1AEptVzLTpDe9KGpj5tR4/X+ybL8=
237+golang.org/x/text v0.41.0 h1:vz/seA0lnX87Othu2f/0L24RcgrXD9/YFTSuGjj3rH8=
277−golang.org/x/text v0.35.0/go.mod h1:khi/HExzZJ2pGnjenulevKNX1W67CUy0AsXcNubPGCA=
238+golang.org/x/text v0.41.0/go.mod h1:jvf1O8ajNzZqhSrQBPbutR/EB83Cc0CFrezNQIwbb5M=
278−golang.org/x/text v0.36.0 h1:JfKh3XmcRPqZPKevfXVpI1wXPTqbkE5f7JA92a55Yxg=
279−golang.org/x/text v0.36.0/go.mod h1:NIdBknypM8iqVmPiuco0Dh6P5Jcdk8lJL0CUebqK164=
280golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
239golang.org/x/time v0.15.0 h1:bbrp8t3bGUeFOx08pvsMYRTCVSMk89u4tKbNOZbp88U=
281golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
240golang.org/x/time v0.15.0/go.mod h1:Y4YMaQmXwGQZoFaVFk4YpCt4FLQMYKZe9oeV/f4MSno=
282golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
241golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
283golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
242golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
284golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
243golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
285golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
244golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
286−golang.org/x/tools v0.43.0 h1:12BdW9CeB3Z+J/I/wj34VMl8X+fEXBxVR90JeMX5E7s=
245+golang.org/x/tools v0.49.0 h1:3NI7VXzL9+1WZD52Dx2ttoPwD5DWrFGpl9mFZDlmisI=
287−golang.org/x/tools v0.43.0/go.mod h1:uHkMso649BX2cZK6+RpuIPXS3ho2hZo4FVwfoy1vIk0=
246+golang.org/x/tools v0.49.0/go.mod h1:SJNXV9DBKT0UbdttsQjbfJlAE/q+y36++zo3uL3N0Oo=
288−golang.org/x/tools v0.44.0 h1:UP4ajHPIcuMjT1GqzDWRlalUEoY+uzoZKnhOjbIPD2c=
289−golang.org/x/tools v0.44.0/go.mod h1:KA0AfVErSdxRZIsOVipbv3rQhVXTnlU6UhKxHd1seDI=
290golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
247golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
291−gonum.org/v1/gonum v0.16.0 h1:5+ul4Swaf3ESvrOnidPp4GZbzf0mxVQpDCYUQE7OJfk=
292−gonum.org/v1/gonum v0.16.0/go.mod h1:fef3am4MQ93R2HHpKnLk4/Tbh/s0+wqD5nfa6Pnwy4E=
293gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
248gonum.org/v1/gonum v0.17.0 h1:VbpOemQlsSMrYmn7T2OUvQ4dqxQXU+ouZFQsZOx50z4=
294−google.golang.org/api v0.272.0 h1:eLUQZGnAS3OHn31URRf9sAmRk3w2JjMx37d2k8AjJmA=
249+gonum.org/v1/gonum v0.17.0/go.mod h1:El3tOrEuMpv2UdMrbNlKEh9vd86bmQ6vqIcDwxEOc1E=
295−google.golang.org/api v0.272.0/go.mod h1:wKjowi5LNJc5qarNvDCvNQBn3rVK8nSy6jg2SwRwzIA=
250+google.golang.org/api v0.293.0 h1:p9XIWOf63U4OgYx120ZwVU8+vl4XTPmWfgVPnmOAS9w=
296−google.golang.org/api v0.275.0 h1:vfY5d9vFVJeWEZT65QDd9hbndr7FyZ2+6mIzGAh71NI=
251+google.golang.org/api v0.293.0/go.mod h1:6n5tjEB1gzwniZTepZ0g5u+wM7Bof5GeULCx/zh8ZE0=
297−google.golang.org/api v0.275.0/go.mod h1:Fnag/EWUPIcJXuIkP1pjoTgS5vdxlk3eeemL7Do6bvw=
298−google.golang.org/genproto v0.0.0-20260316180232-0b37fe3546d5 h1:JNfk58HZ8lfmXbYK2vx/UvsqIL59TzByCxPIX4TDmsE=
299−google.golang.org/genproto v0.0.0-20260316180232-0b37fe3546d5/go.mod h1:x5julN69+ED4PcFk/XWayw35O0lf/nGa4aNgODCmNmw=
300google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0=
252google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7 h1:XzmzkmB14QhVhgnawEVsOn6OFsnpyxNPRY9QV01dNB0=
301−google.golang.org/genproto/googleapis/api v0.0.0-20260316180232-0b37fe3546d5 h1:CogIeEXn4qWYzzQU0QqvYBM8yDF9cFYzDq9ojSpv0Js=
253+google.golang.org/genproto v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:L43LFes82YgSonw6iTXTxXUX1OlULt4AQtkik4ULL/I=
302−google.golang.org/genproto/googleapis/api v0.0.0-20260316180232-0b37fe3546d5/go.mod h1:EIQZ5bFCfRQDV4MhRle7+OgjNtZ6P1PiZBgAKuxXu/Y=
254+google.golang.org/genproto/googleapis/api v0.0.0-20260630182238-925bb5da69e7 h1:jQ9p21COKWjP3VwuFrNRiiOTMh3mPpN45R7SLrH/HUU=
303−google.golang.org/genproto/googleapis/api v0.0.0-20260319201613-d00831a3d3e7 h1:41r6JMbpzBMen0R/4TZeeAmGXSJC7DftGINUodzTkPI=
255+google.golang.org/genproto/googleapis/api v0.0.0-20260630182238-925bb5da69e7/go.mod h1:KqHwBx2upmfa1XSi1WuRvC+2VGCLtooKkfmyvRbUmqA=
304−google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7 h1:ndE4FoJqsIceKP2oYSnUZqhTdYufCYYkqwtFzfrhI7w=
256+google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1 h1:utmqiUzZAEINbLmRyAt1QjVkMyLQ+lvIwCpLaB1rZfw=
305−google.golang.org/genproto/googleapis/rpc v0.0.0-20260319201613-d00831a3d3e7/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
306−google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d h1:wT2n40TBqFY6wiwazVK9/iTWbsQrgk5ZfCSVFLO9LQA=
307−google.golang.org/genproto/googleapis/rpc v0.0.0-20260406210006-6f92a3bedf2d/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
257+google.golang.org/genproto/googleapis/rpc v0.0.0-20260817212433-ac3dfec99bb1/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8=
308−google.golang.org/grpc v1.79.3 h1:sybAEdRIEtvcD68Gx7dmnwjZKlyfuc61Dyo9pGXXkKE=
258+google.golang.org/grpc v1.83.0 h1:JeNZEKJFbQxArAMl+hiytHauacDNqJUllNfmIMmpqnQ=
309−google.golang.org/grpc v1.79.3/go.mod h1:KmT0Kjez+0dde/v2j9vzwoAScgEPx/Bw1CYChhHLrHQ=
259+google.golang.org/grpc v1.83.0/go.mod h1:kDyl6SKsiHKt0uylY5gtn5cEjkrIOhQOGDgIc4JGwzQ=
310−google.golang.org/grpc v1.80.0 h1:Xr6m2WmWZLETvUNvIUmeD5OAagMw3FiKmMlTdViWsHM=
260+google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc=
311−google.golang.org/grpc v1.80.0/go.mod h1:ho/dLnxwi3EDJA4Zghp7k2Ec1+c2jqup0bFkw07bwF4=
312−google.golang.org/protobuf v1.36.11 h1:fV6ZwhNocDyBLK0dj+fg8ektcVegBBuEolpbTQyBNVE=
313−google.golang.org/protobuf v1.36.11/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
261+google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco=
314gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
262gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
315gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
263gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c h1:Hei/4ADfdWqJk1ZMxUNpqntNwaWcugrBjAiHlqqRiVk=
316gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
264gopkg.in/check.v1 v1.0.0-20201130134442-10cb98267c6c/go.mod h1:JHkPIbrfpd72SG/EVd6muEfDQjcINNoR0C8j2r3qZ4Q=
@@ -319,5 +267,5 @@ gopkg.in/natefinch/lumberjack.v2 v2.2.1/go.mod h1:YD8tP3GAjkrDg1eZH7EGmyESg/lsYs
319gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
267gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
320gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
268gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
321gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
269gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
322−k8s.io/apimachinery v0.35.3 h1:MeaUwQCV3tjKP4bcwWGgZ/cp/vpsRnQzqO6J6tJyoF8=
270+k8s.io/apimachinery v0.36.3 h1:PkzMRBRG8joFD8EhCuQAtNPvJlxb82FwplP26HIzvAM=
323−k8s.io/apimachinery v0.35.3/go.mod h1:jQCgFZFR1F4Ik7hvr2g84RTJSZegBc8yHgFWKn//hns=
271+k8s.io/apimachinery v0.36.3/go.mod h1:cTSjBWgPe/6CQyBKzY/hDIRWCQQQeK0mfLbml0UYFHE=
oauthproxy.go+2−2
@@ -587,7 +587,7 @@ func (p *OAuthProxy) ErrorPage(rw http.ResponseWriter, req *http.Request, code i
587
587
588// IsAllowedRequest is used to check if auth should be skipped for this request
588// IsAllowedRequest is used to check if auth should be skipped for this request
589func (p *OAuthProxy) IsAllowedRequest(req *http.Request) bool {
589func (p *OAuthProxy) IsAllowedRequest(req *http.Request) bool {
590− isPreflightRequestAllowed := p.skipAuthPreflight && req.Method == "OPTIONS"
590+ isPreflightRequestAllowed := p.skipAuthPreflight && req.Method == http.MethodOptions
591 return isPreflightRequestAllowed || p.isAllowedRoute(req) || p.isTrustedIP(req)
591 return isPreflightRequestAllowed || p.isAllowedRoute(req) || p.isTrustedIP(req)
592}
592}
593
593
@@ -669,7 +669,7 @@ func (p *OAuthProxy) SignInPage(rw http.ResponseWriter, req *http.Request, code
669
669
670// ManualSignIn handles basic auth logins to the proxy
670// ManualSignIn handles basic auth logins to the proxy
671func (p *OAuthProxy) ManualSignIn(req *http.Request) (string, bool, int) {
671func (p *OAuthProxy) ManualSignIn(req *http.Request) (string, bool, int) {
672− if req.Method != "POST" || p.basicAuthValidator == nil {
672+ if req.Method != http.MethodPost || p.basicAuthValidator == nil {
673 return "", false, http.StatusOK
673 return "", false, http.StatusOK
674 }
674 }
675 user := req.FormValue("username")
675 user := req.FormValue("username")
oauthproxy_test.go+75−75
@@ -54,7 +54,7 @@ func TestRobotsTxt(t *testing.T) {
54 t.Fatal(err)
54 t.Fatal(err)
55 }
55 }
56 rw := httptest.NewRecorder()
56 rw := httptest.NewRecorder()
57− req, _ := http.NewRequest("GET", "/robots.txt", nil)
57+ req, _ := http.NewRequest(http.MethodGet, "/robots.txt", nil)
58 proxy.ServeHTTP(rw, req)
58 proxy.ServeHTTP(rw, req)
59 assert.Equal(t, 200, rw.Code)
59 assert.Equal(t, 200, rw.Code)
60 assert.Equal(t, "User-agent: *\nDisallow: /\n", rw.Body.String())
60 assert.Equal(t, "User-agent: *\nDisallow: /\n", rw.Body.String())
@@ -241,7 +241,7 @@ func TestBasicAuthPassword(t *testing.T) {
241
241
242 // Save the required session
242 // Save the required session
243 rw := httptest.NewRecorder()
243 rw := httptest.NewRecorder()
244− req, _ := http.NewRequest("GET", "/", nil)
244+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
245 err = proxy.sessionStore.Save(rw, req, &sessions.SessionState{
245 err = proxy.sessionStore.Save(rw, req, &sessions.SessionState{
246 Email: emailAddress,
246 Email: emailAddress,
247 })
247 })
@@ -250,7 +250,7 @@ func TestBasicAuthPassword(t *testing.T) {
250 // Extract the cookie value to inject into the test request
250 // Extract the cookie value to inject into the test request
251 cookie := rw.Header().Values("Set-Cookie")[0]
251 cookie := rw.Header().Values("Set-Cookie")[0]
252
252
253− req, _ = http.NewRequest("GET", "/", nil)
253+ req, _ = http.NewRequest(http.MethodGet, "/", nil)
254 req.Header.Set("Cookie", cookie)
254 req.Header.Set("Cookie", cookie)
255 rw = httptest.NewRecorder()
255 rw = httptest.NewRecorder()
256 proxy.ServeHTTP(rw, req)
256 proxy.ServeHTTP(rw, req)
@@ -300,14 +300,14 @@ func TestPassGroupsHeadersWithGroups(t *testing.T) {
300
300
301 // Save the required session
301 // Save the required session
302 rw := httptest.NewRecorder()
302 rw := httptest.NewRecorder()
303− req, _ := http.NewRequest("GET", "/", nil)
303+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
304 err = proxy.sessionStore.Save(rw, req, session)
304 err = proxy.sessionStore.Save(rw, req, session)
305 assert.NoError(t, err)
305 assert.NoError(t, err)
306
306
307 // Extract the cookie value to inject into the test request
307 // Extract the cookie value to inject into the test request
308 cookie := rw.Header().Values("Set-Cookie")[0]
308 cookie := rw.Header().Values("Set-Cookie")[0]
309
309
310− req, _ = http.NewRequest("GET", "/", nil)
310+ req, _ = http.NewRequest(http.MethodGet, "/", nil)
311 req.Header.Set("Cookie", cookie)
311 req.Header.Set("Cookie", cookie)
312 rw = httptest.NewRecorder()
312 rw = httptest.NewRecorder()
313 proxy.ServeHTTP(rw, req)
313 proxy.ServeHTTP(rw, req)
@@ -457,7 +457,7 @@ func (patTest *PassAccessTokenTest) getEndpointWithCookie(cookie string, endpoin
457 return 0, ""
457 return 0, ""
458 }
458 }
459
459
460− req, err := http.NewRequest("GET", endpoint, strings.NewReader(""))
460+ req, err := http.NewRequest(http.MethodGet, endpoint, strings.NewReader(""))
461 if err != nil {
461 if err != nil {
462 return 0, ""
462 return 0, ""
463 }
463 }
@@ -608,7 +608,7 @@ func NewSignInPageTest(skipProvider bool) (*SignInPageTest, error) {
608
608
609func (sipTest *SignInPageTest) GetEndpoint(endpoint string) (int, string) {
609func (sipTest *SignInPageTest) GetEndpoint(endpoint string) (int, string) {
610 rw := httptest.NewRecorder()
610 rw := httptest.NewRecorder()
611− req, _ := http.NewRequest("GET", endpoint, strings.NewReader(""))
611+ req, _ := http.NewRequest(http.MethodGet, endpoint, strings.NewReader(""))
612 sipTest.proxy.ServeHTTP(rw, req)
612 sipTest.proxy.ServeHTTP(rw, req)
613 return rw.Code, rw.Body.String()
613 return rw.Code, rw.Body.String()
614}
614}
@@ -894,7 +894,7 @@ func NewProcessCookieTest(opts ProcessCookieTestOpts, modifiers ...OptionsModifi
894 // access_token validation.
894 // access_token validation.
895 pcTest.proxy.CookieOptions.Refresh = time.Duration(0)
895 pcTest.proxy.CookieOptions.Refresh = time.Duration(0)
896 pcTest.rw = httptest.NewRecorder()
896 pcTest.rw = httptest.NewRecorder()
897− pcTest.req, _ = http.NewRequest("GET", "/", strings.NewReader(""))
897+ pcTest.req, _ = http.NewRequest(http.MethodGet, "/", strings.NewReader(""))
898 pcTest.validateUser = true
898 pcTest.validateUser = true
899 return &pcTest, nil
899 return &pcTest, nil
900}
900}
@@ -1027,7 +1027,7 @@ func NewUserInfoEndpointTest() (*ProcessCookieTest, error) {
1027 if err != nil {
1027 if err != nil {
1028 return nil, err
1028 return nil, err
1029 }
1029 }
1030− pcTest.req, _ = http.NewRequest("GET",
1030+ pcTest.req, _ = http.NewRequest(http.MethodGet,
1031 pcTest.opts.ProxyPrefix+"/userinfo", nil)
1031 pcTest.opts.ProxyPrefix+"/userinfo", nil)
1032 return pcTest, nil
1032 return pcTest, nil
1033}
1033}
@@ -1135,7 +1135,7 @@ func NewAuthOnlyEndpointTest(querystring string, modifiers ...OptionsModifier) (
1135 return nil, err
1135 return nil, err
1136 }
1136 }
1137 pcTest.req, _ = http.NewRequest(
1137 pcTest.req, _ = http.NewRequest(
1138− "GET",
1138+ http.MethodGet,
1139 fmt.Sprintf("%s/auth%s", pcTest.opts.ProxyPrefix, querystring),
1139 fmt.Sprintf("%s/auth%s", pcTest.opts.ProxyPrefix, querystring),
1140 nil)
1140 nil)
1141 return pcTest, nil
1141 return pcTest, nil
@@ -1274,7 +1274,7 @@ func TestAuthOnlyEndpointSetXAuthRequestHeaders(t *testing.T) {
1274 pcTest.validateUser = true
1274 pcTest.validateUser = true
1275
1275
1276 pcTest.rw = httptest.NewRecorder()
1276 pcTest.rw = httptest.NewRecorder()
1277− pcTest.req, _ = http.NewRequest("GET",
1277+ pcTest.req, _ = http.NewRequest(http.MethodGet,
1278 pcTest.opts.ProxyPrefix+authOnlyPath, nil)
1278 pcTest.opts.ProxyPrefix+authOnlyPath, nil)
1279
1279
1280 created := time.Now()
1280 created := time.Now()
@@ -1367,7 +1367,7 @@ func TestAuthOnlyEndpointSetBasicAuthTrueRequestHeaders(t *testing.T) {
1367 pcTest.validateUser = true
1367 pcTest.validateUser = true
1368
1368
1369 pcTest.rw = httptest.NewRecorder()
1369 pcTest.rw = httptest.NewRecorder()
1370− pcTest.req, _ = http.NewRequest("GET",
1370+ pcTest.req, _ = http.NewRequest(http.MethodGet,
1371 pcTest.opts.ProxyPrefix+authOnlyPath, nil)
1371 pcTest.opts.ProxyPrefix+authOnlyPath, nil)
1372
1372
1373 created := time.Now()
1373 created := time.Now()
@@ -1447,7 +1447,7 @@ func TestAuthOnlyEndpointSetBasicAuthFalseRequestHeaders(t *testing.T) {
1447 pcTest.validateUser = true
1447 pcTest.validateUser = true
1448
1448
1449 pcTest.rw = httptest.NewRecorder()
1449 pcTest.rw = httptest.NewRecorder()
1450− pcTest.req, _ = http.NewRequest("GET",
1450+ pcTest.req, _ = http.NewRequest(http.MethodGet,
1451 pcTest.opts.ProxyPrefix+authOnlyPath, nil)
1451 pcTest.opts.ProxyPrefix+authOnlyPath, nil)
1452
1452
1453 created := time.Now()
1453 created := time.Now()
@@ -1495,7 +1495,7 @@ func TestAuthSkippedForPreflightRequests(t *testing.T) {
1495 }
1495 }
1496 proxy.provider = NewTestProvider(upstreamURL, "")
1496 proxy.provider = NewTestProvider(upstreamURL, "")
1497 rw := httptest.NewRecorder()
1497 rw := httptest.NewRecorder()
1498− req, _ := http.NewRequest("OPTIONS", "/preflight-request", nil)
1498+ req, _ := http.NewRequest(http.MethodOptions, "/preflight-request", nil)
1499 proxy.ServeHTTP(rw, req)
1499 proxy.ServeHTTP(rw, req)
1500
1500
1501 assert.Equal(t, 200, rw.Code)
1501 assert.Equal(t, 200, rw.Code)
@@ -1652,19 +1652,19 @@ func TestRequestSignature(t *testing.T) {
1652 resp string
1652 resp string
1653 }{
1653 }{
1654 "No request signature": {
1654 "No request signature": {
1655− method: "GET",
1655+ method: http.MethodGet,
1656 body: "",
1656 body: "",
1657 key: "",
1657 key: "",
1658 resp: "no signature received",
1658 resp: "no signature received",
1659 },
1659 },
1660 "Get request": {
1660 "Get request": {
1661− method: "GET",
1661+ method: http.MethodGet,
1662 body: "",
1662 body: "",
1663 key: "7d9e1aa87a5954e6f9fc59266b3af9d7c35fda2d",
1663 key: "7d9e1aa87a5954e6f9fc59266b3af9d7c35fda2d",
1664 resp: "signatures match",
1664 resp: "signatures match",
1665 },
1665 },
1666 "Post request": {
1666 "Post request": {
1667− method: "POST",
1667+ method: http.MethodPost,
1668 body: `{ "hello": "world!" }`,
1668 body: `{ "hello": "world!" }`,
1669 key: "d90df39e2d19282840252612dd7c81421a372f61",
1669 key: "d90df39e2d19282840252612dd7c81421a372f61",
1670 resp: "signatures match",
1670 resp: "signatures match",
@@ -2189,7 +2189,7 @@ func TestTrustedIPs(t *testing.T) {
2189 reverseProxy: false,
2189 reverseProxy: false,
2190 realClientIPHeader: "X-Real-IP", // Default value
2190 realClientIPHeader: "X-Real-IP", // Default value
2191 req: func() *http.Request {
2191 req: func() *http.Request {
2192− req, _ := http.NewRequest("GET", "/", nil)
2192+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
2193 return req
2193 return req
2194 }(),
2194 }(),
2195 expectTrusted: false,
2195 expectTrusted: false,
@@ -2201,7 +2201,7 @@ func TestTrustedIPs(t *testing.T) {
2201 reverseProxy: false,
2201 reverseProxy: false,
2202 realClientIPHeader: "X-Real-IP",
2202 realClientIPHeader: "X-Real-IP",
2203 req: func() *http.Request {
2203 req: func() *http.Request {
2204− req, _ := http.NewRequest("GET", "/", nil)
2204+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
2205 req.RemoteAddr = "@"
2205 req.RemoteAddr = "@"
2206 return req
2206 return req
2207 }(),
2207 }(),
@@ -2214,7 +2214,7 @@ func TestTrustedIPs(t *testing.T) {
2214 reverseProxy: false,
2214 reverseProxy: false,
2215 realClientIPHeader: "X-Real-IP",
2215 realClientIPHeader: "X-Real-IP",
2216 req: func() *http.Request {
2216 req: func() *http.Request {
2217− req, _ := http.NewRequest("GET", "/", nil)
2217+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
2218 req.RemoteAddr = "@"
2218 req.RemoteAddr = "@"
2219 return req
2219 return req
2220 }(),
2220 }(),
@@ -2227,7 +2227,7 @@ func TestTrustedIPs(t *testing.T) {
2227 reverseProxy: false,
2227 reverseProxy: false,
2228 realClientIPHeader: "X-Real-IP", // Default value
2228 realClientIPHeader: "X-Real-IP", // Default value
2229 req: func() *http.Request {
2229 req: func() *http.Request {
2230− req, _ := http.NewRequest("GET", "/", nil)
2230+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
2231 req.RemoteAddr = "127.0.0.1:43670"
2231 req.RemoteAddr = "127.0.0.1:43670"
2232 return req
2232 return req
2233 }(),
2233 }(),
@@ -2240,7 +2240,7 @@ func TestTrustedIPs(t *testing.T) {
2240 reverseProxy: true,
2240 reverseProxy: true,
2241 realClientIPHeader: "X-Real-IP", // Default value
2241 realClientIPHeader: "X-Real-IP", // Default value
2242 req: func() *http.Request {
2242 req: func() *http.Request {
2243− req, _ := http.NewRequest("GET", "/", nil)
2243+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
2244 req.RemoteAddr = "127.0.0.1:44324"
2244 req.RemoteAddr = "127.0.0.1:44324"
2245 return req
2245 return req
2246 }(),
2246 }(),
@@ -2253,7 +2253,7 @@ func TestTrustedIPs(t *testing.T) {
2253 reverseProxy: true,
2253 reverseProxy: true,
2254 realClientIPHeader: "X-Forwarded-For",
2254 realClientIPHeader: "X-Forwarded-For",
2255 req: func() *http.Request {
2255 req: func() *http.Request {
2256− req, _ := http.NewRequest("GET", "/", nil)
2256+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
2257 req.Header.Add("X-Forwarded-For", "127.0.0.1")
2257 req.Header.Add("X-Forwarded-For", "127.0.0.1")
2258 return req
2258 return req
2259 }(),
2259 }(),
@@ -2266,7 +2266,7 @@ func TestTrustedIPs(t *testing.T) {
2266 reverseProxy: true,
2266 reverseProxy: true,
2267 realClientIPHeader: "X-Forwarded-For",
2267 realClientIPHeader: "X-Forwarded-For",
2268 req: func() *http.Request {
2268 req: func() *http.Request {
2269− req, _ := http.NewRequest("GET", "/", nil)
2269+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
2270 req.Header.Add("X-Forwarded-For", "::1")
2270 req.Header.Add("X-Forwarded-For", "::1")
2271 return req
2271 return req
2272 }(),
2272 }(),
@@ -2279,7 +2279,7 @@ func TestTrustedIPs(t *testing.T) {
2279 reverseProxy: true,
2279 reverseProxy: true,
2280 realClientIPHeader: "X-Forwarded-For",
2280 realClientIPHeader: "X-Forwarded-For",
2281 req: func() *http.Request {
2281 req: func() *http.Request {
2282− req, _ := http.NewRequest("GET", "/", nil)
2282+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
2283 req.Header.Add("X-Forwarded-For", "12.34.56.78")
2283 req.Header.Add("X-Forwarded-For", "12.34.56.78")
2284 return req
2284 return req
2285 }(),
2285 }(),
@@ -2292,7 +2292,7 @@ func TestTrustedIPs(t *testing.T) {
2292 reverseProxy: true,
2292 reverseProxy: true,
2293 realClientIPHeader: "X-Forwarded-For",
2293 realClientIPHeader: "X-Forwarded-For",
2294 req: func() *http.Request {
2294 req: func() *http.Request {
2295− req, _ := http.NewRequest("GET", "/", nil)
2295+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
2296 req.Header.Add("X-Forwarded-For", "::2")
2296 req.Header.Add("X-Forwarded-For", "::2")
2297 return req
2297 return req
2298 }(),
2298 }(),
@@ -2305,7 +2305,7 @@ func TestTrustedIPs(t *testing.T) {
2305 reverseProxy: true,
2305 reverseProxy: true,
2306 realClientIPHeader: "X-Forwarded-For",
2306 realClientIPHeader: "X-Forwarded-For",
2307 req: func() *http.Request {
2307 req: func() *http.Request {
2308− req, _ := http.NewRequest("GET", "/", nil)
2308+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
2309 req.Header.Add("X-Real-IP", "::1")
2309 req.Header.Add("X-Real-IP", "::1")
2310 return req
2310 return req
2311 }(),
2311 }(),
@@ -2318,7 +2318,7 @@ func TestTrustedIPs(t *testing.T) {
2318 reverseProxy: true,
2318 reverseProxy: true,
2319 realClientIPHeader: "X-Forwarded-For",
2319 realClientIPHeader: "X-Forwarded-For",
2320 req: func() *http.Request {
2320 req: func() *http.Request {
2321− req, _ := http.NewRequest("GET", "/", nil)
2321+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
2322 req.Header.Add("X-Forwarded-For", "adsfljk29242as!!")
2322 req.Header.Add("X-Forwarded-For", "adsfljk29242as!!")
2323 return req
2323 return req
2324 }(),
2324 }(),
@@ -2331,7 +2331,7 @@ func TestTrustedIPs(t *testing.T) {
2331 reverseProxy: false,
2331 reverseProxy: false,
2332 realClientIPHeader: "X-Real-IP",
2332 realClientIPHeader: "X-Real-IP",
2333 req: func() *http.Request {
2333 req: func() *http.Request {
2334− req, _ := http.NewRequest("GET", "/", nil)
2334+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
2335 req.RemoteAddr = "adsfljk29242as!!"
2335 req.RemoteAddr = "adsfljk29242as!!"
2336 return req
2336 return req
2337 }(),
2337 }(),
@@ -2427,12 +2427,12 @@ func Test_buildRoutesAllowlist(t *testing.T) {
2427 },
2427 },
2428 expectedRoutes: []expectedAllowedRoute{
2428 expectedRoutes: []expectedAllowedRoute{
2429 {
2429 {
2430− method: "GET",
2430+ method: http.MethodGet,
2431 negate: false,
2431 negate: false,
2432 regexString: "^/foo/bar",
2432 regexString: "^/foo/bar",
2433 },
2433 },
2434 {
2434 {
2435− method: "POST",
2435+ method: http.MethodPost,
2436 negate: false,
2436 negate: false,
2437 regexString: "^/baz/[0-9]+/thing",
2437 regexString: "^/baz/[0-9]+/thing",
2438 },
2438 },
@@ -2485,11 +2485,11 @@ func Test_buildRoutesAllowlist(t *testing.T) {
2485 regexString: "^/baz/[0-9]+/thing/regex",
2485 regexString: "^/baz/[0-9]+/thing/regex",
2486 },
2486 },
2487 {
2487 {
2488− method: "GET",
2488+ method: http.MethodGet,
2489 regexString: "^/foo/bar",
2489 regexString: "^/foo/bar",
2490 },
2490 },
2491 {
2491 {
2492− method: "POST",
2492+ method: http.MethodPost,
2493 regexString: "^/baz/[0-9]+/thing",
2493 regexString: "^/baz/[0-9]+/thing",
2494 },
2494 },
2495 {
2495 {
@@ -2641,7 +2641,7 @@ func TestApiRoutes(t *testing.T) {
2641
2641
2642 for _, tc := range testCases {
2642 for _, tc := range testCases {
2643 t.Run(tc.name, func(t *testing.T) {
2643 t.Run(tc.name, func(t *testing.T) {
2644− req, err := http.NewRequest("GET", tc.url, nil)
2644+ req, err := http.NewRequest(http.MethodGet, tc.url, nil)
2645 req.Header.Set("Accept", tc.contentType)
2645 req.Header.Set("Accept", tc.contentType)
2646 assert.NoError(t, err)
2646 assert.NoError(t, err)
2647
2647
@@ -2700,37 +2700,37 @@ func TestAllowedRequest(t *testing.T) {
2700 }{
2700 }{
2701 {
2701 {
2702 name: "Regex GET allowed",
2702 name: "Regex GET allowed",
2703− method: "GET",
2703+ method: http.MethodGet,
2704 url: "/skip/auth/regex",
2704 url: "/skip/auth/regex",
2705 allowed: true,
2705 allowed: true,
2706 },
2706 },
2707 {
2707 {
2708 name: "Regex POST allowed ",
2708 name: "Regex POST allowed ",
2709− method: "POST",
2709+ method: http.MethodPost,
2710 url: "/skip/auth/regex",
2710 url: "/skip/auth/regex",
2711 allowed: true,
2711 allowed: true,
2712 },
2712 },
2713 {
2713 {
2714 name: "Regex denied",
2714 name: "Regex denied",
2715− method: "GET",
2715+ method: http.MethodGet,
2716 url: "/wrong/denied",
2716 url: "/wrong/denied",
2717 allowed: false,
2717 allowed: false,
2718 },
2718 },
2719 {
2719 {
2720 name: "Regex allowed with fragment-free path",
2720 name: "Regex allowed with fragment-free path",
2721− method: "GET",
2721+ method: http.MethodGet,
2722 url: "/public/legit/endpoint",
2722 url: "/public/legit/endpoint",
2723 allowed: true,
2723 allowed: true,
2724 },
2724 },
2725 {
2725 {
2726 name: "Regex denied when path contains encoded fragment suffix",
2726 name: "Regex denied when path contains encoded fragment suffix",
2727− method: "GET",
2727+ method: http.MethodGet,
2728 url: "/public/secret%23/endpoint",
2728 url: "/public/secret%23/endpoint",
2729 allowed: false,
2729 allowed: false,
2730 },
2730 },
2731 {
2731 {
2732 name: "Route allowed",
2732 name: "Route allowed",
2733− method: "GET",
2733+ method: http.MethodGet,
2734 url: "/skip/auth/routes/get",
2734 url: "/skip/auth/routes/get",
2735 allowed: true,
2735 allowed: true,
2736 },
2736 },
@@ -2742,25 +2742,25 @@ func TestAllowedRequest(t *testing.T) {
2742 },
2742 },
2743 {
2743 {
2744 name: "Route denied with wrong path",
2744 name: "Route denied with wrong path",
2745− method: "GET",
2745+ method: http.MethodGet,
2746 url: "/skip/auth/routes/wrong/path",
2746 url: "/skip/auth/routes/wrong/path",
2747 allowed: false,
2747 allowed: false,
2748 },
2748 },
2749 {
2749 {
2750 name: "Route denied with wrong path and method",
2750 name: "Route denied with wrong path and method",
2751− method: "POST",
2751+ method: http.MethodPost,
2752 url: "/skip/auth/routes/wrong/path",
2752 url: "/skip/auth/routes/wrong/path",
2753 allowed: false,
2753 allowed: false,
2754 },
2754 },
2755 {
2755 {
2756 name: "Route allowed with fragment-free path",
2756 name: "Route allowed with fragment-free path",
2757− method: "GET",
2757+ method: http.MethodGet,
2758 url: "/foo/public/bar",
2758 url: "/foo/public/bar",
2759 allowed: true,
2759 allowed: true,
2760 },
2760 },
2761 {
2761 {
2762 name: "Route denied when path contains encoded fragment suffix",
2762 name: "Route denied when path contains encoded fragment suffix",
2763− method: "GET",
2763+ method: http.MethodGet,
2764 url: "/foo/secret%23/bar",
2764 url: "/foo/secret%23/bar",
2765 allowed: false,
2765 allowed: false,
2766 },
2766 },
@@ -2825,37 +2825,37 @@ func TestAllowedRequestWithForwardedUriHeader(t *testing.T) {
2825 }{
2825 }{
2826 {
2826 {
2827 name: "Regex GET allowed",
2827 name: "Regex GET allowed",
2828− method: "GET",
2828+ method: http.MethodGet,
2829 url: "/skip/auth/regex",
2829 url: "/skip/auth/regex",
2830 allowed: true,
2830 allowed: true,
2831 },
2831 },
2832 {
2832 {
2833 name: "Regex POST allowed ",
2833 name: "Regex POST allowed ",
2834− method: "POST",
2834+ method: http.MethodPost,
2835 url: "/skip/auth/regex",
2835 url: "/skip/auth/regex",
2836 allowed: true,
2836 allowed: true,
2837 },
2837 },
2838 {
2838 {
2839 name: "Regex denied",
2839 name: "Regex denied",
2840− method: "GET",
2840+ method: http.MethodGet,
2841 url: "/wrong/denied",
2841 url: "/wrong/denied",
2842 allowed: false,
2842 allowed: false,
2843 },
2843 },
2844 {
2844 {
2845 name: "Regex allowed with fragment-free path",
2845 name: "Regex allowed with fragment-free path",
2846− method: "GET",
2846+ method: http.MethodGet,
2847 url: "/public/legit/endpoint",
2847 url: "/public/legit/endpoint",
2848 allowed: true,
2848 allowed: true,
2849 },
2849 },
2850 {
2850 {
2851 name: "Regex denied when X-Forwarded-Uri contains an encoded fragment suffix",
2851 name: "Regex denied when X-Forwarded-Uri contains an encoded fragment suffix",
2852− method: "GET",
2852+ method: http.MethodGet,
2853 url: "/public/secret%23/endpoint",
2853 url: "/public/secret%23/endpoint",
2854 allowed: false,
2854 allowed: false,
2855 },
2855 },
2856 {
2856 {
2857 name: "Route allowed",
2857 name: "Route allowed",
2858− method: "GET",
2858+ method: http.MethodGet,
2859 url: "/skip/auth/routes/get",
2859 url: "/skip/auth/routes/get",
2860 allowed: true,
2860 allowed: true,
2861 },
2861 },
@@ -2867,25 +2867,25 @@ func TestAllowedRequestWithForwardedUriHeader(t *testing.T) {
2867 },
2867 },
2868 {
2868 {
2869 name: "Route denied with wrong path",
2869 name: "Route denied with wrong path",
2870− method: "GET",
2870+ method: http.MethodGet,
2871 url: "/skip/auth/routes/wrong/path",
2871 url: "/skip/auth/routes/wrong/path",
2872 allowed: false,
2872 allowed: false,
2873 },
2873 },
2874 {
2874 {
2875 name: "Route denied with wrong path and method",
2875 name: "Route denied with wrong path and method",
2876− method: "POST",
2876+ method: http.MethodPost,
2877 url: "/skip/auth/routes/wrong/path",
2877 url: "/skip/auth/routes/wrong/path",
2878 allowed: false,
2878 allowed: false,
2879 },
2879 },
2880 {
2880 {
2881 name: "Route allowed with fragment-free path",
2881 name: "Route allowed with fragment-free path",
2882− method: "GET",
2882+ method: http.MethodGet,
2883 url: "/foo/public/bar",
2883 url: "/foo/public/bar",
2884 allowed: true,
2884 allowed: true,
2885 },
2885 },
2886 {
2886 {
2887 name: "Route denied when X-Forwarded-Uri contains an encoded fragment suffix",
2887 name: "Route denied when X-Forwarded-Uri contains an encoded fragment suffix",
2888− method: "GET",
2888+ method: http.MethodGet,
2889 url: "/foo/secret%23/bar",
2889 url: "/foo/secret%23/bar",
2890 allowed: false,
2890 allowed: false,
2891 },
2891 },
@@ -2986,37 +2986,37 @@ func TestAllowedRequestNegateWithoutMethod(t *testing.T) {
2986 }{
2986 }{
2987 {
2987 {
2988 name: "Some static file allowed",
2988 name: "Some static file allowed",
2989− method: "GET",
2989+ method: http.MethodGet,
2990 url: "/static/file.txt",
2990 url: "/static/file.txt",
2991 allowed: true,
2991 allowed: true,
2992 },
2992 },
2993 {
2993 {
2994 name: "POST to contact form allowed",
2994 name: "POST to contact form allowed",
2995− method: "POST",
2995+ method: http.MethodPost,
2996 url: "/contact",
2996 url: "/contact",
2997 allowed: true,
2997 allowed: true,
2998 },
2998 },
2999 {
2999 {
3000 name: "Regex POST allowed",
3000 name: "Regex POST allowed",
3001− method: "POST",
3001+ method: http.MethodPost,
3002 url: "/api/public-entity",
3002 url: "/api/public-entity",
3003 allowed: true,
3003 allowed: true,
3004 },
3004 },
3005 {
3005 {
3006 name: "Regex POST with trailing slash allowed",
3006 name: "Regex POST with trailing slash allowed",
3007− method: "POST",
3007+ method: http.MethodPost,
3008 url: "/api/public-entity/",
3008 url: "/api/public-entity/",
3009 allowed: true,
3009 allowed: true,
3010 },
3010 },
3011 {
3011 {
3012 name: "Regex GET api route denied",
3012 name: "Regex GET api route denied",
3013− method: "GET",
3013+ method: http.MethodGet,
3014 url: "/api/users",
3014 url: "/api/users",
3015 allowed: false,
3015 allowed: false,
3016 },
3016 },
3017 {
3017 {
3018 name: "Regex POST api route denied",
3018 name: "Regex POST api route denied",
3019− method: "POST",
3019+ method: http.MethodPost,
3020 url: "/api/users",
3020 url: "/api/users",
3021 allowed: false,
3021 allowed: false,
3022 },
3022 },
@@ -3086,37 +3086,37 @@ func TestAllowedRequestNegateWithMethod(t *testing.T) {
3086 }{
3086 }{
3087 {
3087 {
3088 name: "Some static file allowed",
3088 name: "Some static file allowed",
3089− method: "GET",
3089+ method: http.MethodGet,
3090 url: "/static/file.txt",
3090 url: "/static/file.txt",
3091 allowed: true,
3091 allowed: true,
3092 },
3092 },
3093 {
3093 {
3094 name: "POST to contact form not allowed",
3094 name: "POST to contact form not allowed",
3095− method: "POST",
3095+ method: http.MethodPost,
3096 url: "/contact",
3096 url: "/contact",
3097 allowed: false,
3097 allowed: false,
3098 },
3098 },
3099 {
3099 {
3100 name: "Regex POST allowed",
3100 name: "Regex POST allowed",
3101− method: "POST",
3101+ method: http.MethodPost,
3102 url: "/api/public-entity",
3102 url: "/api/public-entity",
3103 allowed: true,
3103 allowed: true,
3104 },
3104 },
3105 {
3105 {
3106 name: "Regex POST with trailing slash allowed",
3106 name: "Regex POST with trailing slash allowed",
3107− method: "POST",
3107+ method: http.MethodPost,
3108 url: "/api/public-entity/",
3108 url: "/api/public-entity/",
3109 allowed: true,
3109 allowed: true,
3110 },
3110 },
3111 {
3111 {
3112 name: "Regex GET api route denied",
3112 name: "Regex GET api route denied",
3113− method: "GET",
3113+ method: http.MethodGet,
3114 url: "/api/users",
3114 url: "/api/users",
3115 allowed: false,
3115 allowed: false,
3116 },
3116 },
3117 {
3117 {
3118 name: "Regex POST api route denied",
3118 name: "Regex POST api route denied",
3119− method: "POST",
3119+ method: http.MethodPost,
3120 url: "/api/users",
3120 url: "/api/users",
3121 allowed: false,
3121 allowed: false,
3122 },
3122 },
@@ -3256,7 +3256,7 @@ func TestProxyAllowedGroups(t *testing.T) {
3256 t.Fatal(err)
3256 t.Fatal(err)
3257 }
3257 }
3258
3258
3259− test.req, _ = http.NewRequest("GET", fmt.Sprintf("/%s", tt.querystring), nil)
3259+ test.req, _ = http.NewRequest(http.MethodGet, fmt.Sprintf("/%s", tt.querystring), nil)
3260
3260
3261 test.req.Header.Add("accept", applicationJSON)
3261 test.req.Header.Add("accept", applicationJSON)
3262 err = test.SaveSession(session)
3262 err = test.SaveSession(session)
@@ -3400,7 +3400,7 @@ func TestAuthOnlyAllowedGroupsWithSkipMethods(t *testing.T) {
3400 {
3400 {
3401 name: "UserWithGroupSkipAuthPreflight",
3401 name: "UserWithGroupSkipAuthPreflight",
3402 groups: []string{"a", "c"},
3402 groups: []string{"a", "c"},
3403− method: "OPTIONS",
3403+ method: http.MethodOptions,
3404 ip: "1.2.3.5:43670",
3404 ip: "1.2.3.5:43670",
3405 withSession: true,
3405 withSession: true,
3406 expectedStatusCode: http.StatusAccepted,
3406 expectedStatusCode: http.StatusAccepted,
@@ -3408,7 +3408,7 @@ func TestAuthOnlyAllowedGroupsWithSkipMethods(t *testing.T) {
3408 {
3408 {
3409 name: "UserWithGroupTrustedIp",
3409 name: "UserWithGroupTrustedIp",
3410 groups: []string{"a", "c"},
3410 groups: []string{"a", "c"},
3411− method: "GET",
3411+ method: http.MethodGet,
3412 ip: "1.2.3.4:43670",
3412 ip: "1.2.3.4:43670",
3413 withSession: true,
3413 withSession: true,
3414 expectedStatusCode: http.StatusAccepted,
3414 expectedStatusCode: http.StatusAccepted,
@@ -3416,7 +3416,7 @@ func TestAuthOnlyAllowedGroupsWithSkipMethods(t *testing.T) {
3416 {
3416 {
3417 name: "UserWithoutGroupSkipAuthPreflight",
3417 name: "UserWithoutGroupSkipAuthPreflight",
3418 groups: []string{"c"},
3418 groups: []string{"c"},
3419− method: "OPTIONS",
3419+ method: http.MethodOptions,
3420 ip: "1.2.3.5:43670",
3420 ip: "1.2.3.5:43670",
3421 withSession: true,
3421 withSession: true,
3422 expectedStatusCode: http.StatusForbidden,
3422 expectedStatusCode: http.StatusForbidden,
@@ -3424,21 +3424,21 @@ func TestAuthOnlyAllowedGroupsWithSkipMethods(t *testing.T) {
3424 {
3424 {
3425 name: "UserWithoutGroupTrustedIp",
3425 name: "UserWithoutGroupTrustedIp",
3426 groups: []string{"c"},
3426 groups: []string{"c"},
3427− method: "GET",
3427+ method: http.MethodGet,
3428 ip: "1.2.3.4:43670",
3428 ip: "1.2.3.4:43670",
3429 withSession: true,
3429 withSession: true,
3430 expectedStatusCode: http.StatusForbidden,
3430 expectedStatusCode: http.StatusForbidden,
3431 },
3431 },
3432 {
3432 {
3433 name: "UserWithoutSessionSkipAuthPreflight",
3433 name: "UserWithoutSessionSkipAuthPreflight",
3434− method: "OPTIONS",
3434+ method: http.MethodOptions,
3435 ip: "1.2.3.5:43670",
3435 ip: "1.2.3.5:43670",
3436 withSession: false,
3436 withSession: false,
3437 expectedStatusCode: http.StatusAccepted,
3437 expectedStatusCode: http.StatusAccepted,
3438 },
3438 },
3439 {
3439 {
3440 name: "UserWithoutSessionTrustedIp",
3440 name: "UserWithoutSessionTrustedIp",
3441− method: "GET",
3441+ method: http.MethodGet,
3442 ip: "1.2.3.4:43670",
3442 ip: "1.2.3.4:43670",
3443 withSession: false,
3443 withSession: false,
3444 expectedStatusCode: http.StatusAccepted,
3444 expectedStatusCode: http.StatusAccepted,
@@ -3790,14 +3790,14 @@ func TestIdTokenPlaceholderInSignOut(t *testing.T) {
3790
3790
3791 // Save the required session
3791 // Save the required session
3792 rw := httptest.NewRecorder()
3792 rw := httptest.NewRecorder()
3793− req, _ := http.NewRequest("GET", "/", nil)
3793+ req, _ := http.NewRequest(http.MethodGet, "/", nil)
3794 err = proxy.sessionStore.Save(rw, req, session)
3794 err = proxy.sessionStore.Save(rw, req, session)
3795 assert.NoError(t, err)
3795 assert.NoError(t, err)
3796
3796
3797 rw = httptest.NewRecorder()
3797 rw = httptest.NewRecorder()
3798
3798
3799 rdUrl := url.QueryEscape("https://my-oidc-provider.example.com/sign_out_page?id_token_hint={id_token}&post_logout_redirect_uri=https://my-app.example.com/")
3799 rdUrl := url.QueryEscape("https://my-oidc-provider.example.com/sign_out_page?id_token_hint={id_token}&post_logout_redirect_uri=https://my-app.example.com/")
3800− req, _ = http.NewRequest("GET", "/oauth2/sign_out?rd="+rdUrl, nil)
3800+ req, _ = http.NewRequest(http.MethodGet, "/oauth2/sign_out?rd="+rdUrl, nil)
3801 req = middlewareapi.AddRequestScope(req, &middlewareapi.RequestScope{
3801 req = middlewareapi.AddRequestScope(req, &middlewareapi.RequestScope{
3802 RequestID: "11111111-2222-4333-8444-555555555555",
3802 RequestID: "11111111-2222-4333-8444-555555555555",
3803 Session: session,
3803 Session: session,
pkg/apis/options/load.go+1−1
@@ -145,7 +145,7 @@ func loadAndSubstituteEnvs(configFileName string) ([]byte, error) {
145func registerFlags(v *viper.Viper, prefix string, flagSet *pflag.FlagSet, options interface{}) error {
145func registerFlags(v *viper.Viper, prefix string, flagSet *pflag.FlagSet, options interface{}) error {
146 val := reflect.ValueOf(options)
146 val := reflect.ValueOf(options)
147 var typ reflect.Type
147 var typ reflect.Type
148− if val.Kind() == reflect.Ptr {
148+ if val.Kind() == reflect.Pointer {
149 typ = val.Elem().Type()
149 typ = val.Elem().Type()
150 } else {
150 } else {
151 typ = val.Type()
151 typ = val.Type()
pkg/apis/options/load_test.go+1−1
@@ -282,7 +282,7 @@ var _ = Describe("Load", func() {
282 Entry("with an invalid config file", &testOptionsTableInput{
282 Entry("with an invalid config file", &testOptionsTableInput{
283 configFile: []byte(`slice_option = foo`),
283 configFile: []byte(`slice_option = foo`),
284 flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
284 flagSet: func() *pflag.FlagSet { return testOptionsFlagSet },
285− expectedErr: fmt.Errorf("unable to load config file: While parsing config: toml: expected 'false'"),
285+ expectedErr: fmt.Errorf("unable to load config file: While parsing config: toml: expected keyword \"false\""),
286 expectedOutput: &TestOptions{},
286 expectedOutput: &TestOptions{},
287 }),
287 }),
288 Entry("with an invalid flagset", &testOptionsTableInput{
288 Entry("with an invalid flagset", &testOptionsTableInput{
pkg/apis/options/providers.go+6
@@ -151,6 +151,9 @@ const (
151 // ADFSProvider is the provider type for ADFS
151 // ADFSProvider is the provider type for ADFS
152 ADFSProvider ProviderType = "adfs"
152 ADFSProvider ProviderType = "adfs"
153
153
154+ // AtlassianProvider is the provider type for Atlassian Cloud
155+ AtlassianProvider ProviderType = "atlassian"
156+
154 // AzureProvider is the provider type for Azure
157 // AzureProvider is the provider type for Azure
155 AzureProvider ProviderType = "azure"
158 AzureProvider ProviderType = "azure"
156
159
@@ -166,6 +169,9 @@ const (
166 // DigitalOceanProvider is the provider type for DigitalOcean
169 // DigitalOceanProvider is the provider type for DigitalOcean
167 DigitalOceanProvider ProviderType = "digitalocean"
170 DigitalOceanProvider ProviderType = "digitalocean"
168
171
172+ // DTTSSOProvider is the provider type for the DTT SSO
173+ DTTSSOProvider ProviderType = "dtt-sso"
174+
169 // FacebookProvider is the provider type for Facebook
175 // FacebookProvider is the provider type for Facebook
170 FacebookProvider ProviderType = "facebook"
176 FacebookProvider ProviderType = "facebook"
171
177
pkg/apis/options/upstreams.go+7−5
@@ -62,12 +62,14 @@ type Upstream struct {
62
62
63 // Path is used to map requests to the upstream server.
63 // Path is used to map requests to the upstream server.
64 // The closest match will take precedence and all Paths must be unique.
64 // The closest match will take precedence and all Paths must be unique.
65− // Path can also take a pattern when used with RewriteTarget.
65+ // Without RewriteTarget, Path follows gorilla/mux route syntax.
66− // Path segments can be captured and matched using regular experessions.
67 // Eg:
66 // Eg:
68− // - `^/foo$`: Match only the explicit path `/foo`
67+ // - `/foo`: Match only the explicit path `/foo`
69− // - `^/bar/$`: Match any path prefixed with `/bar/`
68+ // - `/bar/`: Match any path prefixed with `/bar/`
69+ // - `/baz/{path:.*}`: Match any path prefixed with `/baz/`
70+ // When RewriteTarget is set, Path is treated as a regular expression.
71+ // Eg: `^/baz/(.*)$` matches any path prefixed with `/baz` and captures the
70− // - `^/baz/(.*)$`: Match any path prefixed with `/baz` and capture the remaining path for use with RewriteTarget
72+ // remaining path for use with RewriteTarget.
71 Path string `yaml:"path,omitempty"`
73 Path string `yaml:"path,omitempty"`
72
74
73 // RewriteTarget allows users to rewrite the request path before it is sent to
75 // RewriteTarget allows users to rewrite the request path before it is sent to
pkg/authentication/hmacauth/hmacauth_test.go+1−1
@@ -286,7 +286,7 @@ func TestSendAuthenticatedPostRequestToServer(t *testing.T) {
286 upstream := httptest.NewServer(
286 upstream := httptest.NewServer(
287 http.HandlerFunc(authenticator.Authenticate))
287 http.HandlerFunc(authenticator.Authenticate))
288
288
289− req, err := http.NewRequest("POST", upstream.URL+"/foo/bar",
289+ req, err := http.NewRequest(http.MethodPost, upstream.URL+"/foo/bar",
290 io.NopCloser(&fakeNetConn{reqBody: payload}))
290 io.NopCloser(&fakeNetConn{reqBody: payload}))
291 if err != nil {
291 if err != nil {
292 panic(err)
292 panic(err)
pkg/requests/builder_test.go+15−15
@@ -31,7 +31,7 @@ var _ = Describe("Builder suite", func() {
31
31
32 Context("with a basic request", func() {
32 Context("with a basic request", func() {
33 assertSuccessfulRequest(getBuilder, testHTTPRequest{
33 assertSuccessfulRequest(getBuilder, testHTTPRequest{
34− Method: "GET",
34+ Method: http.MethodGet,
35 Header: baseHeaders,
35 Header: baseHeaders,
36 Body: []byte{},
36 Body: []byte{},
37 RequestURI: "/json/path",
37 RequestURI: "/json/path",
@@ -52,7 +52,7 @@ var _ = Describe("Builder suite", func() {
52 })
52 })
53
53
54 assertSuccessfulRequest(getBuilder, testHTTPRequest{
54 assertSuccessfulRequest(getBuilder, testHTTPRequest{
55− Method: "GET",
55+ Method: http.MethodGet,
56 Header: baseHeaders,
56 Header: baseHeaders,
57 Body: []byte{},
57 Body: []byte{},
58 RequestURI: "/json/path",
58 RequestURI: "/json/path",
@@ -78,7 +78,7 @@ var _ = Describe("Builder suite", func() {
78 })
78 })
79
79
80 assertSuccessfulRequest(getBuilder, testHTTPRequest{
80 assertSuccessfulRequest(getBuilder, testHTTPRequest{
81− Method: "GET",
81+ Method: http.MethodGet,
82 Header: header,
82 Header: header,
83 Body: []byte(body),
83 Body: []byte(body),
84 RequestURI: "/json/path",
84 RequestURI: "/json/path",
@@ -93,11 +93,11 @@ var _ = Describe("Builder suite", func() {
93
93
94 BeforeEach(func() {
94 BeforeEach(func() {
95 buf := bytes.NewBuffer([]byte(body))
95 buf := bytes.NewBuffer([]byte(body))
96− b = b.WithMethod("POST").WithBody(buf)
96+ b = b.WithMethod(http.MethodPost).WithBody(buf)
97 })
97 })
98
98
99 assertSuccessfulRequest(getBuilder, testHTTPRequest{
99 assertSuccessfulRequest(getBuilder, testHTTPRequest{
100− Method: "POST",
100+ Method: http.MethodPost,
101 Header: header,
101 Header: header,
102 Body: []byte(body),
102 Body: []byte(body),
103 RequestURI: "/json/path",
103 RequestURI: "/json/path",
@@ -109,11 +109,11 @@ var _ = Describe("Builder suite", func() {
109 header.Set("Content-Length", "0")
109 header.Set("Content-Length", "0")
110
110
111 BeforeEach(func() {
111 BeforeEach(func() {
112− b = b.WithMethod("POST")
112+ b = b.WithMethod(http.MethodPost)
113 })
113 })
114
114
115 assertSuccessfulRequest(getBuilder, testHTTPRequest{
115 assertSuccessfulRequest(getBuilder, testHTTPRequest{
116− Method: "POST",
116+ Method: http.MethodPost,
117 Header: header,
117 Header: header,
118 Body: []byte{},
118 Body: []byte{},
119 RequestURI: "/json/path",
119 RequestURI: "/json/path",
@@ -122,11 +122,11 @@ var _ = Describe("Builder suite", func() {
122
122
123 Context("OPTIONS", func() {
123 Context("OPTIONS", func() {
124 BeforeEach(func() {
124 BeforeEach(func() {
125− b = b.WithMethod("OPTIONS")
125+ b = b.WithMethod(http.MethodOptions)
126 })
126 })
127
127
128 assertSuccessfulRequest(getBuilder, testHTTPRequest{
128 assertSuccessfulRequest(getBuilder, testHTTPRequest{
129− Method: "OPTIONS",
129+ Method: http.MethodOptions,
130 Header: baseHeaders,
130 Header: baseHeaders,
131 Body: []byte{},
131 Body: []byte{},
132 RequestURI: "/json/path",
132 RequestURI: "/json/path",
@@ -152,7 +152,7 @@ var _ = Describe("Builder suite", func() {
152 })
152 })
153
153
154 assertSuccessfulRequest(getBuilder, testHTTPRequest{
154 assertSuccessfulRequest(getBuilder, testHTTPRequest{
155− Method: "GET",
155+ Method: http.MethodGet,
156 Header: header,
156 Header: header,
157 Body: []byte{},
157 Body: []byte{},
158 RequestURI: "/json/path",
158 RequestURI: "/json/path",
@@ -170,7 +170,7 @@ var _ = Describe("Builder suite", func() {
170 })
170 })
171
171
172 assertSuccessfulRequest(getBuilder, testHTTPRequest{
172 assertSuccessfulRequest(getBuilder, testHTTPRequest{
173− Method: "GET",
173+ Method: http.MethodGet,
174 Header: replacementHeaders,
174 Header: replacementHeaders,
175 Body: []byte{},
175 Body: []byte{},
176 RequestURI: "/json/path",
176 RequestURI: "/json/path",
@@ -190,7 +190,7 @@ var _ = Describe("Builder suite", func() {
190 })
190 })
191
191
192 assertSuccessfulRequest(getBuilder, testHTTPRequest{
192 assertSuccessfulRequest(getBuilder, testHTTPRequest{
193− Method: "GET",
193+ Method: http.MethodGet,
194 Header: replacementHeaders,
194 Header: replacementHeaders,
195 Body: []byte{},
195 Body: []byte{},
196 RequestURI: "/json/path",
196 RequestURI: "/json/path",
@@ -205,7 +205,7 @@ var _ = Describe("Builder suite", func() {
205 })
205 })
206
206
207 assertSuccessfulRequest(getBuilder, testHTTPRequest{
207 assertSuccessfulRequest(getBuilder, testHTTPRequest{
208− Method: "GET",
208+ Method: http.MethodGet,
209 Header: header,
209 Header: header,
210 Body: []byte{},
210 Body: []byte{},
211 RequestURI: "/json/path",
211 RequestURI: "/json/path",
@@ -219,12 +219,12 @@ var _ = Describe("Builder suite", func() {
219 result := b.Do()
219 result := b.Do()
220 Expect(result.Error()).ToNot(HaveOccurred())
220 Expect(result.Error()).ToNot(HaveOccurred())
221
221
222− b.WithMethod("POST")
222+ b.WithMethod(http.MethodPost)
223 })
223 })
224
224
225 Context("should not redo the request", func() {
225 Context("should not redo the request", func() {
226 assertSuccessfulRequest(getBuilder, testHTTPRequest{
226 assertSuccessfulRequest(getBuilder, testHTTPRequest{
227− Method: "GET",
227+ Method: http.MethodGet,
228 Header: baseHeaders,
228 Header: baseHeaders,
229 Body: []byte{},
229 Body: []byte{},
230 RequestURI: "/json/path",
230 RequestURI: "/json/path",
pkg/upstream/http.go+55−16
@@ -123,7 +123,7 @@ func (t *unixRoundTripper) RoundTrip(req *http.Request) (*http.Response, error)
123// The proxy should render an error page if there are failures connecting to the
123// The proxy should render an error page if there are failures connecting to the
124// upstream server.
124// upstream server.
125func newReverseProxy(target *url.URL, upstream options.Upstream, errorHandler ProxyErrorHandler) http.Handler {
125func newReverseProxy(target *url.URL, upstream options.Upstream, errorHandler ProxyErrorHandler) http.Handler {
126− proxy := httputil.NewSingleHostReverseProxy(target)
126+ proxy := newSingleHostReverseProxy(target)
127
127
128 // Inherit default transport options from Go's stdlib
128 // Inherit default transport options from Go's stdlib
129 transport := http.DefaultTransport.(*http.Transport).Clone()
129 transport := http.DefaultTransport.(*http.Transport).Clone()
@@ -155,7 +155,7 @@ func newReverseProxy(target *url.URL, upstream options.Upstream, errorHandler Pr
155 }
155 }
156
156
157 // Ensure we always pass the original request path
157 // Ensure we always pass the original request path
158− setProxyDirector(proxy)
158+ setProxyRewrite(proxy)
159
159
160 // TODO (@tuunit) - this should be inverted or get a better name in the future to set the upstream host header
160 // TODO (@tuunit) - this should be inverted or get a better name in the future to set the upstream host header
161 // only if PassHostHeader is explicitly set to true. Currently this would be a breaking change.
161 // only if PassHostHeader is explicitly set to true. Currently this would be a breaking change.
@@ -179,32 +179,71 @@ func newReverseProxy(target *url.URL, upstream options.Upstream, errorHandler Pr
179 return proxy
179 return proxy
180}
180}
181
181
182+func newSingleHostReverseProxy(target *url.URL) *httputil.ReverseProxy {
183+ return &httputil.ReverseProxy{
184+ Rewrite: func(proxyReq *httputil.ProxyRequest) {
185+ proxyReq.SetURL(target)
186+ proxyReq.Out.Host = proxyReq.In.Host
187+ setProxyForwardingHeaders(proxyReq)
188+ },
189+ }
190+}
191+
182−// setProxyUpstreamHostHeader sets the proxy.Director so that upstream requests
192+// setProxyUpstreamHostHeader sets the proxy.Rewrite so that upstream requests
183// receive a host header matching the target URL.
193// receive a host header matching the target URL.
184func setProxyUpstreamHostHeader(proxy *httputil.ReverseProxy, target *url.URL) {
194func setProxyUpstreamHostHeader(proxy *httputil.ReverseProxy, target *url.URL) {
185− director := proxy.Director
195+ rewrite := proxy.Rewrite
186− proxy.Director = func(req *http.Request) {
196+ proxy.Rewrite = func(proxyReq *httputil.ProxyRequest) {
187− director(req)
197+ rewrite(proxyReq)
188− req.Host = target.Host
198+ proxyReq.Out.Host = target.Host
189 }
199 }
190}
200}
191
201
192−// setProxyDirector sets the proxy.Director so that request URIs are escaped
202+// setProxyRewrite sets the proxy.Rewrite so that request URIs are escaped
193// when proxying to usptream servers.
203// when proxying to usptream servers.
194−func setProxyDirector(proxy *httputil.ReverseProxy) {
204+func setProxyRewrite(proxy *httputil.ReverseProxy) {
195− director := proxy.Director
205+ rewrite := proxy.Rewrite
196− proxy.Director = func(req *http.Request) {
206+ proxy.Rewrite = func(proxyReq *httputil.ProxyRequest) {
197− director(req)
207+ rewrite(proxyReq)
198 // use RequestURI so that we aren't unescaping encoded slashes in the request path
208 // use RequestURI so that we aren't unescaping encoded slashes in the request path
199− req.URL.Opaque = req.RequestURI
209+ proxyReq.Out.URL.Opaque = proxyReq.In.RequestURI
200− req.URL.RawQuery = ""
210+ proxyReq.Out.URL.RawQuery = ""
201− req.URL.ForceQuery = false
211+ proxyReq.Out.URL.ForceQuery = false
212+ }
213+}
214+
215+func setProxyForwardingHeaders(proxyReq *httputil.ProxyRequest) {
216+ // TODO (@tuunit): Preserve the legacy Director-based forwarding header behavior
217+ // for backwards compatibility. Harden this with saner defaults and/or
218+ // explicit flags in the future.
219+ for _, header := range []string{"Forwarded", "X-Forwarded-Host", "X-Forwarded-Proto"} {
220+ if values, ok := proxyReq.In.Header[header]; ok {
221+ proxyReq.Out.Header[header] = append([]string(nil), values...)
222+ }
223+ }
224+
225+ prior, ok := proxyReq.In.Header["X-Forwarded-For"]
226+ if ok {
227+ proxyReq.Out.Header["X-Forwarded-For"] = append([]string(nil), prior...)
228+ }
229+
230+ clientIP, _, err := net.SplitHostPort(proxyReq.In.RemoteAddr)
231+ if err != nil {
232+ return
233+ }
234+
235+ omit := ok && prior == nil
236+ if len(prior) > 0 {
237+ clientIP = strings.Join(prior, ", ") + ", " + clientIP
238+ }
239+ if !omit {
240+ proxyReq.Out.Header.Set("X-Forwarded-For", clientIP)
202 }
241 }
203}
242}
204
243
205// newWebSocketReverseProxy creates a new reverse proxy for proxying websocket connections.
244// newWebSocketReverseProxy creates a new reverse proxy for proxying websocket connections.
206func newWebSocketReverseProxy(u *url.URL, skipTLSVerify *bool, passHostHeader *bool) http.Handler {
245func newWebSocketReverseProxy(u *url.URL, skipTLSVerify *bool, passHostHeader *bool) http.Handler {
207− wsProxy := httputil.NewSingleHostReverseProxy(u)
246+ wsProxy := newSingleHostReverseProxy(u)
208
247
209 // Inherit default transport options from Go's stdlib
248 // Inherit default transport options from Go's stdlib
210 transport := http.DefaultTransport.(*http.Transport).Clone()
249 transport := http.DefaultTransport.(*http.Transport).Clone()
pkg/upstream/http_test.go+55−1
@@ -361,7 +361,12 @@ var _ = Describe("HTTP Upstream Suite", func() {
361 return http.HandlerFunc(func(_ http.ResponseWriter, req *http.Request) {
361 return http.HandlerFunc(func(_ http.ResponseWriter, req *http.Request) {
362 proxy, ok := h.(*httputil.ReverseProxy)
362 proxy, ok := h.(*httputil.ReverseProxy)
363 Expect(ok).To(BeTrue())
363 Expect(ok).To(BeTrue())
364− proxy.Director(req)
364+ outReq := req.Clone(req.Context())
365+ proxy.Rewrite(&httputil.ProxyRequest{
366+ In: req,
367+ Out: outReq,
368+ })
369+ req.Host = outReq.Host
365 })
370 })
366 }
371 }
367 httpUpstream.handler = requestInterceptor(httpUpstream.handler)
372 httpUpstream.handler = requestInterceptor(httpUpstream.handler)
@@ -370,6 +375,54 @@ var _ = Describe("HTTP Upstream Suite", func() {
370 Expect(req.Host).To(Equal(strings.TrimPrefix(serverAddr, "http://")))
375 Expect(req.Host).To(Equal(strings.TrimPrefix(serverAddr, "http://")))
371 })
376 })
372
377
378+ It("ServeHTTP preserves forwarding headers when using Rewrite", func() {
379+ req := httptest.NewRequest("", "http://example.localhost/foo", nil)
380+ req.RemoteAddr = "192.0.2.10:1234"
381+ req.Header.Set("Forwarded", "for=192.0.2.1;proto=https;host=example.localhost")
382+ req.Header.Set("X-Forwarded-Host", "forwarded.example.localhost")
383+ req.Header.Set("X-Forwarded-Proto", "https")
384+ req.Header.Set("X-Forwarded-For", "192.0.2.1")
385+ req = middlewareapi.AddRequestScope(req, &middlewareapi.RequestScope{})
386+ rw := httptest.NewRecorder()
387+
388+ upstream := options.Upstream{
389+ ID: "preserveForwardedHeaders",
390+ PassHostHeader: ptr.To(true),
391+ ProxyWebSockets: ptr.To(false),
392+ InsecureSkipTLSVerify: ptr.To(false),
393+ FlushInterval: &defaultFlushInterval,
394+ Timeout: &defaultTimeout,
395+ }
396+
397+ u, err := url.Parse(serverAddr)
398+ Expect(err).ToNot(HaveOccurred())
399+
400+ handler := newHTTPUpstreamProxy(upstream, u, nil, nil)
401+ httpUpstream, ok := handler.(*httpUpstreamProxy)
402+ Expect(ok).To(BeTrue())
403+
404+ requestInterceptor := func(h http.Handler) http.Handler {
405+ return http.HandlerFunc(func(_ http.ResponseWriter, req *http.Request) {
406+ proxy, ok := h.(*httputil.ReverseProxy)
407+ Expect(ok).To(BeTrue())
408+
409+ outReq := req.Clone(req.Context())
410+ proxy.Rewrite(&httputil.ProxyRequest{
411+ In: req,
412+ Out: outReq,
413+ })
414+
415+ Expect(outReq.Header.Values("Forwarded")).To(Equal([]string{"for=192.0.2.1;proto=https;host=example.localhost"}))
416+ Expect(outReq.Header.Values("X-Forwarded-Host")).To(Equal([]string{"forwarded.example.localhost"}))
417+ Expect(outReq.Header.Values("X-Forwarded-Proto")).To(Equal([]string{"https"}))
418+ Expect(outReq.Header.Values("X-Forwarded-For")).To(Equal([]string{"192.0.2.1, 192.0.2.10"}))
419+ })
420+ }
421+ httpUpstream.handler = requestInterceptor(httpUpstream.handler)
422+
423+ httpUpstream.ServeHTTP(rw, req)
424+ })
425+
373 type newUpstreamTableInput struct {
426 type newUpstreamTableInput struct {
374 proxyWebSockets bool
427 proxyWebSockets bool
375 flushInterval time.Duration
428 flushInterval time.Duration
@@ -405,6 +458,7 @@ var _ = Describe("HTTP Upstream Suite", func() {
405
458
406 proxy, ok := upstreamProxy.handler.(*httputil.ReverseProxy)
459 proxy, ok := upstreamProxy.handler.(*httputil.ReverseProxy)
407 Expect(ok).To(BeTrue())
460 Expect(ok).To(BeTrue())
461+ Expect(proxy.Rewrite).ToNot(BeNil())
408 Expect(proxy.FlushInterval).To(Equal(in.flushInterval))
462 Expect(proxy.FlushInterval).To(Equal(in.flushInterval))
409 transport, ok := proxy.Transport.(*http.Transport)
463 transport, ok := proxy.Transport.(*http.Transport)
410 Expect(ok).To(BeTrue())
464 Expect(ok).To(BeTrue())
pkg/upstream/proxy_test.go+1−1
@@ -79,7 +79,7 @@ var _ = Describe("Proxy Suite", func() {
79 {
79 {
80 ID: "bad-http-backend",
80 ID: "bad-http-backend",
81 Path: "/bad-http/",
81 Path: "/bad-http/",
82− URI: "http://::1",
82+ URI: invalidServer,
83 },
83 },
84 {
84 {
85 ID: "single-path-backend",
85 ID: "single-path-backend",
pkg/upstream/upstream_suite_test.go+1−1
@@ -24,7 +24,7 @@ var (
24 serverAddr string
24 serverAddr string
25 unixServer *httptest.Server
25 unixServer *httptest.Server
26 unixServerAddr string
26 unixServerAddr string
27− invalidServer = "http://::1"
27+ invalidServer = "http://127.0.0.1:1"
28)
28)
29
29
30func TestUpstreamSuite(t *testing.T) {
30func TestUpstreamSuite(t *testing.T) {
pkg/validation/sessions_test.go+2−2
@@ -193,8 +193,8 @@ var _ = Describe("Sessions", func() {
193 unreachableRedisDelMsg = "unable to delete the redis initialization key: dial tcp 127.0.0.1:65535: connect: connection refused"
193 unreachableRedisDelMsg = "unable to delete the redis initialization key: dial tcp 127.0.0.1:65535: connect: connection refused"
194 unreachableSentinelSetMsg = "unable to set a redis initialization key: redis: all sentinels specified in configuration are unreachable: redis: nil"
194 unreachableSentinelSetMsg = "unable to set a redis initialization key: redis: all sentinels specified in configuration are unreachable: redis: nil"
195 unrechableSentinelDelMsg = "unable to delete the redis initialization key: redis: all sentinels specified in configuration are unreachable: redis: nil"
195 unrechableSentinelDelMsg = "unable to delete the redis initialization key: redis: all sentinels specified in configuration are unreachable: redis: nil"
196− refusedSentinelSetMsg = "unable to set a redis initialization key: redis: all sentinels specified in configuration are unreachable: context deadline exceeded"
196+ refusedSentinelSetMsg = "unable to set a redis initialization key: redis: all sentinels specified in configuration are unreachable: dial tcp 127.0.0.1:65535: connect: connection refused"
197− refusedSentinelDelMsg = "unable to delete the redis initialization key: redis: all sentinels specified in configuration are unreachable: context deadline exceeded"
197+ refusedSentinelDelMsg = "unable to delete the redis initialization key: redis: all sentinels specified in configuration are unreachable: dial tcp 127.0.0.1:65535: connect: connection refused"
198 )
198 )
199
199
200 type redisStoreTableInput struct {
200 type redisStoreTableInput struct {
providers/atlassian-cloud.gonew file+99
@@ -0,0 +1,99 @@
1+package providers
2+
3+import (
4+ "context"
5+ "errors"
6+ "net/url"
7+
8+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
9+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests"
10+)
11+
12+// AtlassianProvider represents an Atlassian Cloud based Identity Provider
13+type AtlassianProvider struct {
14+ *ProviderData
15+}
16+
17+var _ Provider = (*AtlassianProvider)(nil)
18+
19+const (
20+ atlassianProviderName = "Atlassian"
21+ atlassianDefaultScope = "read:me"
22+ atlassianPrompt = "consent"
23+ atlassianAudience = "api.atlassian.com"
24+)
25+
26+var (
27+ atlassianDefaultLoginURL = &url.URL{
28+ Scheme: "https",
29+ Host: "auth.atlassian.com",
30+ Path: "/authorize",
31+ }
32+
33+ atlassianDefaultRedeemURL = &url.URL{
34+ Scheme: "https",
35+ Host: "auth.atlassian.com",
36+ Path: "/oauth/token",
37+ }
38+
39+ // atlassianDefaultProfileURL returns the signed-in user's profile.
40+ atlassianDefaultProfileURL = &url.URL{
41+ Scheme: "https",
42+ Host: "api.atlassian.com",
43+ Path: "/me",
44+ }
45+)
46+
47+// NewAtlassianProvider initiates a new AtlassianProvider
48+func NewAtlassianProvider(p *ProviderData) *AtlassianProvider {
49+ p.setProviderDefaults(providerDefaults{
50+ name: atlassianProviderName,
51+ loginURL: atlassianDefaultLoginURL,
52+ redeemURL: atlassianDefaultRedeemURL,
53+ profileURL: atlassianDefaultProfileURL,
54+ validateURL: atlassianDefaultProfileURL,
55+ scope: atlassianDefaultScope,
56+ })
57+ return &AtlassianProvider{ProviderData: p}
58+}
59+
60+// GetLoginURL adds the audience and prompt parameters Atlassian requires.
61+func (p *AtlassianProvider) GetLoginURL(redirectURI, state, _ string, extraParams url.Values) string {
62+ params := url.Values{}
63+ for k, v := range extraParams {
64+ params[k] = v
65+ }
66+ params.Del("approval_prompt")
67+ if params.Get("prompt") == "" {
68+ params.Set("prompt", atlassianPrompt)
69+ }
70+ params.Set("audience", atlassianAudience)
71+
72+ loginURL := makeLoginURL(p.ProviderData, redirectURI, state, params)
73+ return loginURL.String()
74+}
75+
76+// EnrichSession sets the session's email from the Atlassian profile.
77+func (p *AtlassianProvider) EnrichSession(ctx context.Context, s *sessions.SessionState) error {
78+ var profile struct {
79+ Email string `json:"email"`
80+ }
81+ err := requests.New(p.ProfileURL.String()).
82+ WithContext(ctx).
83+ WithHeaders(makeOIDCHeader(s.AccessToken)).
84+ Do().
85+ UnmarshalInto(&profile)
86+ if err != nil {
87+ return err
88+ }
89+ if profile.Email == "" {
90+ return errors.New("no email in Atlassian profile")
91+ }
92+ s.Email = profile.Email
93+ return nil
94+}
95+
96+// ValidateSession validates the AccessToken
97+func (p *AtlassianProvider) ValidateSession(ctx context.Context, s *sessions.SessionState) bool {
98+ return validateToken(ctx, p, s.AccessToken, makeOIDCHeader(s.AccessToken))
99+}
providers/atlassian-cloud_test.gonew file+93
@@ -0,0 +1,93 @@
1+package providers
2+
3+import (
4+ "context"
5+ "net/http"
6+ "net/http/httptest"
7+ "net/url"
8+ "testing"
9+
10+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
11+ . "github.com/onsi/gomega"
12+)
13+
14+func testAtlassianBackend(status int, body string) *httptest.Server {
15+ return httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
16+ if r.URL.Path != "/me" || r.Header.Get("Authorization") != "Bearer imaginary_access_token" {
17+ w.WriteHeader(http.StatusUnauthorized)
18+ return
19+ }
20+ w.WriteHeader(status)
21+ _, _ = w.Write([]byte(body))
22+ }))
23+}
24+
25+func testAtlassianProvider(backend *httptest.Server) *AtlassianProvider {
26+ p := NewAtlassianProvider(&ProviderData{})
27+ u, _ := url.Parse(backend.URL)
28+ updateURL(p.ProfileURL, u.Host)
29+ updateURL(p.ValidateURL, u.Host)
30+ return p
31+}
32+
33+func TestNewAtlassianProvider(t *testing.T) {
34+ g := NewWithT(t)
35+
36+ providerData := NewAtlassianProvider(&ProviderData{}).Data()
37+ g.Expect(providerData.ProviderName).To(Equal("Atlassian"))
38+ g.Expect(providerData.LoginURL.String()).To(Equal("https://auth.atlassian.com/authorize"))
39+ g.Expect(providerData.RedeemURL.String()).To(Equal("https://auth.atlassian.com/oauth/token"))
40+ g.Expect(providerData.ProfileURL.String()).To(Equal("https://api.atlassian.com/me"))
41+ g.Expect(providerData.ValidateURL.String()).To(Equal("https://api.atlassian.com/me"))
42+ g.Expect(providerData.Scope).To(Equal("read:me"))
43+}
44+
45+func TestAtlassianProviderGetLoginURL(t *testing.T) {
46+ g := NewWithT(t)
47+ p := NewAtlassianProvider(&ProviderData{ClientID: "client"})
48+
49+ loginURL, err := url.Parse(p.GetLoginURL("https://proxy/oauth2/callback", "state", "",
50+ url.Values{"approval_prompt": {"force"}}))
51+ g.Expect(err).ToNot(HaveOccurred())
52+
53+ q := loginURL.Query()
54+ g.Expect(q.Get("audience")).To(Equal("api.atlassian.com"))
55+ g.Expect(q.Get("prompt")).To(Equal("consent"))
56+ g.Expect(q.Has("approval_prompt")).To(BeFalse())
57+ g.Expect(q.Get("scope")).To(Equal("read:me"))
58+
59+ loginURL, err = url.Parse(p.GetLoginURL("https://proxy/oauth2/callback", "state", "",
60+ url.Values{"prompt": {"login"}}))
61+ g.Expect(err).ToNot(HaveOccurred())
62+ g.Expect(loginURL.Query().Get("prompt")).To(Equal("login"))
63+}
64+
65+func TestAtlassianProviderEnrichSession(t *testing.T) {
66+ g := NewWithT(t)
67+ b := testAtlassianBackend(http.StatusOK, `{"account_id":"abc","email":"user@example.com"}`)
68+ defer b.Close()
69+ p := testAtlassianProvider(b)
70+
71+ session := CreateAuthorizedSession()
72+ g.Expect(p.EnrichSession(context.Background(), session)).To(Succeed())
73+ g.Expect(session.Email).To(Equal("user@example.com"))
74+}
75+
76+func TestAtlassianProviderEnrichSessionWithoutEmail(t *testing.T) {
77+ g := NewWithT(t)
78+ b := testAtlassianBackend(http.StatusOK, `{"account_id":"abc"}`)
79+ defer b.Close()
80+ p := testAtlassianProvider(b)
81+
82+ g.Expect(p.EnrichSession(context.Background(), CreateAuthorizedSession())).ToNot(Succeed())
83+}
84+
85+func TestAtlassianProviderValidateSession(t *testing.T) {
86+ g := NewWithT(t)
87+ b := testAtlassianBackend(http.StatusOK, `{"email":"user@example.com"}`)
88+ defer b.Close()
89+ p := testAtlassianProvider(b)
90+
91+ g.Expect(p.ValidateSession(context.Background(), CreateAuthorizedSession())).To(BeTrue())
92+ g.Expect(p.ValidateSession(context.Background(), &sessions.SessionState{AccessToken: "wrong"})).To(BeFalse())
93+}
providers/azure.go+2−2
@@ -166,7 +166,7 @@ func (p *AzureProvider) Redeem(ctx context.Context, redirectURL, code, codeVerif
166
166
167 err = requests.New(p.RedeemURL.String()).
167 err = requests.New(p.RedeemURL.String()).
168 WithContext(ctx).
168 WithContext(ctx).
169− WithMethod("POST").
169+ WithMethod(http.MethodPost).
170 WithBody(bytes.NewBufferString(params.Encode())).
170 WithBody(bytes.NewBufferString(params.Encode())).
171 SetHeader("Content-Type", "application/x-www-form-urlencoded").
171 SetHeader("Content-Type", "application/x-www-form-urlencoded").
172 Do().
172 Do().
@@ -334,7 +334,7 @@ func (p *AzureProvider) redeemRefreshToken(ctx context.Context, s *sessions.Sess
334
334
335 err = requests.New(p.RedeemURL.String()).
335 err = requests.New(p.RedeemURL.String()).
336 WithContext(ctx).
336 WithContext(ctx).
337− WithMethod("POST").
337+ WithMethod(http.MethodPost).
338 WithBody(bytes.NewBufferString(params.Encode())).
338 WithBody(bytes.NewBufferString(params.Encode())).
339 SetHeader("Content-Type", "application/x-www-form-urlencoded").
339 SetHeader("Content-Type", "application/x-www-form-urlencoded").
340 Do().
340 Do().
providers/dtt_sso.gonew file+202
@@ -0,0 +1,202 @@
1+package providers
2+
3+import (
4+ "bytes"
5+ "context"
6+ "encoding/base64"
7+ "errors"
8+ "fmt"
9+ "net/http"
10+ "net/url"
11+ "time"
12+
13+ "github.com/golang-jwt/jwt/v5"
14+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
15+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests"
16+)
17+
18+// DTTSSOProvider signs users in with the DTT SSO (auth-server).
19+type DTTSSOProvider struct {
20+ *ProviderData
21+}
22+
23+var _ Provider = (*DTTSSOProvider)(nil)
24+
25+const (
26+ dttSSOProviderName = "DTT SSO"
27+ dttSSODefaultScope = "openid"
28+)
29+
30+var (
31+ dttSSODefaultLoginURL = &url.URL{
32+ Scheme: "https",
33+ Host: "auth.thebestagent.pro",
34+ Path: "/oauth/authorize",
35+ }
36+
37+ dttSSODefaultRedeemURL = &url.URL{
38+ Scheme: "https",
39+ Host: "auth.thebestagent.pro",
40+ Path: "/oauth/token",
41+ }
42+
43+ dttSSODefaultValidateURL = &url.URL{
44+ Scheme: "https",
45+ Host: "auth.thebestagent.pro",
46+ Path: "/oauth/check_token",
47+ }
48+)
49+
50+// NewDTTSSOProvider initiates a new DTTSSOProvider
51+func NewDTTSSOProvider(p *ProviderData) *DTTSSOProvider {
52+ p.setProviderDefaults(providerDefaults{
53+ name: dttSSOProviderName,
54+ loginURL: dttSSODefaultLoginURL,
55+ redeemURL: dttSSODefaultRedeemURL,
56+ profileURL: nil,
57+ validateURL: dttSSODefaultValidateURL,
58+ scope: dttSSODefaultScope,
59+ })
60+ return &DTTSSOProvider{ProviderData: p}
61+}
62+
63+// dttSSOTokenResponse is the SSO's /oauth/token response.
64+type dttSSOTokenResponse struct {
65+ AccessToken string `json:"access_token"`
66+ RefreshToken string `json:"refresh_token"`
67+ ExpiresIn int64 `json:"expires_in"`
68+ IDToken string `json:"id_token"`
69+}
70+
71+// dttSSOClaims holds the id_token claims the provider uses.
72+type dttSSOClaims struct {
73+ jwt.RegisteredClaims
74+ Email string `json:"email"`
75+ Role string `json:"role"`
76+ UserID string `json:"user_id"`
77+}
78+
79+// Redeem exchanges the authorization code for a session.
80+func (p *DTTSSOProvider) Redeem(ctx context.Context, redirectURL, code, _ string) (*sessions.SessionState, error) {
81+ if code == "" {
82+ return nil, ErrMissingCode
83+ }
84+ return p.requestTokens(ctx, url.Values{
85+ "grant_type": {"authorization_code"},
86+ "code": {code},
87+ "redirect_uri": {redirectURL},
88+ })
89+}
90+
91+// RefreshSession renews the tokens and re-reads the email and role.
92+func (p *DTTSSOProvider) RefreshSession(ctx context.Context, s *sessions.SessionState) (bool, error) {
93+ if s == nil || s.RefreshToken == "" {
94+ return false, nil
95+ }
96+ refreshed, err := p.requestTokens(ctx, url.Values{
97+ "grant_type": {"refresh_token"},
98+ "refresh_token": {s.RefreshToken},
99+ })
100+ if err != nil {
101+ return false, fmt.Errorf("unable to redeem refresh token: %v", err)
102+ }
103+ s.AccessToken = refreshed.AccessToken
104+ s.RefreshToken = refreshed.RefreshToken
105+ s.IDToken = refreshed.IDToken
106+ s.Email = refreshed.Email
107+ s.User = refreshed.User
108+ s.Groups = refreshed.Groups
109+ s.CreatedAt = refreshed.CreatedAt
110+ s.ExpiresOn = refreshed.ExpiresOn
111+ return true, nil
112+}
113+
114+// ValidateSession checks the access token with the SSO's check_token endpoint.
115+func (p *DTTSSOProvider) ValidateSession(ctx context.Context, s *sessions.SessionState) bool {
116+ return validateToken(ctx, p, s.AccessToken, makeOIDCHeader(s.AccessToken))
117+}
118+
119+// requestTokens calls the token endpoint and builds a session from the verified id_token.
120+//
121+// The SSO compares the Basic credentials byte for byte, so they are sent unescaped
122+// rather than through golang.org/x/oauth2, which URL-escapes them.
123+func (p *DTTSSOProvider) requestTokens(ctx context.Context, params url.Values) (*sessions.SessionState, error) {
124+ clientSecret, err := p.GetClientSecret()
125+ if err != nil {
126+ return nil, err
127+ }
128+ credentials := base64.StdEncoding.EncodeToString([]byte(p.ClientID + ":" + clientSecret))
129+
130+ result := requests.New(p.RedeemURL.String()).
131+ WithContext(ctx).
132+ WithMethod(http.MethodPost).
133+ WithBody(bytes.NewBufferString(params.Encode())).
134+ SetHeader("Content-Type", "application/x-www-form-urlencoded").
135+ SetHeader("Authorization", "Basic "+credentials).
136+ Do()
137+ if result.Error() != nil {
138+ return nil, result.Error()
139+ }
140+ if result.StatusCode() != http.StatusOK {
141+ return nil, fmt.Errorf("token request failed: status %d - %s", result.StatusCode(), result.Body())
142+ }
143+
144+ var token dttSSOTokenResponse
145+ if err := result.UnmarshalInto(&token); err != nil {
146+ return nil, err
147+ }
148+ if token.AccessToken == "" {
149+ return nil, errors.New("token response carried no access_token")
150+ }
151+ claims, err := p.verifyIDToken(token.IDToken, clientSecret)
152+ if err != nil {
153+ return nil, err
154+ }
155+
156+ s := &sessions.SessionState{
157+ AccessToken: token.AccessToken,
158+ RefreshToken: token.RefreshToken,
159+ IDToken: token.IDToken,
160+ Email: claims.Email,
161+ User: claims.UserID,
162+ }
163+ if claims.Role != "" {
164+ s.Groups = []string{claims.Role}
165+ }
166+ s.CreatedAtNow()
167+ if token.ExpiresIn > 0 {
168+ s.ExpiresIn(time.Duration(token.ExpiresIn) * time.Second)
169+ }
170+ return s, nil
171+}
172+
173+// verifyIDToken checks the id_token's HS256 signature (keyed on the client secret),
174+// issuer, audience and expiry.
175+//
176+// go-jose is not used: it rejects HMAC keys under 32 bytes, and SSO client secrets
177+// can be shorter.
178+func (p *DTTSSOProvider) verifyIDToken(raw, clientSecret string) (*dttSSOClaims, error) {
179+ if raw == "" {
180+ return nil, errors.New("token response carried no id_token; the scope must be exactly \"openid\"")
181+ }
182+ var claims dttSSOClaims
183+ _, err := jwt.ParseWithClaims(raw, &claims,
184+ func(*jwt.Token) (any, error) { return []byte(clientSecret), nil },
185+ jwt.WithValidMethods([]string{jwt.SigningMethodHS256.Alg()}),
186+ jwt.WithIssuer(p.issuer()),
187+ jwt.WithAudience(p.ClientID),
188+ jwt.WithExpirationRequired(),
189+ )
190+ if err != nil {
191+ return nil, fmt.Errorf("verifying id_token: %v", err)
192+ }
193+ if claims.Email == "" {
194+ return nil, errors.New("id_token carries no email")
195+ }
196+ return &claims, nil
197+}
198+
199+// issuer is the SSO's site host, the origin of the login URL.
200+func (p *DTTSSOProvider) issuer() string {
201+ return p.LoginURL.Scheme + "://" + p.LoginURL.Host
202+}
providers/dtt_sso_test.gonew file+237
@@ -0,0 +1,237 @@
1+package providers
2+
3+import (
4+ "context"
5+ "encoding/base64"
6+ "encoding/json"
7+ "net/http"
8+ "net/http/httptest"
9+ "net/url"
10+ "testing"
11+ "time"
12+
13+ "github.com/golang-jwt/jwt/v5"
14+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
15+ . "github.com/onsi/gomega"
16+)
17+
18+const (
19+ dttSSOTestClientID = "redash-gate"
20+ // Shorter than 32 bytes, and contains characters golang.org/x/oauth2 would
21+ // URL-escape in Basic auth.
22+ dttSSOTestSecret = "s3cr+t/abc=="
23+)
24+
25+// dttSSOTestServer mimics the SSO's token and check_token endpoints.
26+type dttSSOTestServer struct {
27+ *httptest.Server
28+ claims map[string]interface{}
29+ signingKey []byte
30+ lastForm url.Values
31+ validTokens map[string]bool
32+ tokenCounter int
33+}
34+
35+func newDTTSSOTestServer() *dttSSOTestServer {
36+ s := &dttSSOTestServer{
37+ signingKey: []byte(dttSSOTestSecret),
38+ validTokens: map[string]bool{},
39+ }
40+ s.Server = httptest.NewServer(http.HandlerFunc(s.handle))
41+ u, _ := url.Parse(s.URL)
42+ s.claims = map[string]interface{}{
43+ "iss": "http://" + u.Host,
44+ "aud": dttSSOTestClientID,
45+ "sub": "0b5c1e1a-1111-2222-3333-444455556666",
46+ "user_id": "0b5c1e1a-1111-2222-3333-444455556666",
47+ "email": "admin@example.com",
48+ "role": "dtt_admin",
49+ "exp": time.Now().Add(time.Hour).Unix(),
50+ }
51+ return s
52+}
53+
54+func (s *dttSSOTestServer) handle(w http.ResponseWriter, r *http.Request) {
55+ switch r.URL.Path {
56+ case "/oauth/token":
57+ want := "Basic " + base64.StdEncoding.EncodeToString([]byte(dttSSOTestClientID+":"+dttSSOTestSecret))
58+ if r.Header.Get("Authorization") != want {
59+ w.WriteHeader(http.StatusUnauthorized)
60+ return
61+ }
62+ _ = r.ParseForm()
63+ s.lastForm = r.PostForm
64+ if r.PostForm.Get("grant_type") == "refresh_token" && !s.validTokens["refresh-"+r.PostForm.Get("refresh_token")] {
65+ w.WriteHeader(http.StatusBadRequest)
66+ _, _ = w.Write([]byte(`{"error":"invalid_grant","error_description":"Invalid refresh token"}`))
67+ return
68+ }
69+ s.tokenCounter++
70+ access := "access-" + string(rune('0'+s.tokenCounter))
71+ refresh := "refresh-" + string(rune('0'+s.tokenCounter))
72+ s.validTokens[access] = true
73+ s.validTokens["refresh-"+refresh] = true
74+ w.Header().Set("Content-Type", "application/json")
75+ _ = json.NewEncoder(w).Encode(map[string]interface{}{
76+ "access_token": access,
77+ "token_type": "bearer",
78+ "expires_in": 3600,
79+ "refresh_token": refresh,
80+ "id_token": s.idToken(),
81+ "scope": "openid",
82+ })
83+ case "/oauth/check_token":
84+ token := r.Header.Get("Authorization")
85+ if len(token) > len("Bearer ") && s.validTokens[token[len("Bearer "):]] {
86+ _, _ = w.Write([]byte(`{"user_id":"0b5c1e1a-1111-2222-3333-444455556666","real_user_id":null,"issued_by":"redash-gate"}`))
87+ return
88+ }
89+ w.WriteHeader(http.StatusUnauthorized)
90+ _, _ = w.Write([]byte("Invalid access token"))
91+ default:
92+ w.WriteHeader(http.StatusNotFound)
93+ }
94+}
95+
96+func (s *dttSSOTestServer) idToken() string {
97+ if s.claims == nil {
98+ return ""
99+ }
100+ raw, err := jwt.NewWithClaims(jwt.SigningMethodHS256, jwt.MapClaims(s.claims)).SignedString(s.signingKey)
101+ if err != nil {
102+ panic(err)
103+ }
104+ return raw
105+}
106+
107+func testDTTSSOProvider(server *dttSSOTestServer) *DTTSSOProvider {
108+ p := NewDTTSSOProvider(&ProviderData{ClientID: dttSSOTestClientID, ClientSecret: dttSSOTestSecret})
109+ u, _ := url.Parse(server.URL)
110+ updateURL(p.LoginURL, u.Host)
111+ updateURL(p.RedeemURL, u.Host)
112+ updateURL(p.ValidateURL, u.Host)
113+ return p
114+}
115+
116+func TestNewDTTSSOProvider(t *testing.T) {
117+ g := NewWithT(t)
118+
119+ providerData := NewDTTSSOProvider(&ProviderData{}).Data()
120+ g.Expect(providerData.ProviderName).To(Equal("DTT SSO"))
121+ g.Expect(providerData.LoginURL.String()).To(Equal("https://auth.thebestagent.pro/oauth/authorize"))
122+ g.Expect(providerData.RedeemURL.String()).To(Equal("https://auth.thebestagent.pro/oauth/token"))
123+ g.Expect(providerData.ValidateURL.String()).To(Equal("https://auth.thebestagent.pro/oauth/check_token"))
124+ g.Expect(providerData.Scope).To(Equal("openid"))
125+}
126+
127+func TestDTTSSOProviderRedeem(t *testing.T) {
128+ g := NewWithT(t)
129+ server := newDTTSSOTestServer()
130+ defer server.Close()
131+ p := testDTTSSOProvider(server)
132+
133+ s, err := p.Redeem(context.Background(), "https://redash.example.com/oauth2/callback", "the-code", "")
134+ g.Expect(err).ToNot(HaveOccurred())
135+ g.Expect(s.Email).To(Equal("admin@example.com"))
136+ g.Expect(s.User).To(Equal("0b5c1e1a-1111-2222-3333-444455556666"))
137+ g.Expect(s.Groups).To(Equal([]string{"dtt_admin"}))
138+ g.Expect(s.AccessToken).To(Equal("access-1"))
139+ g.Expect(s.RefreshToken).To(Equal("refresh-1"))
140+ g.Expect(s.IDToken).ToNot(BeEmpty())
141+ g.Expect(s.ExpiresOn).ToNot(BeNil())
142+ g.Expect(*s.ExpiresOn).To(BeTemporally("~", time.Now().Add(time.Hour), time.Minute))
143+
144+ g.Expect(server.lastForm.Get("grant_type")).To(Equal("authorization_code"))
145+ g.Expect(server.lastForm.Get("code")).To(Equal("the-code"))
146+ g.Expect(server.lastForm.Get("redirect_uri")).To(Equal("https://redash.example.com/oauth2/callback"))
147+}
148+
149+func TestDTTSSOProviderRedeemRejectsBadIDTokens(t *testing.T) {
150+ testCases := map[string]func(s *dttSSOTestServer){
151+ "signed with another secret": func(s *dttSSOTestServer) { s.signingKey = []byte("some-other-client-secret") },
152+ "other audience": func(s *dttSSOTestServer) { s.claims["aud"] = "frappe-gate" },
153+ "other issuer": func(s *dttSSOTestServer) { s.claims["iss"] = "https://evil.example.com" },
154+ "expired": func(s *dttSSOTestServer) { s.claims["exp"] = time.Now().Add(-time.Minute).Unix() },
155+ "no email": func(s *dttSSOTestServer) { delete(s.claims, "email") },
156+ "no id_token": func(s *dttSSOTestServer) { s.claims = nil },
157+ }
158+ for name, tamper := range testCases {
159+ t.Run(name, func(t *testing.T) {
160+ g := NewWithT(t)
161+ server := newDTTSSOTestServer()
162+ defer server.Close()
163+ tamper(server)
164+
165+ _, err := testDTTSSOProvider(server).Redeem(context.Background(), "https://redash.example.com/oauth2/callback", "the-code", "")
166+ g.Expect(err).To(HaveOccurred())
167+ })
168+ }
169+}
170+
171+func TestDTTSSOProviderRedeemRejectsUnsignedIDToken(t *testing.T) {
172+ g := NewWithT(t)
173+ server := newDTTSSOTestServer()
174+ defer server.Close()
175+ p := testDTTSSOProvider(server)
176+
177+ header := base64.RawURLEncoding.EncodeToString([]byte(`{"alg":"none"}`))
178+ payload, _ := json.Marshal(server.claims)
179+ unsigned := header + "." + base64.RawURLEncoding.EncodeToString(payload) + "."
180+
181+ _, err := p.verifyIDToken(unsigned, dttSSOTestSecret)
182+ g.Expect(err).To(HaveOccurred())
183+}
184+
185+func TestDTTSSOProviderRefreshSession(t *testing.T) {
186+ g := NewWithT(t)
187+ server := newDTTSSOTestServer()
188+ defer server.Close()
189+ p := testDTTSSOProvider(server)
190+
191+ s, err := p.Redeem(context.Background(), "https://redash.example.com/oauth2/callback", "the-code", "")
192+ g.Expect(err).ToNot(HaveOccurred())
193+ nonce := []byte("keep-me")
194+ s.Nonce = nonce
195+
196+ server.claims["role"] = "agent"
197+ server.claims["email"] = "renamed@example.com"
198+ refreshed, err := p.RefreshSession(context.Background(), s)
199+ g.Expect(err).ToNot(HaveOccurred())
200+ g.Expect(refreshed).To(BeTrue())
201+ g.Expect(s.AccessToken).To(Equal("access-2"))
202+ g.Expect(s.RefreshToken).To(Equal("refresh-2"))
203+ g.Expect(s.Groups).To(Equal([]string{"agent"}))
204+ g.Expect(s.Email).To(Equal("renamed@example.com"))
205+ g.Expect(s.Nonce).To(Equal(nonce))
206+ g.Expect(server.lastForm.Get("grant_type")).To(Equal("refresh_token"))
207+ g.Expect(server.lastForm.Get("refresh_token")).To(Equal("refresh-1"))
208+}
209+
210+func TestDTTSSOProviderRefreshSessionFailures(t *testing.T) {
211+ g := NewWithT(t)
212+ server := newDTTSSOTestServer()
213+ defer server.Close()
214+ p := testDTTSSOProvider(server)
215+
216+ refreshed, err := p.RefreshSession(context.Background(), &sessions.SessionState{})
217+ g.Expect(err).ToNot(HaveOccurred())
218+ g.Expect(refreshed).To(BeFalse())
219+
220+ refreshed, err = p.RefreshSession(context.Background(), &sessions.SessionState{RefreshToken: "revoked"})
221+ g.Expect(err).To(HaveOccurred())
222+ g.Expect(refreshed).To(BeFalse())
223+}
224+
225+func TestDTTSSOProviderValidateSession(t *testing.T) {
226+ g := NewWithT(t)
227+ server := newDTTSSOTestServer()
228+ defer server.Close()
229+ p := testDTTSSOProvider(server)
230+
231+ s, err := p.Redeem(context.Background(), "https://redash.example.com/oauth2/callback", "the-code", "")
232+ g.Expect(err).ToNot(HaveOccurred())
233+ g.Expect(p.ValidateSession(context.Background(), s)).To(BeTrue())
234+
235+ delete(server.validTokens, s.AccessToken)
236+ g.Expect(p.ValidateSession(context.Background(), s)).To(BeFalse())
237+}
providers/google.go+2−2
@@ -219,7 +219,7 @@ func (p *GoogleProvider) Redeem(ctx context.Context, redirectURL, code, codeVeri
219
219
220 err = requests.New(p.RedeemURL.String()).
220 err = requests.New(p.RedeemURL.String()).
221 WithContext(ctx).
221 WithContext(ctx).
222− WithMethod("POST").
222+ WithMethod(http.MethodPost).
223 WithBody(bytes.NewBufferString(params.Encode())).
223 WithBody(bytes.NewBufferString(params.Encode())).
224 SetHeader("Content-Type", "application/x-www-form-urlencoded").
224 SetHeader("Content-Type", "application/x-www-form-urlencoded").
225 Do().
225 Do().
@@ -543,7 +543,7 @@ func (p *GoogleProvider) redeemRefreshToken(ctx context.Context, s *sessions.Ses
543
543
544 err = requests.New(p.RedeemURL.String()).
544 err = requests.New(p.RedeemURL.String()).
545 WithContext(ctx).
545 WithContext(ctx).
546− WithMethod("POST").
546+ WithMethod(http.MethodPost).
547 WithBody(bytes.NewBufferString(params.Encode())).
547 WithBody(bytes.NewBufferString(params.Encode())).
548 SetHeader("Content-Type", "application/x-www-form-urlencoded").
548 SetHeader("Content-Type", "application/x-www-form-urlencoded").
549 Do().
549 Do().
providers/logingov.go+2−1
@@ -8,6 +8,7 @@ import (
8 "errors"
8 "errors"
9 "fmt"
9 "fmt"
10 "math/big"
10 "math/big"
11+ "net/http"
11 "net/url"
12 "net/url"
12 "os"
13 "os"
13 "time"
14 "time"
@@ -237,7 +238,7 @@ func (p *LoginGovProvider) Redeem(ctx context.Context, _, code, codeVerifier str
237 }
238 }
238 err = requests.New(p.RedeemURL.String()).
239 err = requests.New(p.RedeemURL.String()).
239 WithContext(ctx).
240 WithContext(ctx).
240− WithMethod("POST").
241+ WithMethod(http.MethodPost).
241 WithBody(bytes.NewBufferString(params.Encode())).
242 WithBody(bytes.NewBufferString(params.Encode())).
242 SetHeader("Content-Type", "application/x-www-form-urlencoded").
243 SetHeader("Content-Type", "application/x-www-form-urlencoded").
243 Do().
244 Do().
providers/ms_entra_id.go+2−1
@@ -5,6 +5,7 @@ import (
5 "context"
5 "context"
6 "encoding/json"
6 "encoding/json"
7 "fmt"
7 "fmt"
8+ "net/http"
8 "net/url"
9 "net/url"
9 "os"
10 "os"
10 "regexp"
11 "regexp"
@@ -304,7 +305,7 @@ func (p *MicrosoftEntraIDProvider) checkTenantMatchesTenantList(tenant string, a
304func (p *MicrosoftEntraIDProvider) fetchToken(ctx context.Context, params url.Values) (*oauth2.Token, error) {
305func (p *MicrosoftEntraIDProvider) fetchToken(ctx context.Context, params url.Values) (*oauth2.Token, error) {
305 resp := requests.New(p.RedeemURL.String()).
306 resp := requests.New(p.RedeemURL.String()).
306 WithContext(ctx).
307 WithContext(ctx).
307− WithMethod("POST").
308+ WithMethod(http.MethodPost).
308 WithBody(bytes.NewBufferString(params.Encode())).
309 WithBody(bytes.NewBufferString(params.Encode())).
309 SetHeader("Content-Type", "application/x-www-form-urlencoded").
310 SetHeader("Content-Type", "application/x-www-form-urlencoded").
310 Do()
311 Do()
providers/provider_default.go+2−1
@@ -5,6 +5,7 @@ import (
5 "context"
5 "context"
6 "errors"
6 "errors"
7 "fmt"
7 "fmt"
8+ "net/http"
8 "net/url"
9 "net/url"
9
10
10 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
11 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware"
@@ -71,7 +72,7 @@ func (p *ProviderData) Redeem(ctx context.Context, redirectURL, code, codeVerifi
71
72
72 result := requests.New(p.RedeemURL.String()).
73 result := requests.New(p.RedeemURL.String()).
73 WithContext(ctx).
74 WithContext(ctx).
74− WithMethod("POST").
75+ WithMethod(http.MethodPost).
75 WithBody(bytes.NewBufferString(params.Encode())).
76 WithBody(bytes.NewBufferString(params.Encode())).
76 SetHeader("Content-Type", "application/x-www-form-urlencoded").
77 SetHeader("Content-Type", "application/x-www-form-urlencoded").
77 Do()
78 Do()
providers/providers.go+7−3
@@ -40,6 +40,8 @@ func NewProvider(providerConfig options.Provider) (Provider, error) {
40 switch providerConfig.Type {
40 switch providerConfig.Type {
41 case options.ADFSProvider:
41 case options.ADFSProvider:
42 return NewADFSProvider(providerData, providerConfig), nil
42 return NewADFSProvider(providerData, providerConfig), nil
43+ case options.AtlassianProvider:
44+ return NewAtlassianProvider(providerData), nil
43 case options.AzureProvider:
45 case options.AzureProvider:
44 return NewAzureProvider(providerData, providerConfig.AzureConfig), nil
46 return NewAzureProvider(providerData, providerConfig.AzureConfig), nil
45 case options.MicrosoftEntraIDProvider:
47 case options.MicrosoftEntraIDProvider:
@@ -50,6 +52,8 @@ func NewProvider(providerConfig options.Provider) (Provider, error) {
50 return NewCIDAASProvider(providerData, providerConfig), nil
52 return NewCIDAASProvider(providerData, providerConfig), nil
51 case options.DigitalOceanProvider:
53 case options.DigitalOceanProvider:
52 return NewDigitalOceanProvider(providerData), nil
54 return NewDigitalOceanProvider(providerData), nil
55+ case options.DTTSSOProvider:
56+ return NewDTTSSOProvider(providerData), nil
53 case options.FacebookProvider:
57 case options.FacebookProvider:
54 return NewFacebookProvider(providerData), nil
58 return NewFacebookProvider(providerData), nil
55 case options.GitHubProvider:
59 case options.GitHubProvider:
@@ -189,9 +193,9 @@ func parseCodeChallengeMethod(providerConfig options.Provider) string {
189
193
190func providerRequiresOIDCProviderVerifier(providerType options.ProviderType) (bool, error) {
194func providerRequiresOIDCProviderVerifier(providerType options.ProviderType) (bool, error) {
191 switch providerType {
195 switch providerType {
192− case options.BitbucketProvider, options.DigitalOceanProvider, options.FacebookProvider, options.GitHubProvider,
196+ case options.AtlassianProvider, options.BitbucketProvider, options.DigitalOceanProvider, options.DTTSSOProvider,
193− options.GoogleProvider, options.KeycloakProvider, options.LinkedInProvider, options.LoginGovProvider,
197+ options.FacebookProvider, options.GitHubProvider, options.GoogleProvider, options.KeycloakProvider,
194− options.NextCloudProvider, options.SourceHutProvider:
198+ options.LinkedInProvider, options.LoginGovProvider, options.NextCloudProvider, options.SourceHutProvider:
195 return false, nil
199 return false, nil
196 case options.OIDCProvider, options.ADFSProvider, options.AzureProvider, options.CidaasProvider,
200 case options.OIDCProvider, options.ADFSProvider, options.AzureProvider, options.CidaasProvider,
197 options.GitLabProvider, options.KeycloakOIDCProvider, options.MicrosoftEntraIDProvider:
201 options.GitLabProvider, options.KeycloakOIDCProvider, options.MicrosoftEntraIDProvider:
providers/srht.go+2−1
@@ -4,6 +4,7 @@ import (
4 "bytes"
4 "bytes"
5 "context"
5 "context"
6 "fmt"
6 "fmt"
7+ "net/http"
7 "net/url"
8 "net/url"
8
9
9 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
10 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions"
@@ -75,7 +76,7 @@ func NewSourceHutProvider(p *ProviderData) *SourceHutProvider {
75func (p *SourceHutProvider) EnrichSession(ctx context.Context, s *sessions.SessionState) error {
76func (p *SourceHutProvider) EnrichSession(ctx context.Context, s *sessions.SessionState) error {
76 json, err := requests.New(p.ProfileURL.String()).
77 json, err := requests.New(p.ProfileURL.String()).
77 WithContext(ctx).
78 WithContext(ctx).
78− WithMethod("POST").
79+ WithMethod(http.MethodPost).
79 SetHeader("Content-Type", "application/json").
80 SetHeader("Content-Type", "application/json").
80 SetHeader("Authorization", "Bearer "+s.AccessToken).
81 SetHeader("Authorization", "Bearer "+s.AccessToken).
81 WithBody(bytes.NewBufferString(`{"query": "{ me { username, email } }"}`)).
82 WithBody(bytes.NewBufferString(`{"query": "{ me { username, email } }"}`)).