Compare commits
	
		
			No commits in common. "master" and "v7.12.0" have entirely different histories.
		
	
	
		|  | @ -17,7 +17,7 @@ jobs: | ||||||
|       uses: actions/checkout@v5 |       uses: actions/checkout@v5 | ||||||
| 
 | 
 | ||||||
|     - name: Set up Go |     - name: Set up Go | ||||||
|       uses: actions/setup-go@v6 |       uses: actions/setup-go@v5 | ||||||
|       with: |       with: | ||||||
|         go-version-file: go.mod |         go-version-file: go.mod | ||||||
|       id: go |       id: go | ||||||
|  | @ -25,7 +25,7 @@ jobs: | ||||||
|     - name: Get dependencies |     - name: Get dependencies | ||||||
|       env: |       env: | ||||||
|         # renovate: datasource=github-tags depName=golangci/golangci-lint |         # renovate: datasource=github-tags depName=golangci/golangci-lint | ||||||
|         GOLANGCI_LINT_VERSION: v2.5.0 |         GOLANGCI_LINT_VERSION: v2.4.0 | ||||||
|       run: | |       run: | | ||||||
|         curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCI_LINT_VERSION} |         curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCI_LINT_VERSION} | ||||||
|         curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter |         curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter | ||||||
|  |  | ||||||
|  | @ -54,7 +54,7 @@ jobs: | ||||||
|         git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" |         git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com" | ||||||
|      |      | ||||||
|     - name: Setup node |     - name: Setup node | ||||||
|       uses: actions/setup-node@v6 |       uses: actions/setup-node@v4 | ||||||
|       with: |       with: | ||||||
|         node-version-file: docs/package.json |         node-version-file: docs/package.json | ||||||
|        |        | ||||||
|  |  | ||||||
|  | @ -20,7 +20,7 @@ jobs: | ||||||
|         id: pages |         id: pages | ||||||
|         uses: actions/configure-pages@v5 |         uses: actions/configure-pages@v5 | ||||||
|        |        | ||||||
|       - uses: actions/setup-node@v6 |       - uses: actions/setup-node@v4 | ||||||
|         with: |         with: | ||||||
|           # renovate: datasource=node-version depName=node |           # renovate: datasource=node-version depName=node | ||||||
|           node-version: 22 |           node-version: 22 | ||||||
|  | @ -37,7 +37,7 @@ jobs: | ||||||
|     steps: |     steps: | ||||||
|       - uses: actions/checkout@v5 |       - uses: actions/checkout@v5 | ||||||
| 
 | 
 | ||||||
|       - uses: actions/setup-node@v6 |       - uses: actions/setup-node@v4 | ||||||
|         with: |         with: | ||||||
|           # renovate: datasource=node-version depName=node |           # renovate: datasource=node-version depName=node | ||||||
|           node-version: 22 |           node-version: 22 | ||||||
|  |  | ||||||
|  | @ -9,7 +9,7 @@ jobs: | ||||||
|       pull-requests: write |       pull-requests: write | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
|     steps: |     steps: | ||||||
|       - uses: actions/labeler@v6 |       - uses: actions/labeler@v5 | ||||||
|         with: |         with: | ||||||
|           sync-labels: true |           sync-labels: true | ||||||
|           dot: true |           dot: true | ||||||
|  |  | ||||||
|  | @ -43,14 +43,14 @@ jobs: | ||||||
|       id: tag |       id: tag | ||||||
|      |      | ||||||
|     - name: Set up go |     - name: Set up go | ||||||
|       uses: actions/setup-go@v6 |       uses: actions/setup-go@v5 | ||||||
|       with: |       with: | ||||||
|         go-version-file: go.mod |         go-version-file: go.mod | ||||||
| 
 | 
 | ||||||
|     - name: Get dependencies |     - name: Get dependencies | ||||||
|       env: |       env: | ||||||
|         # renovate: datasource=github-tags depName=golangci/golangci-lint |         # renovate: datasource=github-tags depName=golangci/golangci-lint | ||||||
|         GOLANGCI_LINT_VERSION: v2.5.0 |         GOLANGCI_LINT_VERSION: v2.4.0 | ||||||
|       run: | |       run: | | ||||||
|         curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCI_LINT_VERSION} |         curl -sSfL https://raw.githubusercontent.com/golangci/golangci-lint/master/install.sh | sh -s -- -b $(go env GOPATH)/bin ${GOLANGCI_LINT_VERSION} | ||||||
|         curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter |         curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter | ||||||
|  | @ -64,7 +64,7 @@ jobs: | ||||||
|      |      | ||||||
|     # Upload artifacts in case of workflow failure |     # Upload artifacts in case of workflow failure | ||||||
|     - name: Upload Artifacts |     - name: Upload Artifacts | ||||||
|       uses: actions/upload-artifact@v5 |       uses: actions/upload-artifact@v4 | ||||||
|       with: |       with: | ||||||
|         name: oauth2-proxy-artifacts |         name: oauth2-proxy-artifacts | ||||||
|         path: | |         path: | | ||||||
|  |  | ||||||
|  | @ -10,7 +10,7 @@ jobs: | ||||||
|     runs-on: ubuntu-latest |     runs-on: ubuntu-latest | ||||||
| 
 | 
 | ||||||
|     steps: |     steps: | ||||||
|     - uses: actions/stale@v10 |     - uses: actions/stale@v9 | ||||||
|       with: |       with: | ||||||
|         repo-token: ${{ secrets.GITHUB_TOKEN }} |         repo-token: ${{ secrets.GITHUB_TOKEN }} | ||||||
|         days-before-stale: 180 |         days-before-stale: 180 | ||||||
|  |  | ||||||
|  | @ -8,10 +8,6 @@ | ||||||
| 
 | 
 | ||||||
| ## Changes since v7.12.0 | ## Changes since v7.12.0 | ||||||
| 
 | 
 | ||||||
