git.iko.soy/typeable/oauth2-proxy
on master help sign in
Merge branch 'master' into kamal/whitelist-redirects-with-ports
6d1b5fcKamal Nasser
.gitignore+1
@@ -16,6 +16,7 @@ release
16# Folders
16# Folders
17_obj
17_obj
18_test
18_test
19+.idea/
19
20
20# Architecture specific extensions/prefixes
21# Architecture specific extensions/prefixes
21*.[568vq]
22*.[568vq]
.travis.yml−1
@@ -1,6 +1,5 @@
1language: go
1language: go
2go:
2go:
3− - 1.12.x
4 - 1.13.x
3 - 1.13.x
5install:
4install:
6 # Fetch dependencies
5 # Fetch dependencies
CHANGELOG.md+8−2
@@ -1,14 +1,20 @@
1# Vx.x.x (Pre-release)
1# Vx.x.x (Pre-release)
2
2
3## Changes since v4.0.0
3## Changes since v4.0.0
4−
4+- [#292](https://github.com/pusher/oauth2_proxy/pull/292) Added bash >= 4.0 dependency to configure script (@jmfrank63)
5- [#227](https://github.com/pusher/oauth2_proxy/pull/227) Add Keycloak provider (@Ofinka)
5- [#227](https://github.com/pusher/oauth2_proxy/pull/227) Add Keycloak provider (@Ofinka)
6- [#259](https://github.com/pusher/oauth2_proxy/pull/259) Redirect to HTTPS (@jmickey)
6- [#259](https://github.com/pusher/oauth2_proxy/pull/259) Redirect to HTTPS (@jmickey)
7- [#273](https://github.com/pusher/oauth2_proxy/pull/273) Support Go 1.13 (@dio)
7- [#273](https://github.com/pusher/oauth2_proxy/pull/273) Support Go 1.13 (@dio)
8- [#275](https://github.com/pusher/oauth2_proxy/pull/275) docker: build from debian buster (@syscll)
8- [#275](https://github.com/pusher/oauth2_proxy/pull/275) docker: build from debian buster (@syscll)
9- [#258](https://github.com/pusher/oauth2_proxy/pull/258) Add IDToken for Azure provider
9- [#258](https://github.com/pusher/oauth2_proxy/pull/258) Add IDToken for Azure provider
10 - This PR adds the IDToken into the session for the Azure provider allowing requests to a backend to be identified as a specific user. As a consequence, if you are using a cookie to store the session the cookie will now exceed the 4kb size limit and be split into multiple cookies. This can cause problems when using nginx as a proxy, resulting in no cookie being passed at all. Either increase the proxy_buffer_size in nginx or implement the redis session storage (see https://pusher.github.io/oauth2_proxy/configuration#redis-storage)
10 - This PR adds the IDToken into the session for the Azure provider allowing requests to a backend to be identified as a specific user. As a consequence, if you are using a cookie to store the session the cookie will now exceed the 4kb size limit and be split into multiple cookies. This can cause problems when using nginx as a proxy, resulting in no cookie being passed at all. Either increase the proxy_buffer_size in nginx or implement the redis session storage (see https://pusher.github.io/oauth2_proxy/configuration#redis-storage)
11+- [#286](https://github.com/pusher/oauth2_proxy/pull/286) Requests.go updated with useful error messages (@biotom)
12+- [#274](https://github.com/pusher/oauth2_proxy/pull/274) Supports many github teams with api pagination support (@toshi-miura, @apratina)
13+- [#302](https://github.com/pusher/oauth2_proxy/pull/302) Rewrite dist script (@syscll)
14+- [#304](https://github.com/pusher/oauth2_proxy/pull/304) Add new Logo! :tada: (@JoelSpeed)
15+- [#300](https://github.com/pusher/oauth2_proxy/pull/300) Added userinfo endpoint (@kbabuadze)
16+- [#309](https://github.com/pusher/oauth2_proxy/pull/309) Added support for custom CA when connecting to Redis cache
11− - [#280](https://github.com/pusher/oauth2_proxy/pull/280) Add support for whitelisting specific ports or allowing wildcard ports in whitelisted redirect domains
17+- [#280](https://github.com/pusher/oauth2_proxy/pull/280) Add support for whitelisting specific ports or allowing wildcard ports in whitelisted redirect domains
12
18
13# v4.0.0
19# v4.0.0
14
20
Makefile+1−26
@@ -61,29 +61,4 @@ test: lint
61
61
62.PHONY: release
62.PHONY: release
63release: lint test
63release: lint test
64− mkdir release
64+ BINARY=${BINARY} VERSION=${VERSION} ./dist.sh
65− mkdir release/$(BINARY)-$(VERSION).darwin-amd64.$(GO_VERSION)
66− mkdir release/$(BINARY)-$(VERSION).linux-amd64.$(GO_VERSION)
67− mkdir release/$(BINARY)-$(VERSION).linux-arm64.$(GO_VERSION)
68− mkdir release/$(BINARY)-$(VERSION).linux-armv6.$(GO_VERSION)
69− mkdir release/$(BINARY)-$(VERSION).windows-amd64.$(GO_VERSION)
70− GO111MODULE=on GOOS=darwin GOARCH=amd64 go build -ldflags="-X main.VERSION=${VERSION}" \
71− -o release/$(BINARY)-$(VERSION).darwin-amd64.$(GO_VERSION)/$(BINARY) github.com/pusher/oauth2_proxy
72− GO111MODULE=on GOOS=linux GOARCH=amd64 go build -ldflags="-X main.VERSION=${VERSION}" \
73− -o release/$(BINARY)-$(VERSION).linux-amd64.$(GO_VERSION)/$(BINARY) github.com/pusher/oauth2_proxy
74− GO111MODULE=on GOOS=linux GOARCH=arm64 go build -ldflags="-X main.VERSION=${VERSION}" \
75− -o release/$(BINARY)-$(VERSION).linux-arm64.$(GO_VERSION)/$(BINARY) github.com/pusher/oauth2_proxy
76− GO111MODULE=on GOOS=linux GOARCH=arm GOARM=6 go build -ldflags="-X main.VERSION=${VERSION}" \
77− -o release/$(BINARY)-$(VERSION).linux-armv6.$(GO_VERSION)/$(BINARY) github.com/pusher/oauth2_proxy
78− GO111MODULE=on GOOS=windows GOARCH=amd64 go build -ldflags="-X main.VERSION=${VERSION}" \
79− -o release/$(BINARY)-$(VERSION).windows-amd64.$(GO_VERSION)/$(BINARY) github.com/pusher/oauth2_proxy
80− shasum -a 256 release/$(BINARY)-$(VERSION).darwin-amd64.$(GO_VERSION)/$(BINARY) > release/$(BINARY)-$(VERSION).darwin-amd64-sha256sum.txt
81− shasum -a 256 release/$(BINARY)-$(VERSION).linux-amd64.$(GO_VERSION)/$(BINARY) > release/$(BINARY)-$(VERSION).linux-amd64-sha256sum.txt
82− shasum -a 256 release/$(BINARY)-$(VERSION).linux-arm64.$(GO_VERSION)/$(BINARY) > release/$(BINARY)-$(VERSION).linux-arm64-sha256sum.txt
83− shasum -a 256 release/$(BINARY)-$(VERSION).linux-armv6.$(GO_VERSION)/$(BINARY) > release/$(BINARY)-$(VERSION).linux-armv6-sha256sum.txt
84− shasum -a 256 release/$(BINARY)-$(VERSION).windows-amd64.$(GO_VERSION)/$(BINARY) > release/$(BINARY)-$(VERSION).windows-amd64-sha256sum.txt
85− tar -C release -czvf release/$(BINARY)-$(VERSION).darwin-amd64.$(GO_VERSION).tar.gz $(BINARY)-$(VERSION).darwin-amd64.$(GO_VERSION)
86− tar -C release -czvf release/$(BINARY)-$(VERSION).linux-amd64.$(GO_VERSION).tar.gz $(BINARY)-$(VERSION).linux-amd64.$(GO_VERSION)
87− tar -C release -czvf release/$(BINARY)-$(VERSION).linux-arm64.$(GO_VERSION).tar.gz $(BINARY)-$(VERSION).linux-arm64.$(GO_VERSION)
88− tar -C release -czvf release/$(BINARY)-$(VERSION).linux-armv6.$(GO_VERSION).tar.gz $(BINARY)-$(VERSION).linux-armv6.$(GO_VERSION)
89− tar -C release -czvf release/$(BINARY)-$(VERSION).windows-amd64.$(GO_VERSION).tar.gz $(BINARY)-$(VERSION).windows-amd64.$(GO_VERSION)
README.md+1−1
@@ -1,4 +1,4 @@
1−# oauth2_proxy
1+![OAuth2 Proxy](/docs/logos/OAuth2_Proxy_horizontal.svg)
2
2
3[![Build Status](https://secure.travis-ci.org/pusher/oauth2_proxy.svg?branch=master)](http://travis-ci.org/pusher/oauth2_proxy)
3[![Build Status](https://secure.travis-ci.org/pusher/oauth2_proxy.svg?branch=master)](http://travis-ci.org/pusher/oauth2_proxy)
4[![Go Report Card](https://goreportcard.com/badge/github.com/pusher/oauth2_proxy)](https://goreportcard.com/report/github.com/pusher/oauth2_proxy)
4[![Go Report Card](https://goreportcard.com/badge/github.com/pusher/oauth2_proxy)](https://goreportcard.com/report/github.com/pusher/oauth2_proxy)
configure+4
@@ -5,6 +5,10 @@ GREEN='\033[0;32m'
5BLUE='\033[0;34m'
5BLUE='\033[0;34m'
6NC='\033[0m'
6NC='\033[0m'
7
7
8+if [ -z "${BASH_VERSINFO}" ] || [ -z "${BASH_VERSINFO[0]}" ] || [ ${BASH_VERSINFO[0]} -lt 4 ]; then
9+ echo "This script requires Bash version >= 4"; exit 1;
10+fi
11+
8declare -A tools=()
12declare -A tools=()
9declare -A desired=()
13declare -A desired=()
10
14
contrib/oauth2_proxy_autocomplete.shnew file+30
@@ -0,0 +1,30 @@
1+#
2+# Autocompletion for oauth2_proxy
3+#
4+# To install this, copy/move this file to /etc/bash.completion.d/
5+# or add a line to your ~/.bashrc | ~/.bash_profile that says ". /path/to/oauth2_proxy/contrib/oauth2_proxy_autocomplete.sh"
6+#
7+
8+_oauth2_proxy() {
9+ _oauth2_proxy_commands=$(oauth2_proxy -h 2>&1 | sed -n '/^\s*-/s/ \+/ /gp' | awk '{print $1}' | tr '\n' ' ')
10+ local cur prev
11+ COMPREPLY=()
12+ cur="${COMP_WORDS[COMP_CWORD]}"
13+ prev="${COMP_WORDS[COMP_CWORD-1]}"
14+ case "$prev" in
15+ -@(config|tls-cert-file|tls-key-file|authenticated-emails-file|htpasswd-file|custom-templates-dir|logging-filename|jwt-key-file))
16+ _filedir
17+ return 0
18+ ;;
19+ -provider)
20+ COMPREPLY=( $(compgen -W "google azure facebook github keycloak gitlab linkedin login.gov" -- ${cur}) )
21+ return 0
22+ ;;
23+ -@(http-address|https-address|redirect-url|upstream|basic-auth-password|skip-auth-regex|flush-interval|extra-jwt-issuers|email-domain|whitelist-domain|keycloak-group|azure-tenant|bitbucket-team|bitbucket-repository|github-org|github-team|gitlab-group|google-group|google-admin-email|google-service-account-json|client-id|client_secret|banner|footer|proxy-prefix|ping-path|cookie-name|cookie-secret|cookie-domain|cookie-path|cookie-expire|cookie-refresh|redist-sentinel-master-name|redist-sentinel-connection-urls|logging-max-size|logging-max-age|logging-max-backups|standard-logging-format|request-logging-format|exclude-logging-paths|auth-logging-format|oidc-issuer-url|oidc-jwks-url|login-url|redeem-url|profile-url|resource|validate-url|scope|approval-prompt|signature-key|acr-values|jwt-key|pubjwk-url))
24+ return 0
25+ ;;
26+ esac
27+ COMPREPLY=( $(compgen -W "${_oauth2_proxy_commands}" -- ${cur}) )
28+ return 0;
29+}
30+complete -F _oauth2_proxy oauth2_proxy
dist.sh+43−42
@@ -1,45 +1,46 @@
1−#!/bin/bash
1+#!/usr/bin/env bash
2−# build binary distributions for linux/amd64 and darwin/amd64
2+
3−set -e
3+set -o errexit
4−
4+
5−DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
5+if [[ -z ${BINARY} ]] || [[ -z ${VERSION} ]]; then
6−echo "working dir $DIR"
6+ echo "Missing required env var: BINARY=X VERSION=X $(basename $0)"
7−mkdir -p $DIR/dist
7+ exit 1
8−dep ensure || exit 1
9−
10−os=$(go env GOOS)
11−arch=$(go env GOARCH)
12−version=$(cat $DIR/version.go | grep "const VERSION" | awk '{print $NF}' | sed 's/"//g')
13−goversion=$(go version | awk '{print $3}')
14−sha256sum=()
15−
16−echo "... running tests"
17−./test.sh
18−
19−for os in windows linux darwin; do
20− echo "... building v$version for $os/$arch"
21− EXT=
22− if [ $os = windows ]; then
23− EXT=".exe"
24− fi
8+fi
25− BUILD=$(mktemp -d ${TMPDIR:-/tmp}/oauth2_proxy.XXXXXX)
26− TARGET="oauth2_proxy-$version.$os-$arch.$goversion"
27− FILENAME="oauth2_proxy-$version.$os-$arch$EXT"
28− GOOS=$os GOARCH=$arch CGO_ENABLED=0 \
29− go build -ldflags="-s -w" -o $BUILD/$TARGET/$FILENAME || exit 1
30− pushd $BUILD/$TARGET
31− sha256sum+=("$(shasum -a 256 $FILENAME || exit 1)")
32− cd .. && tar czvf $TARGET.tar.gz $TARGET
33− mv $TARGET.tar.gz $DIR/dist
34− popd
35−done
36
9
37−checksum_file="sha256sum.txt"
10+# Check for Go version 1.13.*
38−cd $DIR/dist
11+GO_VERSION=$(go version | awk '{print $3}')
39−if [ -f $checksum_file ]; then
12+if [[ ! "${GO_VERSION}" =~ ^go1.13.* ]]; then
40− rm $checksum_file
13+ echo "Go version must be >= go1.13"
14+ exit 1
41fi
15fi
42−touch $checksum_file
16+
43−for checksum in "${sha256sum[@]}"; do
17+ARCHS=(darwin-amd64 linux-amd64 linux-arm64 linux-armv6 windows-amd64)
44− echo "$checksum" >> $checksum_file
18+
19+mkdir -p release
20+
21+# Create architecture specific release dirs
22+for ARCH in "${ARCHS[@]}"; do
23+ mkdir -p release/${BINARY}-${VERSION}.${ARCH}.${GO_VERSION}
24+
25+ GO_OS=$(echo $ARCH | awk -F- '{print $1}')
26+ GO_ARCH=$(echo $ARCH | awk -F- '{print $2}')
27+
28+ # Create architecture specific binaries
29+ if [[ ${GO_ARCH} == "armv6" ]]; then
30+ GO111MODULE=on GOOS=${GO_OS} GOARCH=arm GOARM=6 go build -ldflags="-X main.VERSION=${VERSION}" \
31+ -o release/${BINARY}-${VERSION}.${ARCH}.${GO_VERSION}/${BINARY} github.com/pusher/oauth2_proxy
32+ else
33+ GO111MODULE=on GOOS=${GO_OS} GOARCH=${GO_ARCH} go build -ldflags="-X main.VERSION=${VERSION}" \
34+ -o release/${BINARY}-${VERSION}.${ARCH}.${GO_VERSION}/${BINARY} github.com/pusher/oauth2_proxy
35+ fi
36+
37+ cd release
38+
39+ # Create sha256sum for architecture specific binary
40+ shasum -a 256 ${BINARY}-${VERSION}.${ARCH}.${GO_VERSION}/${BINARY} > ${BINARY}-${VERSION}.${ARCH}-sha256sum.txt
41+
42+ # Create tar file for architecture specific binary
43+ tar -czvf ${BINARY}-${VERSION}.${ARCH}.${GO_VERSION}.tar.gz ${BINARY}-${VERSION}.${ARCH}.${GO_VERSION}
44+
45+ cd ..
45done
46done
docs/0_index.md+1−1
@@ -5,7 +5,7 @@ permalink: /
5nav_order: 0
5nav_order: 0
6---
6---
7
7
8−# oauth2_proxy
8+![OAuth2 Proxy](/logos/OAuth2_Proxy_horizontal.svg)
9
9
10A reverse proxy and static file server that provides authentication using Providers (Google, GitHub, and others)
10A reverse proxy and static file server that provides authentication using Providers (Google, GitHub, and others)
11to validate accounts by email, domain or group.
11to validate accounts by email, domain or group.
docs/1_installation.md+3−3
@@ -9,7 +9,7 @@ nav_order: 1
9
9
101. Choose how to deploy:
101. Choose how to deploy:
11
11
12− a. Download [Prebuilt Binary](https://github.com/pusher/oauth2_proxy/releases) (current release is `v3.2.0`)
12+ a. Download [Prebuilt Binary](https://github.com/pusher/oauth2_proxy/releases) (current release is `v4.0.0`)
13
13
14 b. Build with `$ go get github.com/pusher/oauth2_proxy` which will put the binary in `$GOROOT/bin`
14 b. Build with `$ go get github.com/pusher/oauth2_proxy` which will put the binary in `$GOROOT/bin`
15
15
@@ -18,8 +18,8 @@ nav_order: 1
18Prebuilt binaries can be validated by extracting the file and verifying it against the `sha256sum.txt` checksum file provided for each release starting with version `v3.0.0`.
18Prebuilt binaries can be validated by extracting the file and verifying it against the `sha256sum.txt` checksum file provided for each release starting with version `v3.0.0`.
19
19
20```
20```
21−sha256sum -c sha256sum.txt 2>&1 | grep OK
21+$ sha256sum -c sha256sum.txt 2>&1 | grep OK
22−oauth2_proxy-3.2.0.linux-amd64: OK
22+oauth2_proxy-4.0.0.linux-amd64: OK
23```
23```
24
24
252. [Select a Provider and Register an OAuth Application with a Provider](auth-configuration)
252. [Select a Provider and Register an OAuth Application with a Provider](auth-configuration)
docs/5_endpoints.md+1
@@ -14,4 +14,5 @@ OAuth2 Proxy responds directly to the following endpoints. All other endpoints w
14- /oauth2/sign_in - the login page, which also doubles as a sign out page (it clears cookies)
14- /oauth2/sign_in - the login page, which also doubles as a sign out page (it clears cookies)
15- /oauth2/start - a URL that will redirect to start the OAuth cycle
15- /oauth2/start - a URL that will redirect to start the OAuth cycle
16- /oauth2/callback - the URL used at the end of the OAuth cycle. The oauth app will be configured with this as the callback url.
16- /oauth2/callback - the URL used at the end of the OAuth cycle. The oauth app will be configured with this as the callback url.
17+- /oauth2/userinfo - the URL is used to return user's email from the session in JSON format.
17- /oauth2/auth - only returns a 202 Accepted response or a 401 Unauthorized response; for use with the [Nginx `auth_request` directive](#nginx-auth-request)
18- /oauth2/auth - only returns a 202 Accepted response or a 401 Unauthorized response; for use with the [Nginx `auth_request` directive](#nginx-auth-request)
docs/_config.yml+1
@@ -14,6 +14,7 @@
14# You can create any custom variable you would like, and they will be accessible
14# You can create any custom variable you would like, and they will be accessible
15# in the templates via {{ site.myvariable }}.
15# in the templates via {{ site.myvariable }}.
16title: OAuth2_Proxy
16title: OAuth2_Proxy
17+logo: /logos/OAuth2_Proxy_horizontal.svg
17description: >- # this means to ignore newlines until "baseurl:"
18description: >- # this means to ignore newlines until "baseurl:"
18 OAuth2_Proxy documentation site
19 OAuth2_Proxy documentation site
19baseurl: "/oauth2_proxy" # the subpath of your site, e.g. /blog
20baseurl: "/oauth2_proxy" # the subpath of your site, e.g. /blog
docs/configuration/configuration.md+1−1
@@ -117,7 +117,7 @@ See below for provider specific options
117
117
118### Upstreams Configuration
118### Upstreams Configuration
119
119
120−`oauth2_proxy` supports having multiple upstreams, and has the option to pass requests on to HTTP(S) servers or serve static files from the file system. HTTP and HTTPS upstreams are configured by providing a URL such as `http://127.0.0.1:8080/` for the upstream parameter, that will forward all authenticated requests to be forwarded to the upstream server. If you instead provide `http://127.0.0.1:8080/some/path/` then it will only be requests that start with `/some/path/` which are forwarded to the upstream.
120+`oauth2_proxy` supports having multiple upstreams, and has the option to pass requests on to HTTP(S) servers or serve static files from the file system. HTTP and HTTPS upstreams are configured by providing a URL such as `http://127.0.0.1:8080/` for the upstream parameter, this will forward all authenticated requests to the upstream server. If you instead provide `http://127.0.0.1:8080/some/path/` then it will only be requests that start with `/some/path/` which are forwarded to the upstream.
121
121
122Static file paths are configured as a file:// URL. `file:///var/www/static/` will serve the files from that directory at `http://[oauth2_proxy url]/var/www/static/`, which may not be what you want. You can provide the path to where the files should be available by adding a fragment to the configured URL. The value of the fragment will then be used to specify which path the files are available at. `file:///var/www/static/#/static/` will ie. make `/var/www/static/` available at `http://[oauth2_proxy url]/static/`.
122Static file paths are configured as a file:// URL. `file:///var/www/static/` will serve the files from that directory at `http://[oauth2_proxy url]/var/www/static/`, which may not be what you want. You can provide the path to where the files should be available by adding a fragment to the configured URL. The value of the fragment will then be used to specify which path the files are available at. `file:///var/www/static/#/static/` will ie. make `/var/www/static/` available at `http://[oauth2_proxy url]/static/`.
123
123
docs/logos/OAuth2_Proxy_horizontal.pngnew file

Binary file not shown.

docs/logos/OAuth2_Proxy_horizontal.svgnew file+1
@@ -0,0 +1 @@
1+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 840.55645 412.45904"><defs><style>.cls-1{fill:#333;}.cls-2{fill:#7c7c82;}.cls-3{fill:#06ed94;}.cls-4{fill:#059b61;}.cls-5{fill:#1957ba;}.cls-6{fill:#6ca8ff;}</style></defs><title>OAuth2_Proxy_logo_v3</title><path class="cls-1" d="M474.20933,196.63116a41.46679,41.46679,0,0,1-16.33506-3.18964,39.80757,39.80757,0,0,1-21.67092-21.83518,42.99409,42.99409,0,0,1,0-32.23,39.80765,39.80765,0,0,1,21.67092-21.83518,43.41665,43.41665,0,0,1,32.67012,0,39.8101,39.8101,0,0,1,21.66989,21.83518,42.994,42.994,0,0,1,0,32.23,39.81,39.81,0,0,1-21.66989,21.83518A41.485,41.485,0,0,1,474.20933,196.63116Zm0-14.07985a26.63714,26.63714,0,0,0,10.28431-1.9799,25.89889,25.89889,0,0,0,8.36072-5.50012,25.04229,25.04229,0,0,0,5.60963-8.525,31.04294,31.04294,0,0,0,0-22.11,25.07963,25.07963,0,0,0-5.60963-8.52549,25.9145,25.9145,0,0,0-8.36072-5.49961,27.6962,27.6962,0,0,0-20.56965,0,25.89233,25.89233,0,0,0-8.36072,5.49961,25.03434,25.03434,0,0,0-5.60962,8.52549,31.04279,31.04279,0,0,0,0,22.11,24.99714,24.99714,0,0,0,5.60962,8.525,25.87675,25.87675,0,0,0,8.36072,5.50012A26.63112,26.63112,0,0,0,474.20933,182.55131Z"/><path class="cls-1" d="M548.12931,116.11083h16.94044l29.59051,78.76048H578.26942L571.77961,176.061H541.41945l-6.48981,18.81033H518.53983Zm18.81032,46.3104-7.03941-19.69-2.8606-9.56994h-.88018l-2.85957,9.56994-7.04044,19.69Z"/><path class="cls-1" d="M636.67873,187.83138h-.88019a18.26469,18.26469,0,0,1-6.82039,6.43506,19.94541,19.94541,0,0,1-9.78947,2.36472q-10.23059,0-15.23483-6.26977-5.00682-6.27052-5.00527-16.7204V140.97087H613.359v30.91027q0,5.832,2.58477,8.635,2.58321,2.80482,7.53425,2.80533a10.50006,10.50006,0,0,0,5.22532-1.265,12.21407,12.21407,0,0,0,3.85029-3.41019,15.44293,15.44293,0,0,0,2.41947-5.06,22.69746,22.69746,0,0,0,.82543-6.215V140.97087H650.209v53.90044H636.67873Z"/><path class="cls-1" d="M669.01723,153.29138h-9.461V140.97087h9.461V124.471h14.4094v16.49984h13.2007v12.32051h-13.2007V174.829a15.54561,15.54561,0,0,0,.38534,3.57135,5.7936,5.7936,0,0,0,1.48454,2.77744,5.44914,5.44914,0,0,0,4.39988,1.81357,8.00547,8.00547,0,0,0,5.06-1.4303l4.07034,12.65007a23.83924,23.83924,0,0,1-5.44434,1.81512,31.8029,31.8029,0,0,1-6.43505.60487,19.66528,19.66528,0,0,1-7.31524-1.29084,15.54151,15.54151,0,0,1-5.44537-3.53675q-5.17107-5.05331-5.16953-14.37272Z"/><path class="cls-1" d="M707.735,116.11083h14.4094v22.1105l-.87915,9.79h.87915a17.85447,17.85447,0,0,1,6.65511-6.325,19.31492,19.31492,0,0,1,9.73574-2.47526q10.44909,0,15.45488,6.16026,5.00371,6.16128,5.00527,16.8299v32.67013H744.585V163.961q0-5.60962-2.64055-8.525a9.44018,9.44018,0,0,0-7.37-2.91484,10.82482,10.82482,0,0,0-5.28007,1.265,11.76092,11.76092,0,0,0-3.904,3.465,16.09429,16.09429,0,0,0-2.4205,5.11478,22.748,22.748,0,0,0-.82544,6.215v26.29034H707.735Z"/><path class="cls-1" d="M769.77353,194.87131V181.78115q6.48979-6.37824,11.98992-11.87991,2.31048-2.3097,4.73048-4.73,2.4174-2.41818,4.50939-4.50991,2.08888-2.08889,3.74078-3.795,1.6488-1.70458,2.53-2.69531,1.75726-2.08811,3.13436-3.73975a18.16413,18.16413,0,0,0,2.20046-3.24542,16.47472,16.47472,0,0,0,1.26449-3.18963,13.02445,13.02445,0,0,0,.44009-3.465,8.4276,8.4276,0,0,0-2.85956-6.325,10.93308,10.93308,0,0,0-7.81009-2.6953q-4.951,0-7.70058,2.80533a14.59773,14.59773,0,0,0-3.73975,6.325l-13.09015-5.3901a24.55547,24.55547,0,0,1,2.8606-6.105,22.76637,22.76637,0,0,1,5.06-5.61014,26.44875,26.44875,0,0,1,7.31421-4.07034,26.86184,26.86184,0,0,1,9.5157-1.59456,29.71878,29.71878,0,0,1,10.3401,1.70458,23.00883,23.00883,0,0,1,7.81008,4.67521,20.92923,20.92923,0,0,1,4.94949,6.98517,21.28551,21.28551,0,0,1,1.76037,8.635,25.00612,25.00612,0,0,1-3.465,12.92486,56.87664,56.87664,0,0,1-8.41546,10.945l-9.45992,9.46043q-3.74077,3.74077-8.47022,8.35968l.44009.66014h30.4707v12.65007Z"/><path class="cls-2" d="M447.03623,265.05824v31.9h-10.12V218.19774h26.84045a24.25664,24.25664,0,0,1,17.38054,6.82039,22.75761,22.75761,0,0,1,0,33.32974,24.22463,24.22463,0,0,1-17.38054,6.71037Zm0-37.18055v27.50059h16.94045a13.378,13.378,0,0,0,10.01054-4.07033,13.80577,13.80577,0,0,0-10.01054-23.43026Z"/><path class="cls-2" d="M507.31645,296.95821h-10.12V243.05777h9.68v8.80029h.44009a14.60805,14.60805,0,0,1,6.32451-7.31523,17.56035,17.56035,0,0,1,9.40516-3.02486,18.97706,18.97706,0,0,1,7.48054,1.31976l-3.08065,9.7905a16.79182,16.79182,0,0,0-5.94021-.77017A13.09674,13.09674,0,0,0,511.551,256.478a15.45278,15.45278,0,0,0-4.23459,10.78019Z"/><path class="cls-2" d="M530.74567,270.00825q0-12.42951,7.81009-20.57018a26.6222,26.6222,0,0,1,19.91055-8.14015q11.98785,0,19.8,8.14015,7.92011,8.14172,7.91959,20.57018,0,12.53952-7.91959,20.57017-7.81164,8.1417-19.8,8.13964a26.616,26.616,0,0,1-19.91055-8.13964Q530.74412,282.439,530.74567,270.00825Zm10.12,0q0,8.691,5.06,14.07984a17.28222,17.28222,0,0,0,25.08008,0q5.05952-5.388,5.06-14.07984,0-8.58025-5.06-13.97034a17.04544,17.04544,0,0,0-25.08008,0Q540.86469,261.429,540.86572,270.00825Z"/><path class="cls-2" d="M619.95626,269.56816l19.25041,27.39005H627.32625l-13.3102-19.91-13.09016,19.91H589.04651l19.03036-27.39005L589.4866,243.05777h11.43929l13.31021,19.03037,12.65006-19.03037h11.88042Z"/><path class="cls-2" d="M694.4248,243.05777l-33.77035,77.66076H650.20484L662.74437,293.548l-22.21949-50.49024h11.00024l16.06026,38.72036h.219l15.62017-38.72036Z"/><path class="cls-3" d="M211.931,392.63536c-101.38027,0-183.85932-82.47852-183.85932-183.8588S110.55071,24.91776,211.931,24.91776,395.7903,107.39629,395.7903,208.77656,313.31125,392.63536,211.931,392.63536Zm0-354.70389c-94.2045,0-170.84457,76.64111-170.84457,170.84509S117.72648,379.62165,211.931,379.62165s170.84457-76.6411,170.84457-170.84509S306.13548,37.93147,211.931,37.93147Z"/><polygon class="cls-4" points="244.602 112.007 239.295 131.843 281.959 166.856 322.055 161.912 244.602 112.007"/><polygon class="cls-3" points="348.449 195.562 348.449 158.734 290.209 109.179 244.602 112.007 300.209 156.67 76.642 156.67 76.642 198.05 348.449 195.562"/><polygon class="cls-5" points="180.489 297.175 185.796 277.34 141.87 240.558 103.037 247.271 180.489 297.175"/><polygon class="cls-6" points="76.642 213.62 76.642 250.448 134.882 300.004 180.489 297.175 124.882 252.512 348.449 252.512 348.449 211.132 76.642 213.62"/></svg>
docs/logos/OAuth2_Proxy_icon.pngnew file

Binary file not shown.

docs/logos/OAuth2_Proxy_icon.svgnew file+1
@@ -0,0 +1 @@
1+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 360 360"><defs><style>.cls-1{fill:#06ed94;}.cls-2{fill:#059b61;}.cls-3{fill:#1957ba;}.cls-4{fill:#6ca8ff;}</style></defs><title>OAuth2_Proxy_logo_v3</title><path class="cls-1" d="M179.00205,349.728c-93.69483,0-169.921-76.22687-169.921-169.9217S85.30722,9.88462,179.00205,9.88462c93.69408,0,169.92021,76.22687,169.92021,169.9217S272.69613,349.728,179.00205,349.728Zm0-327.81581c-87.06238,0-157.89338,70.831-157.89338,157.89411s70.831,157.89411,157.89338,157.89411,157.89411-70.831,157.89411-157.89411S266.06442,21.91221,179.00205,21.91221Z"/><polygon class="cls-2" points="208.628 90.373 203.724 108.705 243.153 141.064 280.21 136.494 208.628 90.373"/><polygon class="cls-1" points="304.604 167.593 304.604 133.558 250.778 87.759 208.628 90.373 260.02 131.65 53.401 131.65 53.401 169.893 304.604 167.593"/><polygon class="cls-3" points="149.376 261.504 154.28 243.172 113.684 209.179 77.795 215.382 149.376 261.504"/><polygon class="cls-4" points="53.401 184.283 53.401 218.319 107.226 264.118 149.376 261.504 97.984 220.226 304.604 220.226 304.604 181.984 53.401 184.283"/></svg>
docs/logos/OAuth2_Proxy_vertical.pngnew file

Binary file not shown.

docs/logos/OAuth2_Proxy_vertical.svgnew file+1
@@ -0,0 +1 @@
1+<svg id="Layer_1" data-name="Layer 1" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 271.25527 412.45904"><defs><style>.cls-1{fill:#06ed94;}.cls-2{fill:#333;}.cls-3{fill:#7c7c82;}.cls-4{fill:#059b61;}.cls-5{fill:#1957ba;}.cls-6{fill:#6ca8ff;}</style></defs><title>OAuth2_Proxy_logo_v3</title><path class="cls-1" d="M135.48689,257.62084A124.15145,124.15145,0,1,1,259.6386,133.46912,124.29216,124.29216,0,0,1,135.48689,257.62084Zm0-239.515a115.36361,115.36361,0,1,0,115.365,115.36333A115.4943,115.4943,0,0,0,135.48689,18.10579Z"/><path class="cls-2" d="M43.349,326.11062a25.03594,25.03594,0,0,1-9.86153-1.9257,24.03513,24.03513,0,0,1-13.08258-13.18176,25.95077,25.95077,0,0,1,0-19.45748,24.03513,24.03513,0,0,1,13.08258-13.18176,25.03611,25.03611,0,0,1,9.86153-1.92569,25.04373,25.04373,0,0,1,9.86152,1.92569,24.03191,24.03191,0,0,1,13.082,13.18176,25.95077,25.95077,0,0,1,0,19.45748,24.03191,24.03191,0,0,1-13.082,13.18176A25.04355,25.04355,0,0,1,43.349,326.11062Zm0-8.5a16.08167,16.08167,0,0,0,6.20905-1.19509,15.64,15.64,0,0,0,5.047-3.32023,15.1262,15.1262,0,0,0,3.3869-5.14675,18.7484,18.7484,0,0,0,0-13.34816,15.13964,15.13964,0,0,0-3.3869-5.1462,15.65025,15.65025,0,0,0-5.047-3.32078,16.727,16.727,0,0,0-12.4181,0,15.62678,15.62678,0,0,0-5.047,3.32078,15.1001,15.1001,0,0,0-3.3869,5.1462,18.74056,18.74056,0,0,0,0,13.34816,15.08674,15.08674,0,0,0,3.3869,5.14675,15.61654,15.61654,0,0,0,5.047,3.32023A16.07574,16.07574,0,0,0,43.349,317.61057Z"/><path class="cls-2" d="M87.97487,277.50053H98.20116l17.864,47.54779h-9.89513l-3.91805-11.3558H83.92349L80.006,325.04832H70.11085Zm11.35526,27.95752-4.24976-11.887L93.35359,287.794h-.53115l-1.72678,5.77708-4.24975,11.887Z"/><path class="cls-2" d="M141.432,320.798h-.53115a11.02494,11.02494,0,0,1-4.117,3.885,12.04189,12.04189,0,0,1-5.91041,1.42761q-6.17625,0-9.19759-3.78527-3.02268-3.78444-3.0216-10.0935v-19.723h8.6995V311.169q0,3.521,1.56039,5.21287a5.88,5.88,0,0,0,4.54893,1.69372,6.335,6.335,0,0,0,3.15439-.76366,7.36817,7.36817,0,0,0,2.3246-2.05849,9.31986,9.31986,0,0,0,1.46067-3.0552,13.70705,13.70705,0,0,0,.49809-3.75166V292.5088h8.6995v32.53952H141.432Z"/><path class="cls-2" d="M160.95505,299.94654h-5.711V292.5088h5.711v-9.96125h8.6995v9.96125h7.96889v7.43774h-7.96889v13.00237a9.40943,9.40943,0,0,0,.232,2.156,3.50429,3.50429,0,0,0,.89645,1.67692,3.29006,3.29006,0,0,0,2.6563,1.09428,4.8322,4.8322,0,0,0,3.0552-.86339l2.45685,7.63719a14.4013,14.4013,0,0,1-3.28718,1.09591,19.24352,19.24352,0,0,1-3.885.36476,11.87693,11.87693,0,0,1-4.41614-.77884,9.38856,9.38856,0,0,1-3.28718-2.13545q-3.12186-3.05113-3.12078-8.67673Z"/><path class="cls-2" d="M184.32841,277.50053h8.6995v13.34815l-.53115,5.90987h.53115a10.7816,10.7816,0,0,1,4.01778-3.81832,11.65893,11.65893,0,0,1,5.87681-1.49427q6.30877,0,9.33037,3.71914,3.02106,3.71941,3.0216,10.16016v19.72306H206.575V306.38757a7.42672,7.42672,0,0,0-1.59345-5.14621,5.69992,5.69992,0,0,0-4.44975-1.75984,6.53689,6.53689,0,0,0-3.18745.76366,7.09646,7.09646,0,0,0-2.35711,2.09154A9.73186,9.73186,0,0,0,193.526,305.425a13.71381,13.71381,0,0,0-.49809,3.7522v15.87113h-8.6995Z"/><path class="cls-2" d="M221.78162,325.04832v-7.90223q3.91778-3.85111,7.23829-7.17217l2.85575-2.85575q1.46012-1.45931,2.72242-2.72242,1.26094-1.26095,2.25793-2.291.99672-1.02924,1.52733-1.627,1.06176-1.26095,1.89264-2.25794a10.94254,10.94254,0,0,0,1.32842-1.9593,9.958,9.958,0,0,0,.76366-1.92569,7.87231,7.87231,0,0,0,.26558-2.09154,5.08948,5.08948,0,0,0-1.72679-3.81887,6.60122,6.60122,0,0,0-4.71478-1.62652,6.212,6.212,0,0,0-4.64865,1.69318,8.82073,8.82073,0,0,0-2.25794,3.81833l-7.90223-3.25412A14.83518,14.83518,0,0,1,223.11,285.3697a13.75313,13.75313,0,0,1,3.05466-3.38636,15.96788,15.96788,0,0,1,4.41614-2.45739,16.21169,16.21169,0,0,1,5.744-.96258,17.927,17.927,0,0,1,6.24211,1.02924,13.884,13.884,0,0,1,4.71532,2.82215,12.6457,12.6457,0,0,1,2.98854,4.21669,12.85272,12.85272,0,0,1,1.0623,5.21341,15.09115,15.09115,0,0,1-2.09208,7.80251,34.27852,34.27852,0,0,1-5.08008,6.60741l-5.711,5.7115q-2.25848,2.25766-5.11369,5.04648l.26612.3989h18.39517v7.63666Z"/><path class="cls-3" d="M66.12613,369.53928v19.258H60.01681V341.24951H76.22017A14.64408,14.64408,0,0,1,86.71258,345.367a13.739,13.739,0,0,1,0,20.12141,14.62588,14.62588,0,0,1-10.49241,4.05085Zm0-22.446v16.60227H76.353a8.07584,8.07584,0,0,0,6.0432-2.45739,8.33447,8.33447,0,0,0-6.0432-14.14488Z"/><path class="cls-3" d="M102.51758,388.7973H96.40771V356.25779H102.252v5.31259h.26558a8.81855,8.81855,0,0,1,3.81832-4.41614,10.59845,10.59845,0,0,1,5.6779-1.82651,11.44924,11.44924,0,0,1,4.51533.79727l-1.859,5.90987a10.15183,10.15183,0,0,0-3.58636-.46449,7.90481,7.90481,0,0,0-6.00959,2.78909,9.32607,9.32607,0,0,0-2.55657,6.50768Z"/><path class="cls-3" d="M116.66137,372.52727a17.213,17.213,0,0,1,4.71478-12.4181,16.07132,16.07132,0,0,1,12.01974-4.91423,15.85788,15.85788,0,0,1,11.95307,4.91423,18.51723,18.51723,0,0,1,0,24.83674,15.85544,15.85544,0,0,1-11.95307,4.91369,16.06883,16.06883,0,0,1-12.01974-4.91369A17.2098,17.2098,0,0,1,116.66137,372.52727Zm6.10933,0a11.91437,11.91437,0,0,0,3.05466,8.50059,10.43448,10.43448,0,0,0,15.14106,0,13.26169,13.26169,0,0,0,0-16.934,10.28983,10.28983,0,0,0-15.14106,0A11.823,11.823,0,0,0,122.7707,372.52727Z"/><path class="cls-3" d="M170.51794,372.2617l11.62137,16.5356h-7.17217l-8.03556-12.01973-7.90223,12.01973h-7.17216l11.48858-16.5356-11.223-16.00391h6.90659l8.035,11.48858,7.6372-11.48858h7.17162Z"/><path class="cls-3" d="M215.47447,356.25779l-20.387,46.8833H188.7787l7.57053-16.40227-13.41428-30.481h6.641l9.69513,23.37553h.13279l9.4301-23.37553Z"/><polygon class="cls-4" points="157.133 68.125 153.55 81.519 182.358 105.162 209.433 101.824 157.133 68.125"/><polygon class="cls-1" points="227.257 124.546 227.257 99.678 187.929 66.216 157.133 68.125 194.682 98.284 43.718 98.284 43.718 126.226 227.257 124.546"/><polygon class="cls-5" points="113.841 193.161 117.424 179.767 87.763 154.93 61.541 159.463 113.841 193.161"/><polygon class="cls-6" points="43.718 136.74 43.718 161.608 83.045 195.071 113.841 193.161 76.292 163.002 227.257 163.002 227.257 135.06 43.718 136.74"/></svg>
main.go+2
@@ -91,6 +91,8 @@ func main() {
91 flagSet.String("redis-connection-url", "", "URL of redis server for redis session storage (eg: redis://HOST[:PORT])")
91 flagSet.String("redis-connection-url", "", "URL of redis server for redis session storage (eg: redis://HOST[:PORT])")
92 flagSet.Bool("redis-use-sentinel", false, "Connect to redis via sentinels. Must set --redis-sentinel-master-name and --redis-sentinel-connection-urls to use this feature")
92 flagSet.Bool("redis-use-sentinel", false, "Connect to redis via sentinels. Must set --redis-sentinel-master-name and --redis-sentinel-connection-urls to use this feature")
93 flagSet.String("redis-sentinel-master-name", "", "Redis sentinel master name. Used in conjunction with --redis-use-sentinel")
93 flagSet.String("redis-sentinel-master-name", "", "Redis sentinel master name. Used in conjunction with --redis-use-sentinel")
94+ flagSet.String("redis-ca-path", "", "Redis custom CA path")
95+ flagSet.Bool("redis-insecure-skip-tls-verify", false, "Use insecure TLS connection to redis")
94 flagSet.Var(&redisSentinelConnectionURLs, "redis-sentinel-connection-urls", "List of Redis sentinel connection URLs (eg redis://HOST[:PORT]). Used in conjunction with --redis-use-sentinel")
96 flagSet.Var(&redisSentinelConnectionURLs, "redis-sentinel-connection-urls", "List of Redis sentinel connection URLs (eg redis://HOST[:PORT]). Used in conjunction with --redis-use-sentinel")
95
97
96 flagSet.String("logging-filename", "", "File to log requests to, empty for stdout")
98 flagSet.String("logging-filename", "", "File to log requests to, empty for stdout")
oauthproxy.go+21
@@ -4,6 +4,7 @@ import (
4 "context"
4 "context"
5 "crypto/tls"
5 "crypto/tls"
6 b64 "encoding/base64"
6 b64 "encoding/base64"
7+ "encoding/json"
7 "errors"
8 "errors"
8 "fmt"
9 "fmt"
9 "html/template"
10 "html/template"
@@ -75,6 +76,7 @@ type OAuthProxy struct {
75 OAuthStartPath string
76 OAuthStartPath string
76 OAuthCallbackPath string
77 OAuthCallbackPath string
77 AuthOnlyPath string
78 AuthOnlyPath string
79+ UserInfoPath string
78
80
79 redirectURL *url.URL // the url to receive requests at
81 redirectURL *url.URL // the url to receive requests at
80 whitelistDomains []string
82 whitelistDomains []string
@@ -266,6 +268,7 @@ func NewOAuthProxy(opts *Options, validator func(string) bool) *OAuthProxy {
266 OAuthStartPath: fmt.Sprintf("%s/start", opts.ProxyPrefix),
268 OAuthStartPath: fmt.Sprintf("%s/start", opts.ProxyPrefix),
267 OAuthCallbackPath: fmt.Sprintf("%s/callback", opts.ProxyPrefix),
269 OAuthCallbackPath: fmt.Sprintf("%s/callback", opts.ProxyPrefix),
268 AuthOnlyPath: fmt.Sprintf("%s/auth", opts.ProxyPrefix),
270 AuthOnlyPath: fmt.Sprintf("%s/auth", opts.ProxyPrefix),
271+ UserInfoPath: fmt.Sprintf("%s/userinfo", opts.ProxyPrefix),
269
272
270 ProxyPrefix: opts.ProxyPrefix,
273 ProxyPrefix: opts.ProxyPrefix,
271 provider: opts.provider,
274 provider: opts.provider,
@@ -611,6 +614,8 @@ func (p *OAuthProxy) ServeHTTP(rw http.ResponseWriter, req *http.Request) {
611 p.OAuthCallback(rw, req)
614 p.OAuthCallback(rw, req)
612 case path == p.AuthOnlyPath:
615 case path == p.AuthOnlyPath:
613 p.AuthenticateOnly(rw, req)
616 p.AuthenticateOnly(rw, req)
617+ case path == p.UserInfoPath:
618+ p.UserInfo(rw, req)
614 default:
619 default:
615 p.Proxy(rw, req)
620 p.Proxy(rw, req)
616 }
621 }
@@ -639,6 +644,22 @@ func (p *OAuthProxy) SignIn(rw http.ResponseWriter, req *http.Request) {
639 }
644 }
640}
645}
641
646
647+//UserInfo endpoint outputs session email in JSON format
648+func (p *OAuthProxy) UserInfo(rw http.ResponseWriter, req *http.Request) {
649+
650+ session, err := p.getAuthenticatedSession(rw, req)
651+ if err != nil {
652+ http.Error(rw, http.StatusText(http.StatusUnauthorized), http.StatusUnauthorized)
653+ return
654+ }
655+ userInfo := struct {
656+ Email string `json:"email"`
657+ }{session.Email}
658+ rw.Header().Set("Content-Type", "application/json")
659+ rw.WriteHeader(http.StatusOK)
660+ json.NewEncoder(rw).Encode(userInfo)
661+}
662+
642// SignOut sends a response to clear the authentication cookie
663// SignOut sends a response to clear the authentication cookie
643func (p *OAuthProxy) SignOut(rw http.ResponseWriter, req *http.Request) {
664func (p *OAuthProxy) SignOut(rw http.ResponseWriter, req *http.Request) {
644 p.ClearSessionCookie(rw, req)
665 p.ClearSessionCookie(rw, req)
oauthproxy_test.go+26
@@ -855,6 +855,32 @@ func TestProcessCookieFailIfRefreshSetAndCookieExpired(t *testing.T) {
855 }
855 }
856}
856}
857
857
858+func NewUserInfoEndpointTest() *ProcessCookieTest {
859+ pcTest := NewProcessCookieTestWithDefaults()
860+ pcTest.req, _ = http.NewRequest("GET",
861+ pcTest.opts.ProxyPrefix+"/userinfo", nil)
862+ return pcTest
863+}
864+
865+func TestUserInfoEndpointAccepted(t *testing.T) {
866+ test := NewUserInfoEndpointTest()
867+ startSession := &sessions.SessionState{
868+ Email: "john.doe@example.com", AccessToken: "my_access_token"}
869+ test.SaveSession(startSession)
870+
871+ test.proxy.ServeHTTP(test.rw, test.req)
872+ assert.Equal(t, http.StatusOK, test.rw.Code)
873+ bodyBytes, _ := ioutil.ReadAll(test.rw.Body)
874+ assert.Equal(t, "{\"email\":\"john.doe@example.com\"}\n", string(bodyBytes))
875+}
876+
877+func TestUserInfoEndpointUnauthorizedOnNoCookieSetError(t *testing.T) {
878+ test := NewUserInfoEndpointTest()
879+
880+ test.proxy.ServeHTTP(test.rw, test.req)
881+ assert.Equal(t, http.StatusUnauthorized, test.rw.Code)
882+}
883+
858func NewAuthOnlyEndpointTest(modifiers ...OptionsModifier) *ProcessCookieTest {
884func NewAuthOnlyEndpointTest(modifiers ...OptionsModifier) *ProcessCookieTest {
859 pcTest := NewProcessCookieTestWithOptionsModifiers(modifiers...)
885 pcTest := NewProcessCookieTestWithOptionsModifiers(modifiers...)
860 pcTest.req, _ = http.NewRequest("GET",
886 pcTest.req, _ = http.NewRequest("GET",
pkg/apis/options/sessions.go+2
@@ -27,4 +27,6 @@ type RedisStoreOptions struct {
27 UseSentinel bool `flag:"redis-use-sentinel" cfg:"redis_use_sentinel" env:"OAUTH2_PROXY_REDIS_USE_SENTINEL"`
27 UseSentinel bool `flag:"redis-use-sentinel" cfg:"redis_use_sentinel" env:"OAUTH2_PROXY_REDIS_USE_SENTINEL"`
28 SentinelMasterName string `flag:"redis-sentinel-master-name" cfg:"redis_sentinel_master_name" env:"OAUTH2_PROXY_REDIS_SENTINEL_MASTER_NAME"`
28 SentinelMasterName string `flag:"redis-sentinel-master-name" cfg:"redis_sentinel_master_name" env:"OAUTH2_PROXY_REDIS_SENTINEL_MASTER_NAME"`
29 SentinelConnectionURLs []string `flag:"redis-sentinel-connection-urls" cfg:"redis_sentinel_connection_urls" env:"OAUTH2_PROXY_REDIS_SENTINEL_CONNECTION_URLS"`
29 SentinelConnectionURLs []string `flag:"redis-sentinel-connection-urls" cfg:"redis_sentinel_connection_urls" env:"OAUTH2_PROXY_REDIS_SENTINEL_CONNECTION_URLS"`
30+ RedisCAPath string `flag:"redis-ca-path" cfg:"redis_ca_path" env:"OAUTH2_PROXY_REDIS_CA_PATH"`
31+ RedisInsecureTLS bool `flag:"redis-insecure-skip-tls-verify" cfg:"redis_insecure_skip_tls_verify" env:"OAUTH2_PROXY_REDIS_INSECURE_SKIP_TLS_VERIFY"`
30}
32}
pkg/requests/requests.go+15−6
@@ -18,17 +18,23 @@ func Request(req *http.Request) (*simplejson.Json, error) {
18 return nil, err
18 return nil, err
19 }
19 }
20 body, err := ioutil.ReadAll(resp.Body)
20 body, err := ioutil.ReadAll(resp.Body)
21+ if body != nil {
21− resp.Body.Close()
22+ defer resp.Body.Close()
23+ }
24+
22 logger.Printf("%d %s %s %s", resp.StatusCode, req.Method, req.URL, body)
25 logger.Printf("%d %s %s %s", resp.StatusCode, req.Method, req.URL, body)
26+
23 if err != nil {
27 if err != nil {
24− return nil, err
28+ return nil, fmt.Errorf("problem reading http request body: %w", err)
25 }
29 }
30+
26 if resp.StatusCode != 200 {
31 if resp.StatusCode != 200 {
27 return nil, fmt.Errorf("got %d %s", resp.StatusCode, body)
32 return nil, fmt.Errorf("got %d %s", resp.StatusCode, body)
28 }
33 }
34+
29 data, err := simplejson.NewJson(body)
35 data, err := simplejson.NewJson(body)
30 if err != nil {
36 if err != nil {
31− return nil, err
37+ return nil, fmt.Errorf("error unmarshalling json: %w", err)
32 }
38 }
33 return data, nil
39 return data, nil
34}
40}
@@ -41,10 +47,13 @@ func RequestJSON(req *http.Request, v interface{}) error {
41 return err
47 return err
42 }
48 }
43 body, err := ioutil.ReadAll(resp.Body)
49 body, err := ioutil.ReadAll(resp.Body)
50+ if body != nil {
44− resp.Body.Close()
51+ defer resp.Body.Close()
52+ }
53+
45 logger.Printf("%d %s %s %s", resp.StatusCode, req.Method, req.URL, body)
54 logger.Printf("%d %s %s %s", resp.StatusCode, req.Method, req.URL, body)
46 if err != nil {
55 if err != nil {
47− return err
56+ return fmt.Errorf("error reading body from http response: %w", err)
48 }
57 }
49 if resp.StatusCode != 200 {
58 if resp.StatusCode != 200 {
50 return fmt.Errorf("got %d %s", resp.StatusCode, body)
59 return fmt.Errorf("got %d %s", resp.StatusCode, body)
@@ -56,7 +65,7 @@ func RequestJSON(req *http.Request, v interface{}) error {
56func RequestUnparsedResponse(url string, header http.Header) (resp *http.Response, err error) {
65func RequestUnparsedResponse(url string, header http.Header) (resp *http.Response, err error) {
57 req, err := http.NewRequest("GET", url, nil)
66 req, err := http.NewRequest("GET", url, nil)
58 if err != nil {
67 if err != nil {
59− return nil, err
68+ return nil, fmt.Errorf("error performing get request: %w", err)
60 }
69 }
61 req.Header = header
70 req.Header = header
62
71
pkg/requests/requests_test.go+18−12
@@ -8,20 +8,21 @@ import (
8 "testing"
8 "testing"
9
9
10 "github.com/bitly/go-simplejson"
10 "github.com/bitly/go-simplejson"
11−
12 "github.com/stretchr/testify/assert"
11 "github.com/stretchr/testify/assert"
12+ "github.com/stretchr/testify/require"
13)
13)
14
14
15−func testBackend(responseCode int, payload string) *httptest.Server {
15+func testBackend(t *testing.T, responseCode int, payload string) *httptest.Server {
16 return httptest.NewServer(http.HandlerFunc(
16 return httptest.NewServer(http.HandlerFunc(
17 func(w http.ResponseWriter, r *http.Request) {
17 func(w http.ResponseWriter, r *http.Request) {
18 w.WriteHeader(responseCode)
18 w.WriteHeader(responseCode)
19− w.Write([]byte(payload))
19+ _, err := w.Write([]byte(payload))
20+ require.NoError(t, err)
20 }))
21 }))
21}
22}
22
23
23func TestRequest(t *testing.T) {
24func TestRequest(t *testing.T) {
24− backend := testBackend(200, "{\"foo\": \"bar\"}")
25+ backend := testBackend(t, 200, "{\"foo\": \"bar\"}")
25 defer backend.Close()
26 defer backend.Close()
26
27
27 req, _ := http.NewRequest("GET", backend.URL, nil)
28 req, _ := http.NewRequest("GET", backend.URL, nil)
@@ -35,7 +36,7 @@ func TestRequest(t *testing.T) {
35func TestRequestFailure(t *testing.T) {
36func TestRequestFailure(t *testing.T) {
36 // Create a backend to generate a test URL, then close it to cause a
37 // Create a backend to generate a test URL, then close it to cause a
37 // connection error.
38 // connection error.
38− backend := testBackend(200, "{\"foo\": \"bar\"}")
39+ backend := testBackend(t, 200, "{\"foo\": \"bar\"}")
39 backend.Close()
40 backend.Close()
40
41
41 req, err := http.NewRequest("GET", backend.URL, nil)
42 req, err := http.NewRequest("GET", backend.URL, nil)
@@ -49,7 +50,7 @@ func TestRequestFailure(t *testing.T) {
49}
50}
50
51
51func TestHttpErrorCode(t *testing.T) {
52func TestHttpErrorCode(t *testing.T) {
52− backend := testBackend(404, "{\"foo\": \"bar\"}")
53+ backend := testBackend(t, 404, "{\"foo\": \"bar\"}")
53 defer backend.Close()
54 defer backend.Close()
54
55
55 req, err := http.NewRequest("GET", backend.URL, nil)
56 req, err := http.NewRequest("GET", backend.URL, nil)
@@ -60,7 +61,7 @@ func TestHttpErrorCode(t *testing.T) {
60}
61}
61
62
62func TestJsonParsingError(t *testing.T) {
63func TestJsonParsingError(t *testing.T) {
63− backend := testBackend(200, "not well-formed JSON")
64+ backend := testBackend(t, 200, "not well-formed JSON")
64 defer backend.Close()
65 defer backend.Close()
65
66
66 req, err := http.NewRequest("GET", backend.URL, nil)
67 req, err := http.NewRequest("GET", backend.URL, nil)
@@ -77,7 +78,8 @@ func TestRequestUnparsedResponseUsingAccessTokenParameter(t *testing.T) {
77 token := r.FormValue("access_token")
78 token := r.FormValue("access_token")
78 if r.URL.Path == "/" && token == "my_token" {
79 if r.URL.Path == "/" && token == "my_token" {
79 w.WriteHeader(200)
80 w.WriteHeader(200)
80− w.Write([]byte("some payload"))
81+ _, err := w.Write([]byte("some payload"))
82+ require.NoError(t, err)
81 } else {
83 } else {
82 w.WriteHeader(403)
84 w.WriteHeader(403)
83 }
85 }
@@ -86,16 +88,17 @@ func TestRequestUnparsedResponseUsingAccessTokenParameter(t *testing.T) {
86
88
87 response, err := RequestUnparsedResponse(
89 response, err := RequestUnparsedResponse(
88 backend.URL+"?access_token=my_token", nil)
90 backend.URL+"?access_token=my_token", nil)
91+ defer response.Body.Close()
92+
89 assert.Equal(t, nil, err)
93 assert.Equal(t, nil, err)
90 assert.Equal(t, 200, response.StatusCode)
94 assert.Equal(t, 200, response.StatusCode)
91 body, err := ioutil.ReadAll(response.Body)
95 body, err := ioutil.ReadAll(response.Body)
92 assert.Equal(t, nil, err)
96 assert.Equal(t, nil, err)
93− response.Body.Close()
94 assert.Equal(t, "some payload", string(body))
97 assert.Equal(t, "some payload", string(body))
95}
98}
96
99
97func TestRequestUnparsedResponseUsingAccessTokenParameterFailedResponse(t *testing.T) {
100func TestRequestUnparsedResponseUsingAccessTokenParameterFailedResponse(t *testing.T) {
98− backend := testBackend(200, "some payload")
101+ backend := testBackend(t, 200, "some payload")
99 // Close the backend now to force a request failure.
102 // Close the backend now to force a request failure.
100 backend.Close()
103 backend.Close()
101
104
@@ -110,7 +113,8 @@ func TestRequestUnparsedResponseUsingHeaders(t *testing.T) {
110 func(w http.ResponseWriter, r *http.Request) {
113 func(w http.ResponseWriter, r *http.Request) {
111 if r.URL.Path == "/" && r.Header["Auth"][0] == "my_token" {
114 if r.URL.Path == "/" && r.Header["Auth"][0] == "my_token" {
112 w.WriteHeader(200)
115 w.WriteHeader(200)
113− w.Write([]byte("some payload"))
116+ _, err := w.Write([]byte("some payload"))
117+ require.NoError(t, err)
114 } else {
118 } else {
115 w.WriteHeader(403)
119 w.WriteHeader(403)
116 }
120 }
@@ -120,10 +124,12 @@ func TestRequestUnparsedResponseUsingHeaders(t *testing.T) {
120 headers := make(http.Header)
124 headers := make(http.Header)
121 headers.Set("Auth", "my_token")
125 headers.Set("Auth", "my_token")
122 response, err := RequestUnparsedResponse(backend.URL, headers)
126 response, err := RequestUnparsedResponse(backend.URL, headers)
127+ defer response.Body.Close()
128+
123 assert.Equal(t, nil, err)
129 assert.Equal(t, nil, err)
124 assert.Equal(t, 200, response.StatusCode)
130 assert.Equal(t, 200, response.StatusCode)
125 body, err := ioutil.ReadAll(response.Body)
131 body, err := ioutil.ReadAll(response.Body)
126 assert.Equal(t, nil, err)
132 assert.Equal(t, nil, err)
127− response.Body.Close()
133+
128 assert.Equal(t, "some payload", string(body))
134 assert.Equal(t, "some payload", string(body))
129}
135}
pkg/sessions/redis/redis_store.go+28
@@ -4,10 +4,12 @@ import (
4 "crypto/aes"
4 "crypto/aes"
5 "crypto/cipher"
5 "crypto/cipher"
6 "crypto/rand"
6 "crypto/rand"
7+ "crypto/x509"
7 "encoding/base64"
8 "encoding/base64"
8 "encoding/hex"
9 "encoding/hex"
9 "fmt"
10 "fmt"
10 "io"
11 "io"
12+ "io/ioutil"
11 "net/http"
13 "net/http"
12 "strings"
14 "strings"
13 "time"
15 "time"
@@ -17,6 +19,7 @@ import (
17 "github.com/pusher/oauth2_proxy/pkg/apis/sessions"
19 "github.com/pusher/oauth2_proxy/pkg/apis/sessions"
18 "github.com/pusher/oauth2_proxy/pkg/cookies"
20 "github.com/pusher/oauth2_proxy/pkg/cookies"
19 "github.com/pusher/oauth2_proxy/pkg/encryption"
21 "github.com/pusher/oauth2_proxy/pkg/encryption"
22+ "github.com/pusher/oauth2_proxy/pkg/logger"
20)
23)
21
24
22// TicketData is a structure representing the ticket used in server session storage
25// TicketData is a structure representing the ticket used in server session storage
@@ -64,6 +67,31 @@ func newRedisClient(opts options.RedisStoreOptions) (*redis.Client, error) {
64 return nil, fmt.Errorf("unable to parse redis url: %s", err)
67 return nil, fmt.Errorf("unable to parse redis url: %s", err)
65 }
68 }
66
69
70+ if opts.RedisInsecureTLS != false {
71+ opt.TLSConfig.InsecureSkipVerify = true
72+ }
73+
74+ if opts.RedisCAPath != "" {
75+ rootCAs, err := x509.SystemCertPool()
76+ if err != nil {
77+ logger.Printf("failed to load system cert pool for redis connection, falling back to empty cert pool")
78+ }
79+ if rootCAs == nil {
80+ rootCAs = x509.NewCertPool()
81+ }
82+ certs, err := ioutil.ReadFile(opts.RedisCAPath)
83+ if err != nil {
84+ return nil, fmt.Errorf("failed to load %q, %v", opts.RedisCAPath, err)
85+ }
86+
87+ // Append our cert to the system pool
88+ if ok := rootCAs.AppendCertsFromPEM(certs); !ok {
89+ logger.Printf("no certs appended, using system certs only")
90+ }
91+
92+ opt.TLSConfig.RootCAs = rootCAs
93+ }
94+
67 client := redis.NewClient(opt)
95 client := redis.NewClient(opt)
68 return client, nil
96 return client, nil
69}
97}
providers/github.go+83−28
@@ -7,6 +7,7 @@ import (
7 "net/http"
7 "net/http"
8 "net/url"
8 "net/url"
9 "path"
9 "path"
10+ "regexp"
10 "strconv"
11 "strconv"
11 "strings"
12 "strings"
12
13
@@ -75,8 +76,8 @@ func (p *GitHubProvider) hasOrg(accessToken string) (bool, error) {
75 pn := 1
76 pn := 1
76 for {
77 for {
77 params := url.Values{
78 params := url.Values{
78− "limit": {"200"},
79+ "per_page": {"100"},
79− "page": {strconv.Itoa(pn)},
80+ "page": {strconv.Itoa(pn)},
80 }
81 }
81
82
82 endpoint := &url.URL{
83 endpoint := &url.URL{
@@ -139,36 +140,90 @@ func (p *GitHubProvider) hasOrgAndTeam(accessToken string) (bool, error) {
139 } `json:"organization"`
140 } `json:"organization"`
140 }
141 }
141
142
142− params := url.Values{
143+ type teamsPage []struct {
143− "limit": {"200"},
144+ Name string `json:"name"`
145+ Slug string `json:"slug"`
146+ Org struct {
147+ Login string `json:"login"`
148+ } `json:"organization"`
144 }
149 }
145
150
146− endpoint := &url.URL{
151+ pn := 1
147− Scheme: p.ValidateURL.Scheme,
152+ last := 0
148− Host: p.ValidateURL.Host,
153+ for {
149− Path: path.Join(p.ValidateURL.Path, "/user/teams"),
154+ params := url.Values{
150− RawQuery: params.Encode(),
155+ "per_page": {"100"},
151− }
156+ "page": {strconv.Itoa(pn)},
152− req, _ := http.NewRequest("GET", endpoint.String(), nil)
153− req.Header.Set("Accept", "application/vnd.github.v3+json")
154− req.Header.Set("Authorization", fmt.Sprintf("token %s", accessToken))
155− resp, err := http.DefaultClient.Do(req)
156− if err != nil {
157− return false, err
158− }
157+ }
159
158
160− body, err := ioutil.ReadAll(resp.Body)
159+ endpoint := &url.URL{
161− resp.Body.Close()
160+ Scheme: p.ValidateURL.Scheme,
162− if err != nil {
161+ Host: p.ValidateURL.Host,
163− return false, err
162+ Path: path.Join(p.ValidateURL.Path, "/user/teams"),
164− }
163+ RawQuery: params.Encode(),
165− if resp.StatusCode != 200 {
166− return false, fmt.Errorf(
167− "got %d from %q %s", resp.StatusCode, endpoint.String(), body)
168− }
164+ }
169
165
166+ req, _ := http.NewRequest("GET", endpoint.String(), nil)
167+ req.Header.Set("Accept", "application/vnd.github.v3+json")
168+ req.Header.Set("Authorization", fmt.Sprintf("token %s", accessToken))
169+ resp, err := http.DefaultClient.Do(req)
170+ if err != nil {
171+ return false, err
172+ }
173+
174+ if last == 0 {
175+ // link header may not be obtained
176+ // When paging is not required and all data can be retrieved with a single call
177+
178+ // Conditions for obtaining the link header.
179+ // 1. When paging is required (Example: When the data size is 100 and the page size is 99 or less)
180+ // 2. When it exceeds the paging frame (Example: When there is only 10 records but the second page is called with a page size of 100)
181+
182+ // link header at not last page
183+ // <https://api.github.com/user/teams?page=1&per_page=100>; rel="prev", <https://api.github.com/user/teams?page=1&per_page=100>; rel="last", <https://api.github.com/user/teams?page=1&per_page=100>; rel="first"
184+ // link header at last page (doesn't exist last info)
185+ // <https://api.github.com/user/teams?page=3&per_page=10>; rel="prev", <https://api.github.com/user/teams?page=1&per_page=10>; rel="first"
186+
187+ link := resp.Header.Get("Link")
188+ rep1 := regexp.MustCompile(`(?s).*\<https://api.github.com/user/teams\?page=(.)&per_page=[0-9]+\>; rel="last".*`)
189+ i, converr := strconv.Atoi(rep1.ReplaceAllString(link, "$1"))
190+
191+ // If the last page cannot be taken from the link in the http header, the last variable remains zero
192+ if converr == nil {
193+ last = i
194+ }
195+ }
196+
197+ body, err := ioutil.ReadAll(resp.Body)
198+ if err != nil {
199+ resp.Body.Close()
200+ return false, err
201+ }
202+ resp.Body.Close()
203+
204+ if resp.StatusCode != 200 {
205+ return false, fmt.Errorf(
206+ "got %d from %q %s", resp.StatusCode, endpoint.String(), body)
207+ }
208+
209+ var tp teamsPage
170− if err := json.Unmarshal(body, &teams); err != nil {
210+ if err := json.Unmarshal(body, &tp); err != nil {
171− return false, fmt.Errorf("%s unmarshaling %s", err, body)
211+ return false, fmt.Errorf("%s unmarshaling %s", err, body)
212+ }
213+ if len(tp) == 0 {
214+ break
215+ }
216+
217+ teams = append(teams, tp...)
218+
219+ if pn == last {
220+ break
221+ }
222+ if last == 0 {
223+ break
224+ }
225+
226+ pn++
172 }
227 }
173
228
174 var hasOrg bool
229 var hasOrg bool
providers/github_test.go+1−1
@@ -32,7 +32,7 @@ func testGitHubBackend(payload []string) *httptest.Server {
32 pathToQueryMap := map[string][]string{
32 pathToQueryMap := map[string][]string{
33 "/user": {""},
33 "/user": {""},
34 "/user/emails": {""},
34 "/user/emails": {""},
35− "/user/orgs": {"limit=200&page=1", "limit=200&page=2", "limit=200&page=3"},
35+ "/user/orgs": {"page=1&per_page=100", "page=2&per_page=100", "page=3&per_page=100"},
36 }
36 }
37
37
38 return httptest.NewServer(http.HandlerFunc(
38 return httptest.NewServer(http.HandlerFunc(