| - [#3228](https://github.com/oauth2-proxy/oauth2-proxy/pull/3228) fix: use GetSecret() in ticket.go makeCookie to respect cookie-secret-file (@stagswtf) |  | ||||||
| - [#3244](https://github.com/oauth2-proxy/oauth2-proxy/pull/3244) chore(deps): upgrade to latest go1.25.3 (@tuunit) |  | ||||||
| - [#3238](https://github.com/oauth2-proxy/oauth2-proxy/pull/3238) chore: Replace pkg/clock with narrowly targeted stub clocks (@dsymonds) |  | ||||||
| 
 |  | ||||||
| # V7.12.0 | # V7.12.0 | ||||||
| 
 | 
 | ||||||
| ## Release Highlights | ## Release Highlights | ||||||
|  |  | ||||||
							
								
								
									
										25
									
								
								Makefile
								
								
								
								
							
							
						
						
									
										25
									
								
								Makefile
								
								
								
								
							|  | @ -36,12 +36,13 @@ REPOSITORY ?= oauth2-proxy | ||||||
| DATE := $(shell date +"%Y%m%d") | DATE := $(shell date +"%Y%m%d") | ||||||
| .NOTPARALLEL: | .NOTPARALLEL: | ||||||
| 
 | 
 | ||||||
| # The go version in go.mod used for the Docker build toolchain, without the patch
 | GO_MAJOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f1) | ||||||
| GO_MOD_VERSION_MINOR := $(shell $(GO) list -f '{{printf "%.4s" .Module.GoVersion}}' ) | GO_MINOR_VERSION = $(shell $(GO) version | cut -c 14- | cut -d' ' -f1 | cut -d'.' -f2) | ||||||
| 
 | 
 | ||||||
| # From go1.21 go will transparently download the toolchain declared in go.mod. https://go.dev/doc/toolchain
 | GO_MOD_VERSION = $(shell sed -En 's/^go ([[:digit:]]\.[[:digit:]]+)\.[[:digit:]]+/\1/p' go.mod) | ||||||
| # We don't need to keep this message updated: the important info is in go.mod.
 | MINIMUM_SUPPORTED_GO_MAJOR_VERSION = $(shell echo ${GO_MOD_VERSION} | cut -d' ' -f1 | cut -d'.' -f1) | ||||||
| GO_VERSION_VALIDATION_ERR_MSG = Golang version is not supported, please update to at least go1.21 | MINIMUM_SUPPORTED_GO_MINOR_VERSION = $(shell echo ${GO_MOD_VERSION} | cut -d' ' -f1 | cut -d'.' -f2) | ||||||
|  | GO_VERSION_VALIDATION_ERR_MSG = Golang version is not supported, please update to at least $(MINIMUM_SUPPORTED_GO_MAJOR_VERSION).$(MINIMUM_SUPPORTED_GO_MINOR_VERSION) | ||||||
| 
 | 
 | ||||||
| ifeq ($(COVER),true) | ifeq ($(COVER),true) | ||||||
| TESTCOVER ?= -coverprofile c.out | TESTCOVER ?= -coverprofile c.out | ||||||
|  | @ -55,7 +56,7 @@ build: validate-go-version clean $(BINARY) ## Build and create oauth2-proxy bina | ||||||
| $(BINARY): | $(BINARY): | ||||||
| 	CGO_ENABLED=0 $(GO) build -a -installsuffix cgo -ldflags="-X github.com/oauth2-proxy/oauth2-proxy/v7/pkg/version.VERSION=${VERSION}" -o $@ github.com/oauth2-proxy/oauth2-proxy/v7 | 	CGO_ENABLED=0 $(GO) build -a -installsuffix cgo -ldflags="-X github.com/oauth2-proxy/oauth2-proxy/v7/pkg/version.VERSION=${VERSION}" -o $@ github.com/oauth2-proxy/oauth2-proxy/v7 | ||||||
| 
 | 
 | ||||||
| DOCKER_BUILDX_COMMON_ARGS     ?= --build-arg BUILD_IMAGE=docker.io/library/golang:$(GO_MOD_VERSION_MINOR)-bookworm --build-arg VERSION=$(VERSION) | DOCKER_BUILDX_COMMON_ARGS     ?= --build-arg BUILD_IMAGE=docker.io/library/golang:${GO_MOD_VERSION}-bookworm --build-arg VERSION=${VERSION} | ||||||
| 
 | 
 | ||||||
| DOCKER_BUILD_PLATFORM         ?= linux/amd64,linux/arm64,linux/ppc64le,linux/arm/v7,linux/s390x | DOCKER_BUILD_PLATFORM         ?= linux/amd64,linux/arm64,linux/ppc64le,linux/arm/v7,linux/s390x | ||||||
| DOCKER_BUILD_RUNTIME_IMAGE    ?= gcr.io/distroless/static:nonroot | DOCKER_BUILD_RUNTIME_IMAGE    ?= gcr.io/distroless/static:nonroot | ||||||
|  | @ -66,7 +67,7 @@ DOCKER_BUILDX_PUSH            := $(DOCKER_BUILDX) --push | ||||||
| DOCKER_BUILDX_PUSH_X_PLATFORM := $(DOCKER_BUILDX_PUSH) --platform ${DOCKER_BUILD_PLATFORM} | DOCKER_BUILDX_PUSH_X_PLATFORM := $(DOCKER_BUILDX_PUSH) --platform ${DOCKER_BUILD_PLATFORM} | ||||||
| 
 | 
 | ||||||
| DOCKER_BUILD_PLATFORM_ALPINE         ?= linux/amd64,linux/arm64,linux/ppc64le,linux/arm/v6,linux/arm/v7,linux/s390x | DOCKER_BUILD_PLATFORM_ALPINE         ?= linux/amd64,linux/arm64,linux/ppc64le,linux/arm/v6,linux/arm/v7,linux/s390x | ||||||
| DOCKER_BUILD_RUNTIME_IMAGE_ALPINE    ?= alpine:3.22.2 | DOCKER_BUILD_RUNTIME_IMAGE_ALPINE    ?= alpine:3.22.1 | ||||||
| DOCKER_BUILDX_ARGS_ALPINE            ?= --build-arg RUNTIME_IMAGE=${DOCKER_BUILD_RUNTIME_IMAGE_ALPINE} ${DOCKER_BUILDX_COMMON_ARGS} | DOCKER_BUILDX_ARGS_ALPINE            ?= --build-arg RUNTIME_IMAGE=${DOCKER_BUILD_RUNTIME_IMAGE_ALPINE} ${DOCKER_BUILDX_COMMON_ARGS} | ||||||
| DOCKER_BUILDX_X_PLATFORM_ALPINE      := docker buildx build ${DOCKER_BUILDX_ARGS_ALPINE} --platform ${DOCKER_BUILD_PLATFORM_ALPINE} | DOCKER_BUILDX_X_PLATFORM_ALPINE      := docker buildx build ${DOCKER_BUILDX_ARGS_ALPINE} --platform ${DOCKER_BUILD_PLATFORM_ALPINE} | ||||||
| DOCKER_BUILDX_PUSH_X_PLATFORM_ALPINE := $(DOCKER_BUILDX_X_PLATFORM_ALPINE) --push | DOCKER_BUILDX_PUSH_X_PLATFORM_ALPINE := $(DOCKER_BUILDX_X_PLATFORM_ALPINE) --push | ||||||
|  | @ -157,7 +158,15 @@ lint: validate-go-version ## Lint all files using golangci-lint | ||||||
| 
 | 
 | ||||||
| .PHONY: validate-go-version | .PHONY: validate-go-version | ||||||
| validate-go-version: ## Validate Go environment requirements
 | validate-go-version: ## Validate Go environment requirements
 | ||||||
| 	@$(GO) list . >/dev/null || { echo '$(GO_VERSION_VALIDATION_ERR_MSG)'; exit 1; } | 	@if [ $(GO_MAJOR_VERSION) -gt $(MINIMUM_SUPPORTED_GO_MAJOR_VERSION) ]; then \
 | ||||||
|  | 		exit 0 ;\
 | ||||||
|  | 	elif [ $(GO_MAJOR_VERSION) -lt $(MINIMUM_SUPPORTED_GO_MAJOR_VERSION) ]; then \
 | ||||||
|  | 		echo '$(GO_VERSION_VALIDATION_ERR_MSG)';\
 | ||||||
|  | 		exit 1; \
 | ||||||
|  | 	elif [ $(GO_MINOR_VERSION) -lt $(MINIMUM_SUPPORTED_GO_MINOR_VERSION) ] ; then \
 | ||||||
|  | 		echo '$(GO_VERSION_VALIDATION_ERR_MSG)';\
 | ||||||
|  | 		exit 1; \
 | ||||||
|  | 	fi | ||||||
| 
 | 
 | ||||||
| # local-env can be used to interact with the local development environment
 | # local-env can be used to interact with the local development environment
 | ||||||
| # eg:
 | # eg:
 | ||||||
|  |  | ||||||
|  | @ -39,7 +39,7 @@ services: | ||||||
|       httpbin: {} |       httpbin: {} | ||||||
| 
 | 
 | ||||||
|   gitea: |   gitea: | ||||||
|     image: gitea/gitea:1.24.7 |     image: gitea/gitea:1.24.5 | ||||||
|     container_name: gitea |     container_name: gitea | ||||||
|     environment: |     environment: | ||||||
|       - USER_UID=1000 |       - USER_UID=1000 | ||||||
|  |  | ||||||
|  | @ -55,7 +55,7 @@ services: | ||||||
|       httpbin: {} |       httpbin: {} | ||||||
|   dex: |   dex: | ||||||
|     container_name: dex |     container_name: dex | ||||||
|     image: ghcr.io/dexidp/dex:v2.44.0 |     image: ghcr.io/dexidp/dex:v2.43.1 | ||||||
|     command: dex serve /dex.yaml |     command: dex serve /dex.yaml | ||||||
|     hostname: dex |     hostname: dex | ||||||
|     volumes: |     volumes: | ||||||
|  | @ -78,7 +78,7 @@ services: | ||||||
|       httpbin: {} |       httpbin: {} | ||||||
|   etcd: |   etcd: | ||||||
|     container_name: etcd |     container_name: etcd | ||||||
|     image: gcr.io/etcd-development/etcd:v3.6.5 |     image: gcr.io/etcd-development/etcd:v3.6.4 | ||||||
|     entrypoint: /usr/local/bin/etcd |     entrypoint: /usr/local/bin/etcd | ||||||
|     command: |     command: | ||||||
|       - --listen-client-urls=http://0.0.0.0:2379 |       - --listen-client-urls=http://0.0.0.0:2379 | ||||||
|  |  | ||||||
|  | @ -34,7 +34,7 @@ services: | ||||||
|   # Reverse proxy |   # Reverse proxy | ||||||
|   gateway: |   gateway: | ||||||
|     container_name: traefik |     container_name: traefik | ||||||
|     image: traefik:v2.11.29 |     image: traefik:v2.11.28 | ||||||
|     volumes: |     volumes: | ||||||
|       - "./traefik:/etc/traefik" |       - "./traefik:/etc/traefik" | ||||||
|     ports: |     ports: | ||||||
|  |  | ||||||
|  | @ -29,7 +29,7 @@ services: | ||||||
|       - httpbin |       - httpbin | ||||||
|   dex: |   dex: | ||||||
|     container_name: dex |     container_name: dex | ||||||
|     image: ghcr.io/dexidp/dex:v2.44.0 |     image: ghcr.io/dexidp/dex:v2.43.1 | ||||||
|     command: dex serve /dex.yaml |     command: dex serve /dex.yaml | ||||||
|     hostname: dex |     hostname: dex | ||||||
|     volumes: |     volumes: | ||||||
|  | @ -52,7 +52,7 @@ services: | ||||||
|       httpbin: {} |       httpbin: {} | ||||||
|   etcd: |   etcd: | ||||||
|     container_name: etcd |     container_name: etcd | ||||||
|     image: gcr.io/etcd-development/etcd:v3.6.5 |     image: gcr.io/etcd-development/etcd:v3.6.4 | ||||||
|     entrypoint: /usr/local/bin/etcd |     entrypoint: /usr/local/bin/etcd | ||||||
|     command: |     command: | ||||||
|       - --listen-client-urls=http://0.0.0.0:2379 |       - --listen-client-urls=http://0.0.0.0:2379 | ||||||
|  |  | ||||||
|  | @ -1,15 +1,15 @@ | ||||||
| dependencies: | dependencies: | ||||||
| - name: dex | - name: dex | ||||||
|   repository: https://charts.dexidp.io |   repository: https://charts.dexidp.io | ||||||
|   version: 0.24.0 |   version: 0.23.0 | ||||||
| - name: oauth2-proxy | - name: oauth2-proxy | ||||||
|   repository: https://oauth2-proxy.github.io/manifests |   repository: https://oauth2-proxy.github.io/manifests | ||||||
|   version: 7.18.0 |   version: 7.14.1 | ||||||
| - name: httpbin | - name: httpbin | ||||||
|   repository: https://conservis.github.io/helm-charts |   repository: https://conservis.github.io/helm-charts | ||||||
|   version: 1.1.0 |   version: 1.1.0 | ||||||
| - name: hello-world | - name: hello-world | ||||||
|   repository: https://conservis.github.io/helm-charts |   repository: https://conservis.github.io/helm-charts | ||||||
|   version: 1.1.0 |   version: 1.1.0 | ||||||
| digest: sha256:fee913531bfb67e5555e995d8fb040c330e6eb4b5a8c777ceb9841135ea9bb84 | digest: sha256:9b18e072db6863053c2967d023929ab4b9c03b6bd84f6529d90fe7a9ec5e315f | ||||||
| generated: "2025-10-28T06:46:35.38300324Z" | generated: "2025-07-20T08:56:43.559585022Z" | ||||||
|  |  | ||||||
|  | @ -3,10 +3,10 @@ description: K8S example based on https://kind.sigs.k8s.io | ||||||
| name: kubernetes | name: kubernetes | ||||||
| dependencies: | dependencies: | ||||||
|   - name: dex |   - name: dex | ||||||
|     version: 0.24.0 |     version: 0.23.0 | ||||||
|     repository: https://charts.dexidp.io |     repository: https://charts.dexidp.io | ||||||
|   - name: oauth2-proxy |   - name: oauth2-proxy | ||||||
|     version: &chartVersion 7.18.0 |     version: &chartVersion 7.14.1 | ||||||
|     repository: https://oauth2-proxy.github.io/manifests |     repository: https://oauth2-proxy.github.io/manifests | ||||||
|   # https://github.com/postmanlabs/httpbin/issues/549 is still in progress, for now using a non-official chart |   # https://github.com/postmanlabs/httpbin/issues/549 is still in progress, for now using a non-official chart | ||||||
|   - name: httpbin |   - name: httpbin | ||||||
|  |  | ||||||
|  | @ -19,7 +19,7 @@ This command starts a local development server and open up a browser window. Mos | ||||||
| ## Build | ## Build | ||||||
| 
 | 
 | ||||||
| ```console | ```console | ||||||
| npm run build | npm build | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| This command generates static content into the `build` directory and can be served using any static contents hosting service. | This command generates static content into the `build` directory and can be served using any static contents hosting service. | ||||||
|  |  | ||||||
|  | @ -15,9 +15,7 @@ We suggest using [Visual Studio Code](https://code.visualstudio.com/docs/languag | ||||||
| 
 | 
 | ||||||
| # Go version | # Go version | ||||||
| 
 | 
 | ||||||
| See the `go.mod` file in the root of this repository for the version of Go used by this project. | This project is currently still using go 1.22. You can follow the installation guide for go [here.](https://go.dev/doc/install) And you can find go version 1.22 in the archived section [here.](https://go.dev/dl/) | ||||||
| You can follow [the installation guide for Go](https://go.dev/doc/install), |  | ||||||
| and you can find this specific Go version on [the Go downloads page](https://go.dev/dl/). |  | ||||||
| 
 | 
 | ||||||
| # Preparing your fork | # Preparing your fork | ||||||
| Clone your fork, create a feature branch and update the depedencies to get started. | Clone your fork, create a feature branch and update the depedencies to get started. | ||||||
|  |  | ||||||
|  | @ -77,15 +77,23 @@ server { | ||||||
| } | } | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| When you use ingress-nginx in Kubernetes, you can configure the same behavior with the following annotations on your Ingress resource: | When you use ingress-nginx in Kubernetes, you MUST use `kubernetes/ingress-nginx` (which includes the Lua module) and the following configuration snippet for your `Ingress`. | ||||||
|  | Variables set with `auth_request_set` are not `set`-able in plain nginx config when the location is processed via `proxy_pass` and then may only be processed by Lua. | ||||||
|  | Note that `nginxinc/kubernetes-ingress` does not include the Lua module. | ||||||
| 
 | 
 | ||||||
| ```yaml | ```yaml | ||||||
| nginx.ingress.kubernetes.io/auth-url: "https://<oauth2-proxy-fqdn>/oauth2/auth" | nginx.ingress.kubernetes.io/auth-response-headers: Authorization | ||||||
| nginx.ingress.kubernetes.io/auth-signin: "https://<oauth2-proxy-fqdn>/oauth2/start?rd=$escaped_request_uri" | nginx.ingress.kubernetes.io/auth-signin: https://$host/oauth2/start?rd=$escaped_request_uri | ||||||
|  | nginx.ingress.kubernetes.io/auth-url: https://$host/oauth2/auth | ||||||
|  | nginx.ingress.kubernetes.io/configuration-snippet: | | ||||||
|  |   auth_request_set $name_upstream_1 $upstream_cookie_name_1; | ||||||
|  | 
 | ||||||
|  |   access_by_lua_block { | ||||||
|  |     if ngx.var.name_upstream_1 ~= "" then | ||||||
|  |       ngx.header["Set-Cookie"] = "name_1=" .. ngx.var.name_upstream_1 .. ngx.var.auth_cookie:match("(; .*)") | ||||||
|  |     end | ||||||
|  |   } | ||||||
| ``` | ``` | ||||||
| 
 |  | ||||||
| This minimal configuration works for standard authentication flows. Lua/cookie handling is only needed for advanced scenarios (e.g., multi-part cookies, custom session logic). See the official ingress-nginx example: https://kubernetes.github.io/ingress-nginx/examples/auth/oauth-external-auth/. |  | ||||||
| 
 |  | ||||||
| It is recommended to use `--session-store-type=redis` when expecting large sessions/OIDC tokens (_e.g._ with MS Azure). | It is recommended to use `--session-store-type=redis` when expecting large sessions/OIDC tokens (_e.g._ with MS Azure). | ||||||
| 
 | 
 | ||||||
| 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=". | 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=". | ||||||
|  |  | ||||||
|  | @ -25,7 +25,6 @@ Valid providers are : | ||||||
| - [Microsoft Entra ID](ms_entra_id.md) | - [Microsoft Entra ID](ms_entra_id.md) | ||||||
| - [Nextcloud](nextcloud.md) | - [Nextcloud](nextcloud.md) | ||||||
| - [OpenID Connect](openid_connect.md) | - [OpenID Connect](openid_connect.md) | ||||||
| - [SourceHut](sourcehut.md) |  | ||||||
| 
 | 
 | ||||||
| The provider can be selected using the `provider` configuration value, or set in the [`providers` array using AlphaConfig](https://oauth2-proxy.github.io/oauth2-proxy/configuration/alpha-config#providers). However, [**the feature to implement multiple providers is not complete**](https://github.com/oauth2-proxy/oauth2-proxy/issues/926). | The provider can be selected using the `provider` configuration value, or set in the [`providers` array using AlphaConfig](https://oauth2-proxy.github.io/oauth2-proxy/configuration/alpha-config#providers). However, [**the feature to implement multiple providers is not complete**](https://github.com/oauth2-proxy/oauth2-proxy/issues/926). | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -112,7 +112,7 @@ insecure_oidc_skip_issuer_verification=true | ||||||
| ``` | ``` | ||||||
| `insecure_oidc_skip_issuer_verification` setting is required to disable following checks: | `insecure_oidc_skip_issuer_verification` setting is required to disable following checks: | ||||||
| * Startup check for matching issuer URL returned from [discovery document](https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration) with `oidc_issuer_url` setting. Required, as document's `issuer` field doesn't equal to `https://login.microsoftonline.com/common/v2.0`. See [OIDC Discovery 4.3](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationValidation). | * Startup check for matching issuer URL returned from [discovery document](https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration) with `oidc_issuer_url` setting. Required, as document's `issuer` field doesn't equal to `https://login.microsoftonline.com/common/v2.0`. See [OIDC Discovery 4.3](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationValidation). | ||||||
| * Matching ID token's `issuer` claim with `oidc_issuer_url` setting during ID token validation. Required to support tokens issued by different tenants. See [OIDC Core 3.1.3.7](https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation). | * Matching ID token's `issuer` claim with `oidc_issuer_url` setting during ID token validation. Required to support tokens issued by diffrerent tenants. See [OIDC Core 3.1.3.7](https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation).  | ||||||
| 
 | 
 | ||||||
| To provide additional security, Entra ID provider performs check on the ID token's `issuer` claim to match the `https://login.microsoftonline.com/{tenant-id}/v2.0` template. | To provide additional security, Entra ID provider performs check on the ID token's `issuer` claim to match the `https://login.microsoftonline.com/{tenant-id}/v2.0` template. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -21,5 +21,5 @@ to the appropriate URLs: | ||||||
| 
 | 
 | ||||||
| The default configuration allows everyone with an account to authenticate. | The default configuration allows everyone with an account to authenticate. | ||||||
| Restricting access is currently only supported by | Restricting access is currently only supported by | ||||||
| [email](index.md#email-authentication). | [email](#email-authentication). | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -47,7 +47,6 @@ const sidebars = { | ||||||
|             "configuration/providers/ms_entra_id", |             "configuration/providers/ms_entra_id", | ||||||
|             "configuration/providers/nextcloud", |             "configuration/providers/nextcloud", | ||||||
|             "configuration/providers/openid_connect", |             "configuration/providers/openid_connect", | ||||||
|             "configuration/providers/sourcehut" |  | ||||||
|           ], |           ], | ||||||
|         }, |         }, | ||||||
|         'configuration/session_storage', |         'configuration/session_storage', | ||||||
|  |  | ||||||
|  | @ -77,15 +77,23 @@ server { | ||||||
| } | } | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| When you use ingress-nginx in Kubernetes, you can configure the same behavior with the following annotations on your Ingress resource: | When you use ingress-nginx in Kubernetes, you MUST use `kubernetes/ingress-nginx` (which includes the Lua module) and the following configuration snippet for your `Ingress`. | ||||||
|  | Variables set with `auth_request_set` are not `set`-able in plain nginx config when the location is processed via `proxy_pass` and then may only be processed by Lua. | ||||||
|  | Note that `nginxinc/kubernetes-ingress` does not include the Lua module. | ||||||
| 
 | 
 | ||||||
| ```yaml | ```yaml | ||||||
| nginx.ingress.kubernetes.io/auth-url: "https://<oauth2-proxy-fqdn>/oauth2/auth" | nginx.ingress.kubernetes.io/auth-response-headers: Authorization | ||||||
| nginx.ingress.kubernetes.io/auth-signin: "https://<oauth2-proxy-fqdn>/oauth2/start?rd=$escaped_request_uri" | nginx.ingress.kubernetes.io/auth-signin: https://$host/oauth2/start?rd=$escaped_request_uri | ||||||
|  | nginx.ingress.kubernetes.io/auth-url: https://$host/oauth2/auth | ||||||
|  | nginx.ingress.kubernetes.io/configuration-snippet: | | ||||||
|  |   auth_request_set $name_upstream_1 $upstream_cookie_name_1; | ||||||
|  | 
 | ||||||
|  |   access_by_lua_block { | ||||||
|  |     if ngx.var.name_upstream_1 ~= "" then | ||||||
|  |       ngx.header["Set-Cookie"] = "name_1=" .. ngx.var.name_upstream_1 .. ngx.var.auth_cookie:match("(; .*)") | ||||||
|  |     end | ||||||
|  |   } | ||||||
| ``` | ``` | ||||||
| 
 |  | ||||||
| This minimal configuration works for standard authentication flows. Lua/cookie handling is only needed for advanced scenarios (e.g., multi-part cookies, custom session logic). See the official ingress-nginx example: https://kubernetes.github.io/ingress-nginx/examples/auth/oauth-external-auth/. |  | ||||||
| 
 |  | ||||||
| It is recommended to use `--session-store-type=redis` when expecting large sessions/OIDC tokens (_e.g._ with MS Azure). | It is recommended to use `--session-store-type=redis` when expecting large sessions/OIDC tokens (_e.g._ with MS Azure). | ||||||
| 
 | 
 | ||||||
| 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=". | 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=". | ||||||
|  |  | ||||||
|  | @ -112,7 +112,7 @@ insecure_oidc_skip_issuer_verification=true | ||||||
| ``` | ``` | ||||||
| `insecure_oidc_skip_issuer_verification` setting is required to disable following checks: | `insecure_oidc_skip_issuer_verification` setting is required to disable following checks: | ||||||
| * Startup check for matching issuer URL returned from [discovery document](https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration) with `oidc_issuer_url` setting. Required, as document's `issuer` field doesn't equal to `https://login.microsoftonline.com/common/v2.0`. See [OIDC Discovery 4.3](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationValidation). | * Startup check for matching issuer URL returned from [discovery document](https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration) with `oidc_issuer_url` setting. Required, as document's `issuer` field doesn't equal to `https://login.microsoftonline.com/common/v2.0`. See [OIDC Discovery 4.3](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationValidation). | ||||||
| * Matching ID token's `issuer` claim with `oidc_issuer_url` setting during ID token validation. Required to support tokens issued by different tenants. See [OIDC Core 3.1.3.7](https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation). | * Matching ID token's `issuer` claim with `oidc_issuer_url` setting during ID token validation. Required to support tokens issued by diffrerent tenants. See [OIDC Core 3.1.3.7](https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation).  | ||||||
| 
 | 
 | ||||||
| To provide additional security, Entra ID provider performs check on the ID token's `issuer` claim to match the `https://login.microsoftonline.com/{tenant-id}/v2.0` template. | To provide additional security, Entra ID provider performs check on the ID token's `issuer` claim to match the `https://login.microsoftonline.com/{tenant-id}/v2.0` template. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -77,15 +77,23 @@ server { | ||||||
| } | } | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| When you use ingress-nginx in Kubernetes, you can configure the same behavior with the following annotations on your Ingress resource: | When you use ingress-nginx in Kubernetes, you MUST use `kubernetes/ingress-nginx` (which includes the Lua module) and the following configuration snippet for your `Ingress`. | ||||||
|  | Variables set with `auth_request_set` are not `set`-able in plain nginx config when the location is processed via `proxy_pass` and then may only be processed by Lua. | ||||||
|  | Note that `nginxinc/kubernetes-ingress` does not include the Lua module. | ||||||
| 
 | 
 | ||||||
| ```yaml | ```yaml | ||||||
| nginx.ingress.kubernetes.io/auth-url: "https://<oauth2-proxy-fqdn>/oauth2/auth" | nginx.ingress.kubernetes.io/auth-response-headers: Authorization | ||||||
| nginx.ingress.kubernetes.io/auth-signin: "https://<oauth2-proxy-fqdn>/oauth2/start?rd=$escaped_request_uri" | nginx.ingress.kubernetes.io/auth-signin: https://$host/oauth2/start?rd=$escaped_request_uri | ||||||
|  | nginx.ingress.kubernetes.io/auth-url: https://$host/oauth2/auth | ||||||
|  | nginx.ingress.kubernetes.io/configuration-snippet: | | ||||||
|  |   auth_request_set $name_upstream_1 $upstream_cookie_name_1; | ||||||
|  | 
 | ||||||
|  |   access_by_lua_block { | ||||||
|  |     if ngx.var.name_upstream_1 ~= "" then | ||||||
|  |       ngx.header["Set-Cookie"] = "name_1=" .. ngx.var.name_upstream_1 .. ngx.var.auth_cookie:match("(; .*)") | ||||||
|  |     end | ||||||
|  |   } | ||||||
| ``` | ``` | ||||||
| 
 |  | ||||||
| This minimal configuration works for standard authentication flows. Lua/cookie handling is only needed for advanced scenarios (e.g., multi-part cookies, custom session logic). See the official ingress-nginx example: https://kubernetes.github.io/ingress-nginx/examples/auth/oauth-external-auth/. |  | ||||||
| 
 |  | ||||||
| It is recommended to use `--session-store-type=redis` when expecting large sessions/OIDC tokens (_e.g._ with MS Azure). | It is recommended to use `--session-store-type=redis` when expecting large sessions/OIDC tokens (_e.g._ with MS Azure). | ||||||
| 
 | 
 | ||||||
| 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=". | 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=". | ||||||
|  |  | ||||||
|  | @ -24,7 +24,6 @@ Valid providers are : | ||||||
| - [Microsoft Entra ID](ms_entra_id.md) | - [Microsoft Entra ID](ms_entra_id.md) | ||||||
| - [Nextcloud](nextcloud.md) | - [Nextcloud](nextcloud.md) | ||||||
| - [OpenID Connect](openid_connect.md) | - [OpenID Connect](openid_connect.md) | ||||||
| - [SourceHut](sourcehut.md) |  | ||||||
| 
 | 
 | ||||||
| The provider can be selected using the `provider` configuration value, or set in the [`providers` array using AlphaConfig](https://oauth2-proxy.github.io/oauth2-proxy/configuration/alpha-config#providers). However, [**the feature to implement multiple providers is not complete**](https://github.com/oauth2-proxy/oauth2-proxy/issues/926). | The provider can be selected using the `provider` configuration value, or set in the [`providers` array using AlphaConfig](https://oauth2-proxy.github.io/oauth2-proxy/configuration/alpha-config#providers). However, [**the feature to implement multiple providers is not complete**](https://github.com/oauth2-proxy/oauth2-proxy/issues/926). | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -112,7 +112,7 @@ insecure_oidc_skip_issuer_verification=true | ||||||
| ``` | ``` | ||||||
| `insecure_oidc_skip_issuer_verification` setting is required to disable following checks: | `insecure_oidc_skip_issuer_verification` setting is required to disable following checks: | ||||||
| * Startup check for matching issuer URL returned from [discovery document](https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration) with `oidc_issuer_url` setting. Required, as document's `issuer` field doesn't equal to `https://login.microsoftonline.com/common/v2.0`. See [OIDC Discovery 4.3](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationValidation). | * Startup check for matching issuer URL returned from [discovery document](https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration) with `oidc_issuer_url` setting. Required, as document's `issuer` field doesn't equal to `https://login.microsoftonline.com/common/v2.0`. See [OIDC Discovery 4.3](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationValidation). | ||||||
| * Matching ID token's `issuer` claim with `oidc_issuer_url` setting during ID token validation. Required to support tokens issued by different tenants. See [OIDC Core 3.1.3.7](https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation). | * Matching ID token's `issuer` claim with `oidc_issuer_url` setting during ID token validation. Required to support tokens issued by diffrerent tenants. See [OIDC Core 3.1.3.7](https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation).  | ||||||
| 
 | 
 | ||||||
| To provide additional security, Entra ID provider performs check on the ID token's `issuer` claim to match the `https://login.microsoftonline.com/{tenant-id}/v2.0` template. | To provide additional security, Entra ID provider performs check on the ID token's `issuer` claim to match the `https://login.microsoftonline.com/{tenant-id}/v2.0` template. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -21,5 +21,5 @@ to the appropriate URLs: | ||||||
| 
 | 
 | ||||||
| The default configuration allows everyone with an account to authenticate. | The default configuration allows everyone with an account to authenticate. | ||||||
| Restricting access is currently only supported by | Restricting access is currently only supported by | ||||||
| [email](index.md#email-authentication). | [email](#email-authentication). | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -77,15 +77,23 @@ server { | ||||||
| } | } | ||||||
| ``` | ``` | ||||||
| 
 | 
 | ||||||
| When you use ingress-nginx in Kubernetes, you can configure the same behavior with the following annotations on your Ingress resource: | When you use ingress-nginx in Kubernetes, you MUST use `kubernetes/ingress-nginx` (which includes the Lua module) and the following configuration snippet for your `Ingress`. | ||||||
|  | Variables set with `auth_request_set` are not `set`-able in plain nginx config when the location is processed via `proxy_pass` and then may only be processed by Lua. | ||||||
|  | Note that `nginxinc/kubernetes-ingress` does not include the Lua module. | ||||||
| 
 | 
 | ||||||
| ```yaml | ```yaml | ||||||
| nginx.ingress.kubernetes.io/auth-url: "https://<oauth2-proxy-fqdn>/oauth2/auth" | nginx.ingress.kubernetes.io/auth-response-headers: Authorization | ||||||
| nginx.ingress.kubernetes.io/auth-signin: "https://<oauth2-proxy-fqdn>/oauth2/start?rd=$escaped_request_uri" | nginx.ingress.kubernetes.io/auth-signin: https://$host/oauth2/start?rd=$escaped_request_uri | ||||||
|  | nginx.ingress.kubernetes.io/auth-url: https://$host/oauth2/auth | ||||||
|  | nginx.ingress.kubernetes.io/configuration-snippet: | | ||||||
|  |   auth_request_set $name_upstream_1 $upstream_cookie_name_1; | ||||||
|  | 
 | ||||||
|  |   access_by_lua_block { | ||||||
|  |     if ngx.var.name_upstream_1 ~= "" then | ||||||
|  |       ngx.header["Set-Cookie"] = "name_1=" .. ngx.var.name_upstream_1 .. ngx.var.auth_cookie:match("(; .*)") | ||||||
|  |     end | ||||||
|  |   } | ||||||
| ``` | ``` | ||||||
| 
 |  | ||||||
| This minimal configuration works for standard authentication flows. Lua/cookie handling is only needed for advanced scenarios (e.g., multi-part cookies, custom session logic). See the official ingress-nginx example: https://kubernetes.github.io/ingress-nginx/examples/auth/oauth-external-auth/. |  | ||||||
| 
 |  | ||||||
| It is recommended to use `--session-store-type=redis` when expecting large sessions/OIDC tokens (_e.g._ with MS Azure). | It is recommended to use `--session-store-type=redis` when expecting large sessions/OIDC tokens (_e.g._ with MS Azure). | ||||||
| 
 | 
 | ||||||
| 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=". | 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=". | ||||||
|  |  | ||||||
|  | @ -25,7 +25,6 @@ Valid providers are : | ||||||
| - [Microsoft Entra ID](ms_entra_id.md) | - [Microsoft Entra ID](ms_entra_id.md) | ||||||
| - [Nextcloud](nextcloud.md) | - [Nextcloud](nextcloud.md) | ||||||
| - [OpenID Connect](openid_connect.md) | - [OpenID Connect](openid_connect.md) | ||||||
| - [SourceHut](sourcehut.md) |  | ||||||
| 
 | 
 | ||||||
| The provider can be selected using the `provider` configuration value, or set in the [`providers` array using AlphaConfig](https://oauth2-proxy.github.io/oauth2-proxy/configuration/alpha-config#providers). However, [**the feature to implement multiple providers is not complete**](https://github.com/oauth2-proxy/oauth2-proxy/issues/926). | The provider can be selected using the `provider` configuration value, or set in the [`providers` array using AlphaConfig](https://oauth2-proxy.github.io/oauth2-proxy/configuration/alpha-config#providers). However, [**the feature to implement multiple providers is not complete**](https://github.com/oauth2-proxy/oauth2-proxy/issues/926). | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -112,7 +112,7 @@ insecure_oidc_skip_issuer_verification=true | ||||||
| ``` | ``` | ||||||
| `insecure_oidc_skip_issuer_verification` setting is required to disable following checks: | `insecure_oidc_skip_issuer_verification` setting is required to disable following checks: | ||||||
| * Startup check for matching issuer URL returned from [discovery document](https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration) with `oidc_issuer_url` setting. Required, as document's `issuer` field doesn't equal to `https://login.microsoftonline.com/common/v2.0`. See [OIDC Discovery 4.3](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationValidation). | * Startup check for matching issuer URL returned from [discovery document](https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration) with `oidc_issuer_url` setting. Required, as document's `issuer` field doesn't equal to `https://login.microsoftonline.com/common/v2.0`. See [OIDC Discovery 4.3](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationValidation). | ||||||
| * Matching ID token's `issuer` claim with `oidc_issuer_url` setting during ID token validation. Required to support tokens issued by different tenants. See [OIDC Core 3.1.3.7](https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation). | * Matching ID token's `issuer` claim with `oidc_issuer_url` setting during ID token validation. Required to support tokens issued by diffrerent tenants. See [OIDC Core 3.1.3.7](https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation).  | ||||||
| 
 | 
 | ||||||
| To provide additional security, Entra ID provider performs check on the ID token's `issuer` claim to match the `https://login.microsoftonline.com/{tenant-id}/v2.0` template. | To provide additional security, Entra ID provider performs check on the ID token's `issuer` claim to match the `https://login.microsoftonline.com/{tenant-id}/v2.0` template. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -21,5 +21,5 @@ to the appropriate URLs: | ||||||
| 
 | 
 | ||||||
| The default configuration allows everyone with an account to authenticate. | The default configuration allows everyone with an account to authenticate. | ||||||
| Restricting access is currently only supported by | Restricting access is currently only supported by | ||||||
| [email](index.md#email-authentication). | [email](#email-authentication). | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -112,7 +112,7 @@ insecure_oidc_skip_issuer_verification=true | ||||||
| ``` | ``` | ||||||
| `insecure_oidc_skip_issuer_verification` setting is required to disable following checks: | `insecure_oidc_skip_issuer_verification` setting is required to disable following checks: | ||||||
| * Startup check for matching issuer URL returned from [discovery document](https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration) with `oidc_issuer_url` setting. Required, as document's `issuer` field doesn't equal to `https://login.microsoftonline.com/common/v2.0`. See [OIDC Discovery 4.3](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationValidation). | * Startup check for matching issuer URL returned from [discovery document](https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration) with `oidc_issuer_url` setting. Required, as document's `issuer` field doesn't equal to `https://login.microsoftonline.com/common/v2.0`. See [OIDC Discovery 4.3](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationValidation). | ||||||
| * Matching ID token's `issuer` claim with `oidc_issuer_url` setting during ID token validation. Required to support tokens issued by different tenants. See [OIDC Core 3.1.3.7](https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation). | * Matching ID token's `issuer` claim with `oidc_issuer_url` setting during ID token validation. Required to support tokens issued by diffrerent tenants. See [OIDC Core 3.1.3.7](https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation).  | ||||||
| 
 | 
 | ||||||
| To provide additional security, Entra ID provider performs check on the ID token's `issuer` claim to match the `https://login.microsoftonline.com/{tenant-id}/v2.0` template. | To provide additional security, Entra ID provider performs check on the ID token's `issuer` claim to match the `https://login.microsoftonline.com/{tenant-id}/v2.0` template. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -112,7 +112,7 @@ insecure_oidc_skip_issuer_verification=true | ||||||
| ``` | ``` | ||||||
| `insecure_oidc_skip_issuer_verification` setting is required to disable following checks: | `insecure_oidc_skip_issuer_verification` setting is required to disable following checks: | ||||||
| * Startup check for matching issuer URL returned from [discovery document](https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration) with `oidc_issuer_url` setting. Required, as document's `issuer` field doesn't equal to `https://login.microsoftonline.com/common/v2.0`. See [OIDC Discovery 4.3](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationValidation). | * Startup check for matching issuer URL returned from [discovery document](https://login.microsoftonline.com/common/v2.0/.well-known/openid-configuration) with `oidc_issuer_url` setting. Required, as document's `issuer` field doesn't equal to `https://login.microsoftonline.com/common/v2.0`. See [OIDC Discovery 4.3](https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfigurationValidation). | ||||||
| * Matching ID token's `issuer` claim with `oidc_issuer_url` setting during ID token validation. Required to support tokens issued by different tenants. See [OIDC Core 3.1.3.7](https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation). | * Matching ID token's `issuer` claim with `oidc_issuer_url` setting during ID token validation. Required to support tokens issued by diffrerent tenants. See [OIDC Core 3.1.3.7](https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation).  | ||||||
| 
 | 
 | ||||||
| To provide additional security, Entra ID provider performs check on the ID token's `issuer` claim to match the `https://login.microsoftonline.com/{tenant-id}/v2.0` template. | To provide additional security, Entra ID provider performs check on the ID token's `issuer` claim to match the `https://login.microsoftonline.com/{tenant-id}/v2.0` template. | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -46,8 +46,7 @@ | ||||||
|             "configuration/providers/login_gov", |             "configuration/providers/login_gov", | ||||||
|             "configuration/providers/ms_entra_id", |             "configuration/providers/ms_entra_id", | ||||||
|             "configuration/providers/nextcloud", |             "configuration/providers/nextcloud", | ||||||
|             "configuration/providers/openid_connect", |             "configuration/providers/openid_connect" | ||||||
|             "configuration/providers/sourcehut" |  | ||||||
|           ] |           ] | ||||||
|         }, |         }, | ||||||
|         "configuration/session_storage", |         "configuration/session_storage", | ||||||
|  |  | ||||||
|  | @ -46,8 +46,7 @@ | ||||||
|             "configuration/providers/login_gov", |             "configuration/providers/login_gov", | ||||||
|             "configuration/providers/ms_entra_id", |             "configuration/providers/ms_entra_id", | ||||||
|             "configuration/providers/nextcloud", |             "configuration/providers/nextcloud", | ||||||
|             "configuration/providers/openid_connect", |             "configuration/providers/openid_connect" | ||||||
|             "configuration/providers/sourcehut" |  | ||||||
|           ] |           ] | ||||||
|         }, |         }, | ||||||
|         "configuration/session_storage", |         "configuration/session_storage", | ||||||
|  |  | ||||||
							
								
								
									
										3
									
								
								go.mod
								
								
								
								
							
							
						
						
									
										3
									
								
								go.mod
								
								
								
								
							|  | @ -1,12 +1,13 @@ | ||||||
| module github.com/oauth2-proxy/oauth2-proxy/v7 | module github.com/oauth2-proxy/oauth2-proxy/v7 | ||||||
| 
 | 
 | ||||||
| go 1.25.3 | go 1.24.6 | ||||||
| 
 | 
 | ||||||
| require ( | require ( | ||||||
| 	cloud.google.com/go/compute/metadata v0.7.0 | 	cloud.google.com/go/compute/metadata v0.7.0 | ||||||
| 	github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb | 	github.com/Bose/minisentinel v0.0.0-20200130220412-917c5a9223bb | ||||||
| 	github.com/a8m/envsubst v1.4.3 | 	github.com/a8m/envsubst v1.4.3 | ||||||
| 	github.com/alicebob/miniredis/v2 v2.35.0 | 	github.com/alicebob/miniredis/v2 v2.35.0 | ||||||
|  | 	github.com/benbjohnson/clock v1.3.5 | ||||||
| 	github.com/bitly/go-simplejson v0.5.1 | 	github.com/bitly/go-simplejson v0.5.1 | ||||||
| 	github.com/bsm/redislock v0.9.4 | 	github.com/bsm/redislock v0.9.4 | ||||||
| 	github.com/coreos/go-oidc/v3 v3.14.1 | 	github.com/coreos/go-oidc/v3 v3.14.1 | ||||||
|  |  | ||||||
							
								
								
									
										36
									
								
								go.sum
								
								
								
								
							
							
						
						
									
										36
									
								
								go.sum
								
								
								
								
							|  | @ -14,6 +14,8 @@ github.com/alicebob/gopher-json v0.0.0-20180125190556-5a6b3ba71ee6/go.mod h1:SGn | ||||||
| github.com/alicebob/miniredis/v2 v2.11.1/go.mod h1:UA48pmi7aSazcGAvcdKcBB49z521IC9VjTTRz2nIaJE= | github.com/alicebob/miniredis/v2 v2.11.1/go.mod h1:UA48pmi7aSazcGAvcdKcBB49z521IC9VjTTRz2nIaJE= | ||||||
| github.com/alicebob/miniredis/v2 v2.35.0 h1:QwLphYqCEAo1eu1TqPRN2jgVMPBweeQcR21jeqDCONI= | github.com/alicebob/miniredis/v2 v2.35.0 h1:QwLphYqCEAo1eu1TqPRN2jgVMPBweeQcR21jeqDCONI= | ||||||
| github.com/alicebob/miniredis/v2 v2.35.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM= | github.com/alicebob/miniredis/v2 v2.35.0/go.mod h1:TcL7YfarKPGDAthEtl5NBeHZfeUQj6OXMm/+iu5cLMM= | ||||||
|  | github.com/benbjohnson/clock v1.3.5 h1:VvXlSJBzZpA/zum6Sj74hxwYI2DIxRWuNIoXAzHZz5o= | ||||||
|  | github.com/benbjohnson/clock v1.3.5/go.mod h1:J11/hYXuz8f4ySSvYwY0FKfm+ezbsZBKZxNJlLklBHA= | ||||||
| github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= | github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= | ||||||
| github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= | github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= | ||||||
| github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow= | github.com/bitly/go-simplejson v0.5.1 h1:xgwPbetQScXt1gh9BmoJ6j9JMr3TElvuIyjR8pgdoow= | ||||||
|  | @ -59,8 +61,12 @@ github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= | ||||||
| github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= | github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= | ||||||
| github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= | github.com/go-task/slim-sprig/v3 v3.0.0 h1:sUs3vkvUymDpBKi3qH1YSqBQk9+9D/8M2mN1vB6EwHI= | ||||||
| github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= | github.com/go-task/slim-sprig/v3 v3.0.0/go.mod h1:W848ghGpv3Qj3dhTPRyJypKRiqCdHZiAzKg9hl15HA8= | ||||||
|  | github.com/go-viper/mapstructure/v2 v2.3.0 h1:27XbWsHIqhbdR5TIC911OfYvgSaW93HM+dX7970Q7jk= | ||||||
|  | github.com/go-viper/mapstructure/v2 v2.3.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= | ||||||
| github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= | github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= | ||||||
| github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= | github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= | ||||||
|  | github.com/golang-jwt/jwt/v5 v5.2.2 h1:Rl4B7itRWVtYIHFrSNd7vhTiz9UpLdi6gZhZ3wEeDy8= | ||||||
|  | github.com/golang-jwt/jwt/v5 v5.2.2/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= | ||||||
| github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0= | github.com/golang-jwt/jwt/v5 v5.2.3 h1:kkGXqQOBSDDWRhWNXTFpqGSCMyh/PLnqUvMGJPDJDs0= | ||||||
| github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= | github.com/golang-jwt/jwt/v5 v5.2.3/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk= | ||||||
| github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= | github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= | ||||||
|  | @ -79,6 +85,8 @@ github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0= | ||||||
| github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo= | ||||||
| github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= | github.com/googleapis/enterprise-certificate-proxy v0.3.6 h1:GW/XbdyBFQ8Qe+YAmFU9uHLo7OnF5tL52HFAgMmyrf4= | ||||||
| github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= | github.com/googleapis/enterprise-certificate-proxy v0.3.6/go.mod h1:MkHOF77EYAE7qfSuSS9PU6g4Nt4e11cnsDUowfwewLA= | ||||||
|  | github.com/googleapis/gax-go/v2 v2.14.2 h1:eBLnkZ9635krYIPD+ag1USrOAI0Nr0QYF3+/3GqO0k0= | ||||||
|  | github.com/googleapis/gax-go/v2 v2.14.2/go.mod h1:ON64QhlJkhVtSqp4v1uaK92VyZ2gmvDQsweuyLV+8+w= | ||||||
| github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= | github.com/googleapis/gax-go/v2 v2.15.0 h1:SyjDc1mGgZU5LncH8gimWo9lW1DtIfPibOG81vgd/bo= | ||||||
| github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= | github.com/googleapis/gax-go/v2 v2.15.0/go.mod h1:zVVkkxAQHa1RQpg9z2AUCMnKhi0Qld9rcmyfL1OZhoc= | ||||||
| github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= | github.com/gorilla/mux v1.8.1 h1:TuBL49tXwgrFYWhqrNgrUNEY92u81SPhu7sTdzQEiWY= | ||||||
|  | @ -134,6 +142,8 @@ github.com/spf13/afero v1.14.0 h1:9tH6MapGnn/j0eb0yIXiLjERO8RB6xIVZRDCX7PtqWA= | ||||||
| github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= | github.com/spf13/afero v1.14.0/go.mod h1:acJQ8t0ohCGuMN3O+Pv0V0hgMxNYDlvdk+VTfyZmbYo= | ||||||
| github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE= | github.com/spf13/cast v1.9.2 h1:SsGfm7M8QOFtEzumm7UZrZdLLquNdzFYfIbEXntcFbE= | ||||||
| github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= | github.com/spf13/cast v1.9.2/go.mod h1:jNfB8QC9IA6ZuY2ZjDp0KtFO2LZZlg4S/7bzP6qqeHo= | ||||||
|  | github.com/spf13/pflag v1.0.6 h1:jFzHGLGAlb3ruxLB8MhbI6A8+AQX/2eW4qeyNZXNp2o= | ||||||
|  | github.com/spf13/pflag v1.0.6/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | ||||||
| github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= | github.com/spf13/pflag v1.0.7 h1:vN6T9TfwStFPFM5XzjsvmzZkLuaLX+HS+0SeFLRgU6M= | ||||||
| github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | github.com/spf13/pflag v1.0.7/go.mod h1:McXfInJRrz4CZXVZOBLb0bTZqETkiAhM9Iw0y3An2Bg= | ||||||
| github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= | github.com/spf13/viper v1.20.1 h1:ZMi+z/lvLyPSCoNtFCpqjy0S4kPbirhpTMwl8BkW9X4= | ||||||
|  | @ -176,6 +186,8 @@ go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN8 | ||||||
| golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= | ||||||
| golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= | ||||||
| golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= | golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU= | ||||||
|  | golang.org/x/crypto v0.39.0 h1:SHs+kF4LP+f+p14esP5jAoDpHU8Gu/v9lFRK6IT5imM= | ||||||
|  | golang.org/x/crypto v0.39.0/go.mod h1:L+Xg3Wf6HoL4Bn4238Z6ft6KfEpN0tJGo53AAPC632U= | ||||||
| golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= | golang.org/x/crypto v0.40.0 h1:r4x+VvoG5Fm+eJcxMaY8CQM7Lb0l1lsmjGBQ6s8BfKM= | ||||||
| golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= | golang.org/x/crypto v0.40.0/go.mod h1:Qr1vMER5WyS2dfPHAlsOj01wgLbsyWtFn/aY+5+ZdxY= | ||||||
| golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= | ||||||
|  | @ -185,6 +197,8 @@ golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v | ||||||
| golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= | ||||||
| golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= | ||||||
| golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg= | ||||||
|  | golang.org/x/net v0.41.0 h1:vBTly1HeNPEn3wtREYfy4GZ/NECgw2Cnl+nK6Nz3uvw= | ||||||
|  | golang.org/x/net v0.41.0/go.mod h1:B/K4NNqkfmg07DQYrbwvSluqCJOOXwUjeb/5lOisjbA= | ||||||
| golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= | golang.org/x/net v0.42.0 h1:jzkYrhi3YQWD6MLBJcsklgQsoAcw89EcZbJw8Z614hs= | ||||||
| golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= | golang.org/x/net v0.42.0/go.mod h1:FF1RA5d3u7nAYA4z2TkclSCKh68eSXtiFwcWQpPXdt8= | ||||||
| golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= | golang.org/x/oauth2 v0.30.0 h1:dnDm7JmhM45NNpd8FDDeLhK6FwqbOf4MLCM9zb1BOHI= | ||||||
|  | @ -192,6 +206,8 @@ golang.org/x/oauth2 v0.30.0/go.mod h1:B++QgG3ZKulg6sRPGD/mqlHQs5rB3Ml9erfeDY7xKl | ||||||
| golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
| golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
| golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= | ||||||
|  | golang.org/x/sync v0.15.0 h1:KWH3jNZsfyT6xfAfKiz6MRNmd46ByHDYaZ7KSkCtdW8= | ||||||
|  | golang.org/x/sync v0.15.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= | ||||||
| golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= | golang.org/x/sync v0.16.0 h1:ycBJEhp9p4vXvUZNszeOq0kGTPghopOL8q0fq3vstxw= | ||||||
| golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= | golang.org/x/sync v0.16.0/go.mod h1:1dzgHSNfp02xaA81J2MS99Qcpr2w7fw1gpm99rleRqA= | ||||||
| golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | golang.org/x/sys v0.0.0-20190204203706-41f3e6584952/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= | ||||||
|  | @ -203,6 +219,8 @@ golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBc | ||||||
| golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= | ||||||
| golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA= | ||||||
|  | golang.org/x/sys v0.33.0 h1:q3i8TbbEz+JRD9ywIRlyRAQbM0qF7hu24q3teo2hbuw= | ||||||
|  | golang.org/x/sys v0.33.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= | ||||||
| golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= | golang.org/x/sys v0.34.0 h1:H5Y5sJ2L2JRdyv7ROF1he/lPdvFsd0mJHFw2ThKHxLA= | ||||||
| golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= | golang.org/x/sys v0.34.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k= | ||||||
| golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= | ||||||
|  | @ -216,6 +234,8 @@ golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= | ||||||
| golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= | ||||||
| golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= | ||||||
| golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU= | ||||||
|  | golang.org/x/text v0.26.0 h1:P42AVeLghgTYr4+xUnTRKDMqpar+PtX7KWuNQL21L8M= | ||||||
|  | golang.org/x/text v0.26.0/go.mod h1:QK15LZJUUQVJxhz7wXgxSy/CJaTFjd0G+YLonydOVQA= | ||||||
| golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= | golang.org/x/text v0.27.0 h1:4fGWRpyh641NLlecmyl4LOe6yDdfaYNrGb2zdfo4JV4= | ||||||
| golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= | golang.org/x/text v0.27.0/go.mod h1:1D28KMCvyooCX9hBiosv5Tz/+YLxj0j7XhWjpSUF7CU= | ||||||
| golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= | golang.org/x/time v0.12.0 h1:ScB/8o8olJvc+CQPWrK3fPZNfh7qgwCrY0zJmoEQLSE= | ||||||
|  | @ -224,15 +244,25 @@ golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGm | ||||||
| golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo= | ||||||
| golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= | ||||||
| golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= | ||||||
|  | golang.org/x/tools v0.34.0 h1:qIpSLOxeCYGg9TrcJokLBG4KFA6d795g0xkBkiESGlo= | ||||||
|  | golang.org/x/tools v0.34.0/go.mod h1:pAP9OwEaY1CAW3HOmg3hLZC5Z0CCmzjAF2UQMSqNARg= | ||||||
| golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= | golang.org/x/tools v0.35.0 h1:mBffYraMEf7aa0sB+NuKnuCy8qI/9Bughn8dC2Gu5r0= | ||||||
| golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= | golang.org/x/tools v0.35.0/go.mod h1:NKdj5HkL/73byiZSJjqJgKn3ep7KjFkBOkR/Hps3VPw= | ||||||
| golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= | ||||||
|  | google.golang.org/api v0.240.0 h1:PxG3AA2UIqT1ofIzWV2COM3j3JagKTKSwy7L6RHNXNU= | ||||||
|  | google.golang.org/api v0.240.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50= | ||||||
|  | google.golang.org/api v0.241.0 h1:QKwqWQlkc6O895LchPEDUSYr22Xp3NCxpQRiWTB6avE= | ||||||
|  | google.golang.org/api v0.241.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50= | ||||||
| google.golang.org/api v0.242.0 h1:7Lnb1nfnpvbkCiZek6IXKdJ0MFuAZNAJKQfA1ws62xg= | google.golang.org/api v0.242.0 h1:7Lnb1nfnpvbkCiZek6IXKdJ0MFuAZNAJKQfA1ws62xg= | ||||||
| google.golang.org/api v0.242.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50= | google.golang.org/api v0.242.0/go.mod h1:cOVEm2TpdAGHL2z+UwyS+kmlGr3bVWQQ6sYEqkKje50= | ||||||
|  | google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2 h1:1tXaIXCracvtsRxSBsYDiSBN0cuJvM7QYW+MrpIRY78= | ||||||
|  | google.golang.org/genproto v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:49MsLSx0oWMOZqcpB3uL8ZOkAh1+TndpJ8ONoCBWiZk= | ||||||
| google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= | google.golang.org/genproto v0.0.0-20250603155806-513f23925822 h1:rHWScKit0gvAPuOnu87KpaYtjK5zBMLcULh7gxkCXu4= | ||||||
| google.golang.org/genproto v0.0.0-20250603155806-513f23925822/go.mod h1:HubltRL7rMh0LfnQPkMH4NPDFEWp0jw3vixw7jEM53s= | google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2 h1:vPV0tzlsK6EzEDHNNH5sa7Hs9bd7iXR7B1tSiPepkV0= | ||||||
|  | google.golang.org/genproto/googleapis/api v0.0.0-20250505200425-f936aa4a68b2/go.mod h1:pKLAc5OolXC3ViWGI62vvC0n10CpwAtRcTNCFwTKBEw= | ||||||
| google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY= | google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822 h1:oWVWY3NzT7KJppx2UKhKmzPq4SRe0LdCijVRwvGeikY= | ||||||
| google.golang.org/genproto/googleapis/api v0.0.0-20250603155806-513f23925822/go.mod h1:h3c4v36UTKzUiuaOKQ6gr3S+0hovBtUrXzTG/i3+XEc= | google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822 h1:fc6jSaCT0vBduLYZHYrBBNY4dsWuvgyff9noRNDdBeE= | ||||||
|  | google.golang.org/genproto/googleapis/rpc v0.0.0-20250603155806-513f23925822/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= | ||||||
| google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY= | google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7 h1:pFyd6EwwL2TqFf8emdthzeX+gZE1ElRq3iM8pui4KBY= | ||||||
| google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= | google.golang.org/genproto/googleapis/rpc v0.0.0-20250707201910-8d1bb00bc6a7/go.mod h1:qQ0YXyHHx3XkvlzUtpXDkS29lDSafHMZBAZDc03LQ3A= | ||||||
| google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= | google.golang.org/grpc v1.73.0 h1:VIWSmpI2MegBtTuFt5/JWy2oXxtjJ/e89Z70ImfD2ok= | ||||||
|  | @ -250,5 +280,7 @@ gopkg.in/yaml.v2 v2.4.0/go.mod h1:RDklbk79AGWmwhnvt/jBztapEOGDOx6ZbXqjP6csGnQ= | ||||||
| gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||||
| gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA= | ||||||
| gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM= | ||||||
|  | k8s.io/apimachinery v0.33.2 h1:IHFVhqg59mb8PJWTLi8m1mAoepkUNYmptHsV+Z1m5jY= | ||||||
|  | k8s.io/apimachinery v0.33.2/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= | ||||||
| k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA= | k8s.io/apimachinery v0.33.3 h1:4ZSrmNa0c/ZpZJhAgRdcsFcZOw1PQU1bALVQ0B3I5LA= | ||||||
| k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= | k8s.io/apimachinery v0.33.3/go.mod h1:BHW0YOu7n22fFv/JkYOEfkUYNRN0fj0BlvMFWA7b+SM= | ||||||
|  |  | ||||||
|  | @ -7,6 +7,7 @@ import ( | ||||||
| 	"io" | 	"io" | ||||||
| 	"time" | 	"time" | ||||||
| 
 | 
 | ||||||
|  | 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/clock" | ||||||
| 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption" | 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption" | ||||||
| 	"github.com/pierrec/lz4/v4" | 	"github.com/pierrec/lz4/v4" | ||||||
| 	"github.com/vmihailenco/msgpack/v5" | 	"github.com/vmihailenco/msgpack/v5" | ||||||
|  | @ -29,15 +30,8 @@ type SessionState struct { | ||||||
| 	PreferredUsername string   `msgpack:"pu,omitempty"` | 	PreferredUsername string   `msgpack:"pu,omitempty"` | ||||||
| 
 | 
 | ||||||
| 	// Internal helpers, not serialized
 | 	// Internal helpers, not serialized
 | ||||||
| 	Clock func() time.Time `msgpack:"-"` // override for time.Now, for testing
 | 	Clock clock.Clock `msgpack:"-"` | ||||||
| 	Lock  Lock             `msgpack:"-"` | 	Lock  Lock        `msgpack:"-"` | ||||||
| } |  | ||||||
| 
 |  | ||||||
| func (s *SessionState) now() time.Time { |  | ||||||
| 	if s.Clock != nil { |  | ||||||
| 		return s.Clock() |  | ||||||
| 	} |  | ||||||
| 	return time.Now() |  | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (s *SessionState) ObtainLock(ctx context.Context, expiration time.Duration) error { | func (s *SessionState) ObtainLock(ctx context.Context, expiration time.Duration) error { | ||||||
|  | @ -70,7 +64,7 @@ func (s *SessionState) PeekLock(ctx context.Context) (bool, error) { | ||||||
| 
 | 
 | ||||||
| // CreatedAtNow sets a SessionState's CreatedAt to now
 | // CreatedAtNow sets a SessionState's CreatedAt to now
 | ||||||
| func (s *SessionState) CreatedAtNow() { | func (s *SessionState) CreatedAtNow() { | ||||||
| 	now := s.now() | 	now := s.Clock.Now() | ||||||
| 	s.CreatedAt = &now | 	s.CreatedAt = &now | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -91,7 +85,7 @@ func (s *SessionState) ExpiresIn(d time.Duration) { | ||||||
| 
 | 
 | ||||||
| // IsExpired checks whether the session has expired
 | // IsExpired checks whether the session has expired
 | ||||||
| func (s *SessionState) IsExpired() bool { | func (s *SessionState) IsExpired() bool { | ||||||
| 	if s.ExpiresOn != nil && !s.ExpiresOn.IsZero() && s.ExpiresOn.Before(s.now()) { | 	if s.ExpiresOn != nil && !s.ExpiresOn.IsZero() && s.ExpiresOn.Before(s.Clock.Now()) { | ||||||
| 		return true | 		return true | ||||||
| 	} | 	} | ||||||
| 	return false | 	return false | ||||||
|  | @ -100,7 +94,7 @@ func (s *SessionState) IsExpired() bool { | ||||||
| // Age returns the age of a session
 | // Age returns the age of a session
 | ||||||
| func (s *SessionState) Age() time.Duration { | func (s *SessionState) Age() time.Duration { | ||||||
| 	if s.CreatedAt != nil && !s.CreatedAt.IsZero() { | 	if s.CreatedAt != nil && !s.CreatedAt.IsZero() { | ||||||
| 		return s.now().Truncate(time.Second).Sub(*s.CreatedAt) | 		return s.Clock.Now().Truncate(time.Second).Sub(*s.CreatedAt) | ||||||
| 	} | 	} | ||||||
| 	return 0 | 	return 0 | ||||||
| } | } | ||||||
|  |  | ||||||
|  | @ -22,7 +22,7 @@ func TestCreatedAtNow(t *testing.T) { | ||||||
| 	ss := &SessionState{} | 	ss := &SessionState{} | ||||||
| 
 | 
 | ||||||
| 	now := time.Unix(1234567890, 0) | 	now := time.Unix(1234567890, 0) | ||||||
| 	ss.Clock = func() time.Time { return now } | 	ss.Clock.Set(now) | ||||||
| 
 | 
 | ||||||
| 	ss.CreatedAtNow() | 	ss.CreatedAtNow() | ||||||
| 	g.Expect(*ss.CreatedAt).To(Equal(now)) | 	g.Expect(*ss.CreatedAt).To(Equal(now)) | ||||||
|  | @ -33,9 +33,9 @@ func TestExpiresIn(t *testing.T) { | ||||||
| 	ss := &SessionState{} | 	ss := &SessionState{} | ||||||
| 
 | 
 | ||||||
| 	now := time.Unix(1234567890, 0) | 	now := time.Unix(1234567890, 0) | ||||||
| 	ss.Clock = func() time.Time { return now } | 	ss.Clock.Set(now) | ||||||
| 
 | 
 | ||||||
| 	ttl := 743 * time.Second | 	ttl := time.Duration(743) * time.Second | ||||||
| 	ss.ExpiresIn(ttl) | 	ss.ExpiresIn(ttl) | ||||||
| 
 | 
 | ||||||
| 	g.Expect(*ss.ExpiresOn).To(Equal(ss.CreatedAt.Add(ttl))) | 	g.Expect(*ss.ExpiresOn).To(Equal(ss.CreatedAt.Add(ttl))) | ||||||
|  |  | ||||||
|  | @ -0,0 +1,157 @@ | ||||||
|  | package clock | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"errors" | ||||||
|  | 	"sync" | ||||||
|  | 	"time" | ||||||
|  | 
 | ||||||
|  | 	clockapi "github.com/benbjohnson/clock" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var ( | ||||||
|  | 	globalClock = clockapi.New() | ||||||
|  | 	mu          sync.Mutex | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | // Set the global clock to a clockapi.Mock with the given time.Time
 | ||||||
|  | func Set(t time.Time) { | ||||||
|  | 	mu.Lock() | ||||||
|  | 	defer mu.Unlock() | ||||||
|  | 	mock, ok := globalClock.(*clockapi.Mock) | ||||||
|  | 	if !ok { | ||||||
|  | 		mock = clockapi.NewMock() | ||||||
|  | 	} | ||||||
|  | 	mock.Set(t) | ||||||
|  | 	globalClock = mock | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Add moves the mocked global clock forward the given duration. It will error
 | ||||||
|  | // if the global clock is not mocked.
 | ||||||
|  | func Add(d time.Duration) error { | ||||||
|  | 	mu.Lock() | ||||||
|  | 	defer mu.Unlock() | ||||||
|  | 	mock, ok := globalClock.(*clockapi.Mock) | ||||||
|  | 	if !ok { | ||||||
|  | 		return errors.New("time not mocked") | ||||||
|  | 	} | ||||||
|  | 	mock.Add(d) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Reset sets the global clock to a pure time implementation. Returns any
 | ||||||
|  | // existing Mock if set in case lingering time operations are attached to it.
 | ||||||
|  | func Reset() *clockapi.Mock { | ||||||
|  | 	mu.Lock() | ||||||
|  | 	defer mu.Unlock() | ||||||
|  | 	existing := globalClock | ||||||
|  | 	globalClock = clockapi.New() | ||||||
|  | 
 | ||||||
|  | 	mock, ok := existing.(*clockapi.Mock) | ||||||
|  | 	if !ok { | ||||||
|  | 		return nil | ||||||
|  | 	} | ||||||
|  | 	return mock | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Clock is a non-package level wrapper around time that supports stubbing.
 | ||||||
|  | // It will use its localized stubs (allowing for parallelized unit tests
 | ||||||
|  | // where package level stubbing would cause issues). It falls back to any
 | ||||||
|  | // package level time stubs for non-parallel, cross-package integration
 | ||||||
|  | // testing scenarios.
 | ||||||
|  | //
 | ||||||
|  | // If nothing is stubbed, it defaults to default time behavior in the time
 | ||||||
|  | // package.
 | ||||||
|  | type Clock struct { | ||||||
|  | 	mock *clockapi.Mock | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Set sets the Clock to a clock.Mock at the given time.Time
 | ||||||
|  | func (c *Clock) Set(t time.Time) { | ||||||
|  | 	if c.mock == nil { | ||||||
|  | 		c.mock = clockapi.NewMock() | ||||||
|  | 	} | ||||||
|  | 	c.mock.Set(t) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Add moves clock forward time.Duration if it is mocked. It will error
 | ||||||
|  | // if the clock is not mocked.
 | ||||||
|  | func (c *Clock) Add(d time.Duration) error { | ||||||
|  | 	if c.mock == nil { | ||||||
|  | 		return errors.New("clock not mocked") | ||||||
|  | 	} | ||||||
|  | 	c.mock.Add(d) | ||||||
|  | 	return nil | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Reset removes local clock.Mock.  Returns any existing Mock if set in case
 | ||||||
|  | // lingering time operations are attached to it.
 | ||||||
|  | func (c *Clock) Reset() *clockapi.Mock { | ||||||
|  | 	existing := c.mock | ||||||
|  | 	c.mock = nil | ||||||
|  | 	return existing | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Clock) After(d time.Duration) <-chan time.Time { | ||||||
|  | 	m := c.mock | ||||||
|  | 	if m == nil { | ||||||
|  | 		return globalClock.After(d) | ||||||
|  | 	} | ||||||
|  | 	return m.After(d) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Clock) AfterFunc(d time.Duration, f func()) *clockapi.Timer { | ||||||
|  | 	m := c.mock | ||||||
|  | 	if m == nil { | ||||||
|  | 		return globalClock.AfterFunc(d, f) | ||||||
|  | 	} | ||||||
|  | 	return m.AfterFunc(d, f) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Clock) Now() time.Time { | ||||||
|  | 	m := c.mock | ||||||
|  | 	if m == nil { | ||||||
|  | 		return globalClock.Now() | ||||||
|  | 	} | ||||||
|  | 	return m.Now() | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Clock) Since(t time.Time) time.Duration { | ||||||
|  | 	m := c.mock | ||||||
|  | 	if m == nil { | ||||||
|  | 		return globalClock.Since(t) | ||||||
|  | 	} | ||||||
|  | 	return m.Since(t) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Clock) Sleep(d time.Duration) { | ||||||
|  | 	m := c.mock | ||||||
|  | 	if m == nil { | ||||||
|  | 		globalClock.Sleep(d) | ||||||
|  | 		return | ||||||
|  | 	} | ||||||
|  | 	m.Sleep(d) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Clock) Tick(d time.Duration) <-chan time.Time { | ||||||
|  | 	m := c.mock | ||||||
|  | 	if m == nil { | ||||||
|  | 		return globalClock.Tick(d) | ||||||
|  | 	} | ||||||
|  | 	return m.Tick(d) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Clock) Ticker(d time.Duration) *clockapi.Ticker { | ||||||
|  | 	m := c.mock | ||||||
|  | 	if m == nil { | ||||||
|  | 		return globalClock.Ticker(d) | ||||||
|  | 	} | ||||||
|  | 	return m.Ticker(d) | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (c *Clock) Timer(d time.Duration) *clockapi.Timer { | ||||||
|  | 	m := c.mock | ||||||
|  | 	if m == nil { | ||||||
|  | 		return globalClock.Timer(d) | ||||||
|  | 	} | ||||||
|  | 	return m.Timer(d) | ||||||
|  | } | ||||||
|  | @ -0,0 +1,17 @@ | ||||||
|  | package clock_test | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"testing" | ||||||
|  | 
 | ||||||
|  | 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/logger" | ||||||
|  | 	. "github.com/onsi/ginkgo/v2" | ||||||
|  | 	. "github.com/onsi/gomega" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | func TestClockSuite(t *testing.T) { | ||||||
|  | 	logger.SetOutput(GinkgoWriter) | ||||||
|  | 	logger.SetErrOutput(GinkgoWriter) | ||||||
|  | 
 | ||||||
|  | 	RegisterFailHandler(Fail) | ||||||
|  | 	RunSpecs(t, "Clock") | ||||||
|  | } | ||||||
|  | @ -0,0 +1,380 @@ | ||||||
|  | package clock_test | ||||||
|  | 
 | ||||||
|  | import ( | ||||||
|  | 	"sync" | ||||||
|  | 	"sync/atomic" | ||||||
|  | 	"time" | ||||||
|  | 
 | ||||||
|  | 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/clock" | ||||||
|  | 	. "github.com/onsi/ginkgo/v2" | ||||||
|  | 	. "github.com/onsi/gomega" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	testGlobalEpoch = 1000000000 | ||||||
|  | 	testLocalEpoch  = 1234567890 | ||||||
|  | ) | ||||||
|  | 
 | ||||||
|  | var _ = Describe("Clock suite", func() { | ||||||
|  | 	var testClock = clock.Clock{} | ||||||
|  | 
 | ||||||
|  | 	AfterEach(func() { | ||||||
|  | 		clock.Reset() | ||||||
|  | 		testClock.Reset() | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	Context("Global time not overridden", func() { | ||||||
|  | 		It("errors when trying to Add", func() { | ||||||
|  | 			err := clock.Add(123 * time.Hour) | ||||||
|  | 			Expect(err).To(HaveOccurred()) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		Context("Clock not mocked via Set", func() { | ||||||
|  | 			const ( | ||||||
|  | 				outsideTolerance = int32(0) | ||||||
|  | 				withinTolerance  = int32(1) | ||||||
|  | 			) | ||||||
|  | 
 | ||||||
|  | 			It("uses time.After for After", func() { | ||||||
|  | 				var tolerance int32 | ||||||
|  | 				go func() { | ||||||
|  | 					time.Sleep(10 * time.Millisecond) | ||||||
|  | 					atomic.StoreInt32(&tolerance, withinTolerance) | ||||||
|  | 				}() | ||||||
|  | 				go func() { | ||||||
|  | 					time.Sleep(30 * time.Millisecond) | ||||||
|  | 					atomic.StoreInt32(&tolerance, outsideTolerance) | ||||||
|  | 				}() | ||||||
|  | 
 | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(outsideTolerance)) | ||||||
|  | 
 | ||||||
|  | 				<-testClock.After(20 * time.Millisecond) | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(withinTolerance)) | ||||||
|  | 
 | ||||||
|  | 				<-testClock.After(20 * time.Millisecond) | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(outsideTolerance)) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("uses time.AfterFunc for AfterFunc", func() { | ||||||
|  | 				var tolerance int32 | ||||||
|  | 				go func() { | ||||||
|  | 					time.Sleep(10 * time.Millisecond) | ||||||
|  | 					atomic.StoreInt32(&tolerance, withinTolerance) | ||||||
|  | 				}() | ||||||
|  | 				go func() { | ||||||
|  | 					time.Sleep(30 * time.Millisecond) | ||||||
|  | 					atomic.StoreInt32(&tolerance, outsideTolerance) | ||||||
|  | 				}() | ||||||
|  | 
 | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(outsideTolerance)) | ||||||
|  | 
 | ||||||
|  | 				var wg sync.WaitGroup | ||||||
|  | 				wg.Add(1) | ||||||
|  | 				testClock.AfterFunc(20*time.Millisecond, func() { | ||||||
|  | 					wg.Done() | ||||||
|  | 				}) | ||||||
|  | 				wg.Wait() | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(withinTolerance)) | ||||||
|  | 
 | ||||||
|  | 				wg.Add(1) | ||||||
|  | 				testClock.AfterFunc(20*time.Millisecond, func() { | ||||||
|  | 					wg.Done() | ||||||
|  | 				}) | ||||||
|  | 				wg.Wait() | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(outsideTolerance)) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("uses time.Now for Now", func() { | ||||||
|  | 				a := time.Now() | ||||||
|  | 				b := testClock.Now() | ||||||
|  | 				Expect(b.Sub(a).Round(10 * time.Millisecond)).To(Equal(0 * time.Millisecond)) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("uses time.Since for Since", func() { | ||||||
|  | 				past := time.Now().Add(-60 * time.Second) | ||||||
|  | 				Expect(time.Since(past).Round(10 * time.Millisecond)). | ||||||
|  | 					To(Equal(60 * time.Second)) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("uses time.Sleep for Sleep", func() { | ||||||
|  | 				var tolerance int32 | ||||||
|  | 				go func() { | ||||||
|  | 					time.Sleep(10 * time.Millisecond) | ||||||
|  | 					atomic.StoreInt32(&tolerance, withinTolerance) | ||||||
|  | 				}() | ||||||
|  | 				go func() { | ||||||
|  | 					time.Sleep(30 * time.Millisecond) | ||||||
|  | 					atomic.StoreInt32(&tolerance, outsideTolerance) | ||||||
|  | 				}() | ||||||
|  | 
 | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(outsideTolerance)) | ||||||
|  | 
 | ||||||
|  | 				testClock.Sleep(20 * time.Millisecond) | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(withinTolerance)) | ||||||
|  | 
 | ||||||
|  | 				testClock.Sleep(20 * time.Millisecond) | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(outsideTolerance)) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("uses time.Tick for Tick", func() { | ||||||
|  | 				var tolerance int32 | ||||||
|  | 				go func() { | ||||||
|  | 					time.Sleep(10 * time.Millisecond) | ||||||
|  | 					atomic.StoreInt32(&tolerance, withinTolerance) | ||||||
|  | 				}() | ||||||
|  | 				go func() { | ||||||
|  | 					time.Sleep(50 * time.Millisecond) | ||||||
|  | 					atomic.StoreInt32(&tolerance, outsideTolerance) | ||||||
|  | 				}() | ||||||
|  | 
 | ||||||
|  | 				ch := testClock.Tick(20 * time.Millisecond) | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(outsideTolerance)) | ||||||
|  | 				<-ch | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(withinTolerance)) | ||||||
|  | 				<-ch | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(withinTolerance)) | ||||||
|  | 				<-ch | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(outsideTolerance)) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("uses time.Ticker for Ticker", func() { | ||||||
|  | 				var tolerance int32 | ||||||
|  | 				go func() { | ||||||
|  | 					time.Sleep(10 * time.Millisecond) | ||||||
|  | 					atomic.StoreInt32(&tolerance, withinTolerance) | ||||||
|  | 				}() | ||||||
|  | 				go func() { | ||||||
|  | 					time.Sleep(50 * time.Millisecond) | ||||||
|  | 					atomic.StoreInt32(&tolerance, outsideTolerance) | ||||||
|  | 				}() | ||||||
|  | 
 | ||||||
|  | 				ticker := testClock.Ticker(20 * time.Millisecond) | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(outsideTolerance)) | ||||||
|  | 				<-ticker.C | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(withinTolerance)) | ||||||
|  | 				<-ticker.C | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(withinTolerance)) | ||||||
|  | 				<-ticker.C | ||||||
|  | 				Expect(atomic.LoadInt32(&tolerance)).To(Equal(outsideTolerance)) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("errors if Add is used", func() { | ||||||
|  | 				err := testClock.Add(100 * time.Second) | ||||||
|  | 				Expect(err).To(HaveOccurred()) | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		Context("Clock mocked via Set", func() { | ||||||
|  | 			var now = time.Unix(testLocalEpoch, 0) | ||||||
|  | 
 | ||||||
|  | 			BeforeEach(func() { | ||||||
|  | 				testClock.Set(now) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("mocks After", func() { | ||||||
|  | 				var after int32 | ||||||
|  | 				ready := make(chan struct{}) | ||||||
|  | 				ch := testClock.After(10 * time.Second) | ||||||
|  | 				go func(ch <-chan time.Time) { | ||||||
|  | 					close(ready) | ||||||
|  | 					<-ch | ||||||
|  | 					atomic.StoreInt32(&after, 1) | ||||||
|  | 				}(ch) | ||||||
|  | 				<-ready | ||||||
|  | 
 | ||||||
|  | 				err := testClock.Add(9 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&after)).To(Equal(int32(0))) | ||||||
|  | 
 | ||||||
|  | 				err = testClock.Add(1 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&after)).To(Equal(int32(1))) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("mocks AfterFunc", func() { | ||||||
|  | 				var after int32 | ||||||
|  | 				testClock.AfterFunc(10*time.Second, func() { | ||||||
|  | 					atomic.StoreInt32(&after, 1) | ||||||
|  | 				}) | ||||||
|  | 
 | ||||||
|  | 				err := testClock.Add(9 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&after)).To(Equal(int32(0))) | ||||||
|  | 
 | ||||||
|  | 				err = testClock.Add(1 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&after)).To(Equal(int32(1))) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("mocks AfterFunc with a stopped timer", func() { | ||||||
|  | 				var after int32 | ||||||
|  | 				timer := testClock.AfterFunc(10*time.Second, func() { | ||||||
|  | 					atomic.StoreInt32(&after, 1) | ||||||
|  | 				}) | ||||||
|  | 				timer.Stop() | ||||||
|  | 
 | ||||||
|  | 				err := testClock.Add(11 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&after)).To(Equal(int32(0))) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("mocks Now", func() { | ||||||
|  | 				Expect(testClock.Now()).To(Equal(now)) | ||||||
|  | 				err := testClock.Add(123 * time.Hour) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(testClock.Now()).To(Equal(now.Add(123 * time.Hour))) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("mocks Since", func() { | ||||||
|  | 				Expect(testClock.Since(time.Unix(testLocalEpoch-100, 0))). | ||||||
|  | 					To(Equal(100 * time.Second)) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("mocks Sleep", func() { | ||||||
|  | 				var after int32 | ||||||
|  | 				ready := make(chan struct{}) | ||||||
|  | 				go func() { | ||||||
|  | 					close(ready) | ||||||
|  | 					testClock.Sleep(10 * time.Second) | ||||||
|  | 					atomic.StoreInt32(&after, 1) | ||||||
|  | 				}() | ||||||
|  | 				<-ready | ||||||
|  | 
 | ||||||
|  | 				err := testClock.Add(9 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&after)).To(Equal(int32(0))) | ||||||
|  | 
 | ||||||
|  | 				err = testClock.Add(1 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&after)).To(Equal(int32(1))) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("mocks Tick", func() { | ||||||
|  | 				var ticks int32 | ||||||
|  | 				ready := make(chan struct{}) | ||||||
|  | 				go func() { | ||||||
|  | 					close(ready) | ||||||
|  | 					tick := testClock.Tick(10 * time.Second) | ||||||
|  | 					for ticks < 5 { | ||||||
|  | 						<-tick | ||||||
|  | 						atomic.AddInt32(&ticks, 1) | ||||||
|  | 					} | ||||||
|  | 				}() | ||||||
|  | 				<-ready | ||||||
|  | 
 | ||||||
|  | 				Expect(atomic.LoadInt32(&ticks)).To(Equal(int32(0))) | ||||||
|  | 
 | ||||||
|  | 				err := testClock.Add(9 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&ticks)).To(Equal(int32(0))) | ||||||
|  | 
 | ||||||
|  | 				err = testClock.Add(1 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&ticks)).To(Equal(int32(1))) | ||||||
|  | 
 | ||||||
|  | 				err = testClock.Add(30 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&ticks)).To(Equal(int32(4))) | ||||||
|  | 
 | ||||||
|  | 				err = testClock.Add(10 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&ticks)).To(Equal(int32(5))) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("mocks Ticker", func() { | ||||||
|  | 				var ticks int32 | ||||||
|  | 				ready := make(chan struct{}) | ||||||
|  | 				go func() { | ||||||
|  | 					ticker := testClock.Ticker(10 * time.Second) | ||||||
|  | 					close(ready) | ||||||
|  | 					for ticks < 5 { | ||||||
|  | 						<-ticker.C | ||||||
|  | 						atomic.AddInt32(&ticks, 1) | ||||||
|  | 					} | ||||||
|  | 				}() | ||||||
|  | 				<-ready | ||||||
|  | 
 | ||||||
|  | 				Expect(atomic.LoadInt32(&ticks)).To(Equal(int32(0))) | ||||||
|  | 
 | ||||||
|  | 				err := testClock.Add(9 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&ticks)).To(Equal(int32(0))) | ||||||
|  | 
 | ||||||
|  | 				err = testClock.Add(1 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&ticks)).To(Equal(int32(1))) | ||||||
|  | 
 | ||||||
|  | 				err = testClock.Add(30 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&ticks)).To(Equal(int32(4))) | ||||||
|  | 
 | ||||||
|  | 				err = testClock.Add(10 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&ticks)).To(Equal(int32(5))) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("mocks Timer", func() { | ||||||
|  | 				var after int32 | ||||||
|  | 				ready := make(chan struct{}) | ||||||
|  | 				go func() { | ||||||
|  | 					timer := testClock.Timer(10 * time.Second) | ||||||
|  | 					close(ready) | ||||||
|  | 					<-timer.C | ||||||
|  | 					atomic.AddInt32(&after, 1) | ||||||
|  | 				}() | ||||||
|  | 				<-ready | ||||||
|  | 
 | ||||||
|  | 				err := testClock.Add(9 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&after)).To(Equal(int32(0))) | ||||||
|  | 
 | ||||||
|  | 				err = testClock.Add(1 * time.Second) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(atomic.LoadInt32(&after)).To(Equal(int32(1))) | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | 
 | ||||||
|  | 	Context("Global time overridden", func() { | ||||||
|  | 		var ( | ||||||
|  | 			globalNow = time.Unix(testGlobalEpoch, 0) | ||||||
|  | 			localNow  = time.Unix(testLocalEpoch, 0) | ||||||
|  | 		) | ||||||
|  | 
 | ||||||
|  | 		BeforeEach(func() { | ||||||
|  | 			clock.Set(globalNow) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		Context("Clock not mocked via Set", func() { | ||||||
|  | 			It("uses globally mocked Now", func() { | ||||||
|  | 				Expect(testClock.Now()).To(Equal(globalNow)) | ||||||
|  | 				err := clock.Add(123 * time.Hour) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 				Expect(testClock.Now()).To(Equal(globalNow.Add(123 * time.Hour))) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("errors when Add is called on the local Clock", func() { | ||||||
|  | 				err := testClock.Add(100 * time.Hour) | ||||||
|  | 				Expect(err).To(HaveOccurred()) | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		Context("Clock is mocked via Set", func() { | ||||||
|  | 			BeforeEach(func() { | ||||||
|  | 				testClock.Set(localNow) | ||||||
|  | 			}) | ||||||
|  | 
 | ||||||
|  | 			It("uses the local mock and ignores the global", func() { | ||||||
|  | 				Expect(testClock.Now()).To(Equal(localNow)) | ||||||
|  | 
 | ||||||
|  | 				err := clock.Add(456 * time.Hour) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 
 | ||||||
|  | 				err = testClock.Add(123 * time.Hour) | ||||||
|  | 				Expect(err).ToNot(HaveOccurred()) | ||||||
|  | 
 | ||||||
|  | 				Expect(testClock.Now()).To(Equal(localNow.Add(123 * time.Hour))) | ||||||
|  | 			}) | ||||||
|  | 		}) | ||||||
|  | 	}) | ||||||
|  | }) | ||||||
|  | @ -10,6 +10,7 @@ import ( | ||||||
| 
 | 
 | ||||||
| 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" | 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/options" | ||||||
| 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" | 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" | ||||||
|  | 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/clock" | ||||||
| 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption" | 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/encryption" | ||||||
| 	"github.com/vmihailenco/msgpack/v5" | 	"github.com/vmihailenco/msgpack/v5" | ||||||
| ) | ) | ||||||
|  | @ -46,7 +47,7 @@ type csrf struct { | ||||||
| 	CodeVerifier string `msgpack:"cv,omitempty"` | 	CodeVerifier string `msgpack:"cv,omitempty"` | ||||||
| 
 | 
 | ||||||
| 	cookieOpts *options.Cookie | 	cookieOpts *options.Cookie | ||||||
| 	clock      func() time.Time | 	time       clock.Clock | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // csrtStateTrim will indicate the length of the state trimmed for the name of the csrf cookie
 | // csrtStateTrim will indicate the length of the state trimmed for the name of the csrf cookie
 | ||||||
|  | @ -69,7 +70,6 @@ func NewCSRF(opts *options.Cookie, codeVerifier string) (CSRF, error) { | ||||||
| 		CodeVerifier: codeVerifier, | 		CodeVerifier: codeVerifier, | ||||||
| 
 | 
 | ||||||
| 		cookieOpts: opts, | 		cookieOpts: opts, | ||||||
| 		clock:      time.Now, |  | ||||||
| 	}, nil | 	}, nil | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | @ -187,7 +187,7 @@ func ClearExtraCsrfCookies(opts *options.Cookie, rw http.ResponseWriter, req *ht | ||||||
| 
 | 
 | ||||||
| 	// delete the X oldest cookies
 | 	// delete the X oldest cookies
 | ||||||
| 	slices.SortStableFunc(decodedCookies, func(a, b *csrf) int { | 	slices.SortStableFunc(decodedCookies, func(a, b *csrf) int { | ||||||
| 		return a.clock().Compare(b.clock()) | 		return a.time.Now().Compare(b.time.Now()) | ||||||
| 	}) | 	}) | ||||||
| 
 | 
 | ||||||
| 	for i := 0; i < len(decodedCookies)-opts.CSRFPerRequestLimit; i++ { | 	for i := 0; i < len(decodedCookies)-opts.CSRFPerRequestLimit; i++ { | ||||||
|  | @ -223,7 +223,7 @@ func (c *csrf) encodeCookie() (string, error) { | ||||||
| 	if err != nil { | 	if err != nil { | ||||||
| 		return "", fmt.Errorf("error getting cookie secret: %v", err) | 		return "", fmt.Errorf("error getting cookie secret: %v", err) | ||||||
| 	} | 	} | ||||||
| 	return encryption.SignedValue(secret, c.cookieName(), encrypted, c.clock()) | 	return encryption.SignedValue(secret, c.cookieName(), encrypted, c.time.Now()) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // decodeCSRFCookie validates the signature then decrypts and decodes a CSRF
 | // decodeCSRFCookie validates the signature then decrypts and decodes a CSRF
 | ||||||
|  | @ -249,10 +249,10 @@ func decodeCSRFCookie(cookie *http.Cookie, opts *options.Cookie) (*csrf, error) | ||||||
| 
 | 
 | ||||||
| // unmarshalCSRF unmarshals decrypted data into a CSRF struct
 | // unmarshalCSRF unmarshals decrypted data into a CSRF struct
 | ||||||
| func unmarshalCSRF(decrypted []byte, opts *options.Cookie, csrfTime time.Time) (*csrf, error) { | func unmarshalCSRF(decrypted []byte, opts *options.Cookie, csrfTime time.Time) (*csrf, error) { | ||||||
| 	csrf := &csrf{ | 	clock := clock.Clock{} | ||||||
| 		cookieOpts: opts, | 	clock.Set(csrfTime) | ||||||
| 		clock:      func() time.Time { return csrfTime }, | 
 | ||||||
| 	} | 	csrf := &csrf{cookieOpts: opts, time: clock} | ||||||
| 	if err := msgpack.Unmarshal(decrypted, csrf); err != nil { | 	if err := msgpack.Unmarshal(decrypted, csrf); err != nil { | ||||||
| 		return nil, fmt.Errorf("error unmarshalling data to CSRF: %v", err) | 		return nil, fmt.Errorf("error unmarshalling data to CSRF: %v", err) | ||||||
| 	} | 	} | ||||||
|  |  | ||||||
|  | @ -128,7 +128,7 @@ var _ = Describe("CSRF Cookie with non-fixed name Tests", func() { | ||||||
| 		testNow := time.Unix(nowEpoch, 0) | 		testNow := time.Unix(nowEpoch, 0) | ||||||
| 
 | 
 | ||||||
| 		BeforeEach(func() { | 		BeforeEach(func() { | ||||||
| 			privateCSRF.clock = func() time.Time { return testNow } | 			privateCSRF.time.Set(testNow) | ||||||
| 
 | 
 | ||||||
| 			req = &http.Request{ | 			req = &http.Request{ | ||||||
| 				Method: http.MethodGet, | 				Method: http.MethodGet, | ||||||
|  | @ -144,7 +144,7 @@ var _ = Describe("CSRF Cookie with non-fixed name Tests", func() { | ||||||
| 		}) | 		}) | ||||||
| 
 | 
 | ||||||
| 		AfterEach(func() { | 		AfterEach(func() { | ||||||
| 			privateCSRF.clock = time.Now | 			privateCSRF.time.Reset() | ||||||
| 		}) | 		}) | ||||||
| 
 | 
 | ||||||
| 		Context("SetCookie", func() { | 		Context("SetCookie", func() { | ||||||
|  | @ -200,17 +200,17 @@ var _ = Describe("CSRF Cookie with non-fixed name Tests", func() { | ||||||
| 				publicCSRF1, err := NewCSRF(cookieOpts, "verifier") | 				publicCSRF1, err := NewCSRF(cookieOpts, "verifier") | ||||||
| 				Expect(err).ToNot(HaveOccurred()) | 				Expect(err).ToNot(HaveOccurred()) | ||||||
| 				privateCSRF1 := publicCSRF1.(*csrf) | 				privateCSRF1 := publicCSRF1.(*csrf) | ||||||
| 				privateCSRF1.clock = func() time.Time { return testNow } | 				privateCSRF1.time.Set(testNow) | ||||||
| 
 | 
 | ||||||
| 				publicCSRF2, err := NewCSRF(cookieOpts, "verifier") | 				publicCSRF2, err := NewCSRF(cookieOpts, "verifier") | ||||||
| 				Expect(err).ToNot(HaveOccurred()) | 				Expect(err).ToNot(HaveOccurred()) | ||||||
| 				privateCSRF2 := publicCSRF2.(*csrf) | 				privateCSRF2 := publicCSRF2.(*csrf) | ||||||
| 				privateCSRF2.clock = func() time.Time { return testNow.Add(time.Minute) } | 				privateCSRF2.time.Set(testNow.Add(time.Minute)) | ||||||
| 
 | 
 | ||||||
| 				publicCSRF3, err := NewCSRF(cookieOpts, "verifier") | 				publicCSRF3, err := NewCSRF(cookieOpts, "verifier") | ||||||
| 				Expect(err).ToNot(HaveOccurred()) | 				Expect(err).ToNot(HaveOccurred()) | ||||||
| 				privateCSRF3 := publicCSRF3.(*csrf) | 				privateCSRF3 := publicCSRF3.(*csrf) | ||||||
| 				privateCSRF3.clock = func() time.Time { return testNow.Add(time.Minute * 2) } | 				privateCSRF3.time.Set(testNow.Add(time.Minute * 2)) | ||||||
| 
 | 
 | ||||||
| 				cookies := []string{} | 				cookies := []string{} | ||||||
| 				for _, csrf := range []*csrf{privateCSRF1, privateCSRF2, privateCSRF3} { | 				for _, csrf := range []*csrf{privateCSRF1, privateCSRF2, privateCSRF3} { | ||||||
|  |  | ||||||
|  | @ -130,7 +130,7 @@ var _ = Describe("CSRF Cookie Tests", func() { | ||||||
| 		testNow := time.Unix(nowEpoch, 0) | 		testNow := time.Unix(nowEpoch, 0) | ||||||
| 
 | 
 | ||||||
| 		BeforeEach(func() { | 		BeforeEach(func() { | ||||||
| 			privateCSRF.clock = func() time.Time { return testNow } | 			privateCSRF.time.Set(testNow) | ||||||
| 
 | 
 | ||||||
| 			req = &http.Request{ | 			req = &http.Request{ | ||||||
| 				Method: http.MethodGet, | 				Method: http.MethodGet, | ||||||
|  | @ -146,7 +146,7 @@ var _ = Describe("CSRF Cookie Tests", func() { | ||||||
| 		}) | 		}) | ||||||
| 
 | 
 | ||||||
| 		AfterEach(func() { | 		AfterEach(func() { | ||||||
| 			privateCSRF.clock = time.Now | 			privateCSRF.time.Reset() | ||||||
| 		}) | 		}) | ||||||
| 
 | 
 | ||||||
| 		Context("SetCookie", func() { | 		Context("SetCookie", func() { | ||||||
|  | @ -173,7 +173,7 @@ var _ = Describe("CSRF Cookie Tests", func() { | ||||||
| 		Context("LoadCSRFCookie", func() { | 		Context("LoadCSRFCookie", func() { | ||||||
| 			BeforeEach(func() { | 			BeforeEach(func() { | ||||||
| 				// we need to reset the time to ensure the cookie is valid
 | 				// we need to reset the time to ensure the cookie is valid
 | ||||||
| 				privateCSRF.clock = time.Now | 				privateCSRF.time.Reset() | ||||||
| 			}) | 			}) | ||||||
| 
 | 
 | ||||||
| 			It("should return error when no cookie is set", func() { | 			It("should return error when no cookie is set", func() { | ||||||
|  |  | ||||||
|  | @ -11,6 +11,7 @@ import ( | ||||||
| 
 | 
 | ||||||
| 	middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware" | 	middlewareapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/middleware" | ||||||
| 	sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" | 	sessionsapi "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/apis/sessions" | ||||||
|  | 	"github.com/oauth2-proxy/oauth2-proxy/v7/pkg/clock" | ||||||
| 	"github.com/oauth2-proxy/oauth2-proxy/v7/providers" | 	"github.com/oauth2-proxy/oauth2-proxy/v7/providers" | ||||||
| 	. "github.com/onsi/ginkgo/v2" | 	. "github.com/onsi/ginkgo/v2" | ||||||
| 	. "github.com/onsi/gomega" | 	. "github.com/onsi/gomega" | ||||||
|  | @ -94,7 +95,6 @@ var _ = Describe("Stored Session Suite", func() { | ||||||
| 		now := time.Now() | 		now := time.Now() | ||||||
| 		createdPast := now.Add(-5 * time.Minute) | 		createdPast := now.Add(-5 * time.Minute) | ||||||
| 		createdFuture := now.Add(5 * time.Minute) | 		createdFuture := now.Add(5 * time.Minute) | ||||||
| 		clock := func() time.Time { return now } |  | ||||||
| 
 | 
 | ||||||
| 		var defaultRefreshFunc = func(_ context.Context, ss *sessionsapi.SessionState) (bool, error) { | 		var defaultRefreshFunc = func(_ context.Context, ss *sessionsapi.SessionState) (bool, error) { | ||||||
| 			switch ss.RefreshToken { | 			switch ss.RefreshToken { | ||||||
|  | @ -120,7 +120,6 @@ var _ = Describe("Stored Session Suite", func() { | ||||||
| 						RefreshToken: noRefresh, | 						RefreshToken: noRefresh, | ||||||
| 						CreatedAt:    &createdPast, | 						CreatedAt:    &createdPast, | ||||||
| 						ExpiresOn:    &createdFuture, | 						ExpiresOn:    &createdFuture, | ||||||
| 						Clock:        clock, |  | ||||||
| 					}, nil | 					}, nil | ||||||
| 				case "_oauth2_proxy=InvalidNoRefreshSession": | 				case "_oauth2_proxy=InvalidNoRefreshSession": | ||||||
| 					return &sessionsapi.SessionState{ | 					return &sessionsapi.SessionState{ | ||||||
|  | @ -128,28 +127,24 @@ var _ = Describe("Stored Session Suite", func() { | ||||||
| 						RefreshToken: noRefresh, | 						RefreshToken: noRefresh, | ||||||
| 						CreatedAt:    &createdPast, | 						CreatedAt:    &createdPast, | ||||||
| 						ExpiresOn:    &createdFuture, | 						ExpiresOn:    &createdFuture, | ||||||
| 						Clock:        clock, |  | ||||||
| 					}, nil | 					}, nil | ||||||
| 				case "_oauth2_proxy=ExpiredNoRefreshSession": | 				case "_oauth2_proxy=ExpiredNoRefreshSession": | ||||||
| 					return &sessionsapi.SessionState{ | 					return &sessionsapi.SessionState{ | ||||||
| 						RefreshToken: noRefresh, | 						RefreshToken: noRefresh, | ||||||
| 						CreatedAt:    &createdPast, | 						CreatedAt:    &createdPast, | ||||||
| 						ExpiresOn:    &createdPast, | 						ExpiresOn:    &createdPast, | ||||||
| 						Clock:        clock, |  | ||||||
| 					}, nil | 					}, nil | ||||||
| 				case "_oauth2_proxy=RefreshSession": | 				case "_oauth2_proxy=RefreshSession": | ||||||
| 					return &sessionsapi.SessionState{ | 					return &sessionsapi.SessionState{ | ||||||
| 						RefreshToken: refresh, | 						RefreshToken: refresh, | ||||||
| 						CreatedAt:    &createdPast, | 						CreatedAt:    &createdPast, | ||||||
| 						ExpiresOn:    &createdFuture, | 						ExpiresOn:    &createdFuture, | ||||||
| 						Clock:        clock, |  | ||||||
| 					}, nil | 					}, nil | ||||||
| 				case "_oauth2_proxy=RefreshError": | 				case "_oauth2_proxy=RefreshError": | ||||||
| 					return &sessionsapi.SessionState{ | 					return &sessionsapi.SessionState{ | ||||||
| 						RefreshToken: "RefreshError", | 						RefreshToken: "RefreshError", | ||||||
| 						CreatedAt:    &createdPast, | 						CreatedAt:    &createdPast, | ||||||
| 						ExpiresOn:    &createdFuture, | 						ExpiresOn:    &createdFuture, | ||||||
| 						Clock:        clock, |  | ||||||
| 					}, nil | 					}, nil | ||||||
| 				case "_oauth2_proxy=NonExistent": | 				case "_oauth2_proxy=NonExistent": | ||||||
| 					return nil, fmt.Errorf("invalid cookie") | 					return nil, fmt.Errorf("invalid cookie") | ||||||
|  | @ -159,6 +154,14 @@ var _ = Describe("Stored Session Suite", func() { | ||||||
| 			}, | 			}, | ||||||
| 		} | 		} | ||||||
| 
 | 
 | ||||||
|  | 		BeforeEach(func() { | ||||||
|  | 			clock.Set(now) | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
|  | 		AfterEach(func() { | ||||||
|  | 			clock.Reset() | ||||||
|  | 		}) | ||||||
|  | 
 | ||||||
| 		type storedSessionLoaderTableInput struct { | 		type storedSessionLoaderTableInput struct { | ||||||
| 			requestHeaders  http.Header | 			requestHeaders  http.Header | ||||||
| 			existingSession *sessionsapi.SessionState | 			existingSession *sessionsapi.SessionState | ||||||
|  | @ -197,15 +200,7 @@ var _ = Describe("Stored Session Suite", func() { | ||||||
| 				})) | 				})) | ||||||
| 				handler.ServeHTTP(rw, req) | 				handler.ServeHTTP(rw, req) | ||||||
| 
 | 
 | ||||||
| 				// Compare, ignoring testing Clock.
 | 				Expect(gotSession).To(Equal(in.expectedSession)) | ||||||
| 				if in.expectedSession == nil { |  | ||||||
| 					Expect(gotSession).To(BeNil()) |  | ||||||
| 					return |  | ||||||
| 				} |  | ||||||
| 				Expect(gotSession).ToNot(BeNil()) |  | ||||||
| 				got := *gotSession |  | ||||||
| 				got.Clock = nil |  | ||||||
| 				Expect(&got).To(Equal(in.expectedSession)) |  | ||||||
| 			}, | 			}, | ||||||
| 			Entry("with no cookie", storedSessionLoaderTableInput{ | 			Entry("with no cookie", storedSessionLoaderTableInput{ | ||||||
| 				requestHeaders:  http.Header{}, | 				requestHeaders:  http.Header{}, | ||||||
|  |  | ||||||
|  | @ -233,17 +233,12 @@ func (t *ticket) clearCookie(rw http.ResponseWriter, req *http.Request) { | ||||||
| // makeCookie makes a cookie, signing the value if present
 | // makeCookie makes a cookie, signing the value if present
 | ||||||
| func (t *ticket) makeCookie(req *http.Request, value string, expires time.Duration, now time.Time) (*http.Cookie, error) { | func (t *ticket) makeCookie(req *http.Request, value string, expires time.Duration, now time.Time) (*http.Cookie, error) { | ||||||
| 	if value != "" { | 	if value != "" { | ||||||
| 		secret, err := t.options.GetSecret() | 		var err error | ||||||
|  | 		value, err = encryption.SignedValue(t.options.Secret, t.options.Name, []byte(value), now) | ||||||
| 		if err != nil { | 		if err != nil { | ||||||
| 			return nil, fmt.Errorf("retrieving secret failed: %w", err) | 			return nil, err | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		value, err = encryption.SignedValue(secret, t.options.Name, []byte(value), now) |  | ||||||
| 		if err != nil { |  | ||||||
| 			return nil, fmt.Errorf("signing cookie value failed: %w", err) |  | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 
 |  | ||||||
| 	return cookies.MakeCookieFromOptions( | 	return cookies.MakeCookieFromOptions( | ||||||
| 		req, | 		req, | ||||||
| 		t.options.Name, | 		t.options.Name, | ||||||
|  |  | ||||||
|  | @ -4,7 +4,6 @@ import ( | ||||||
| 	"crypto/rand" | 	"crypto/rand" | ||||||
| 	"net/http" | 	"net/http" | ||||||
| 	"net/http/httptest" | 	"net/http/httptest" | ||||||
| 	"os" |  | ||||||
| 	"strconv" | 	"strconv" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"time" | 	"time" | ||||||
|  | @ -134,42 +133,6 @@ func RunSessionStoreTests(newSS NewSessionStoreFunc, persistentFastForward Persi | ||||||
| 				PersistentSessionStoreInterfaceTests(&input) | 				PersistentSessionStoreInterfaceTests(&input) | ||||||
| 			} | 			} | ||||||
| 		}) | 		}) | ||||||
| 
 |  | ||||||
| 		Context("with cookie secret file", func() { |  | ||||||
| 			var tmpfile *os.File |  | ||||||
| 			var err error |  | ||||||
| 			BeforeEach(func() { |  | ||||||
| 				tmpfile, err = os.CreateTemp("", "cookie-secret-test") |  | ||||||
| 				secretBytes := make([]byte, 32) |  | ||||||
| 				tmpfile.Write(secretBytes) |  | ||||||
| 				tmpfile.Close() |  | ||||||
| 
 |  | ||||||
| 				input.cookieOpts = &options.Cookie{ |  | ||||||
| 					Name:       "_oauth2_proxy_file", |  | ||||||
| 					Path:       "/", |  | ||||||
| 					Expire:     time.Duration(168) * time.Hour, |  | ||||||
| 					Refresh:    time.Duration(1) * time.Hour, |  | ||||||
| 					Secure:     true, |  | ||||||
| 					HTTPOnly:   true, |  | ||||||
| 					SameSite:   "", |  | ||||||
| 					Secret:     "", |  | ||||||
| 					SecretFile: tmpfile.Name(), |  | ||||||
| 				} |  | ||||||
| 				ss, err = newSS(opts, input.cookieOpts) |  | ||||||
| 				Expect(err).ToNot(HaveOccurred()) |  | ||||||
| 			}) |  | ||||||
| 
 |  | ||||||
| 			AfterEach(func() { |  | ||||||
| 				if tmpfile != nil { |  | ||||||
| 					os.Remove(tmpfile.Name()) |  | ||||||
| 				} |  | ||||||
| 			}) |  | ||||||
| 
 |  | ||||||
| 			SessionStoreInterfaceTests(&input) |  | ||||||
| 			if persistentFastForward != nil { |  | ||||||
| 				PersistentSessionStoreInterfaceTests(&input) |  | ||||||
| 			} |  | ||||||
| 		}) |  | ||||||
| 	}) | 	}) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  |  | ||||||
|  | @ -17,9 +17,8 @@ func TestRefresh(t *testing.T) { | ||||||
| 	now := time.Unix(1234567890, 10) | 	now := time.Unix(1234567890, 10) | ||||||
| 	expires := time.Unix(1234567890, 0) | 	expires := time.Unix(1234567890, 0) | ||||||
| 
 | 
 | ||||||
| 	ss := &sessions.SessionState{ | 	ss := &sessions.SessionState{} | ||||||
| 		Clock: func() time.Time { return now }, | 	ss.Clock.Set(now) | ||||||
| 	} |  | ||||||
| 	ss.SetExpiresOn(expires) | 	ss.SetExpiresOn(expires) | ||||||
| 
 | 
 | ||||||
| 	refreshed, err := p.RefreshSession(context.Background(), ss) | 	refreshed, err := p.RefreshSession(context.Background(), ss) | ||||||
|  |  | ||||||
		Loading…
	
		Reference in New Issue