git.iko.soy/typeable/oauth2-proxy
on master help sign in
Merge branch 'master' into feat/static-upstream
commit9e4a7ee84e85c4ef3c9e02041b70448278b7e0a6 authorChristian Groschupp <christian@groschupp.org> dateFri 1 Nov 2019, 17:34 (7 years ago) parentf570fb9004b743
29 files changed+322−262
.gitignore+1 .travis.yml−1 CHANGELOG.md+7−1 Makefile+1−26 README.md+6−3 configure+4 dist.sh+43−42 docs/0_index.md+1−1 docs/1_installation.md+3−3 docs/2_auth.md+2 docs/_config.yml+1 docs/configuration/configuration.md+2−1 docs/logos/OAuth2_Proxy_horizontal.png docs/logos/OAuth2_Proxy_horizontal.svg+1 docs/logos/OAuth2_Proxy_icon.png docs/logos/OAuth2_Proxy_icon.svg+1 docs/logos/OAuth2_Proxy_vertical.png docs/logos/OAuth2_Proxy_vertical.svg+1 http.go+11 http_test.go+50 main.go+3−2 options.go+2 pkg/requests/requests.go+15−6 pkg/requests/requests_test.go+18−12 providers/azure.go+65 providers/azure_test.go+22−1 validator_test.go+62−13 validator_watcher_copy_test.go−48 validator_watcher_test.go−102
.gitignore+1
@@ -16,6 +16,7 @@ release
1616# Folders
1717_obj
1818_test
19+.idea/
1920
2021# Architecture specific extensions/prefixes
2122*.[568vq]
.travis.yml−1
@@ -1,6 +1,5 @@
11language: go
22go:
3− - 1.12.x
43 - 1.13.x
54install:
65 # Fetch dependencies
CHANGELOG.md+7−1
@@ -1,10 +1,16 @@
11# Vx.x.x (Pre-release)
22
33## 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)
55- [#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)
67- [#273](https://github.com/pusher/oauth2_proxy/pull/273) Support Go 1.13 (@dio)
78- [#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
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+- [#302](https://github.com/pusher/oauth2_proxy/pull/302) Rewrite dist script (@syscll)
13+- [#304](https://github.com/pusher/oauth2_proxy/pull/304) Add new Logo! :tada: (@JoelSpeed)
814- [#265](https://github.com/pusher/oauth2_proxy/pull/265) Add upstream with static response (@cgroschupp)
915
1016# v4.0.0
Makefile+1−26
@@ -61,29 +61,4 @@ test: lint
6161
6262.PHONY: release
6363release: lint test
64− mkdir release
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)
64+ BINARY=${BINARY} VERSION=${VERSION} ./dist.sh
README.md+6−3
@@ -1,4 +1,9 @@
1−# oauth2_proxy
1+![OAuth2 Proxy](/docs/logos/OAuth2_Proxy_horizontal.svg)
2+
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)
5+[![GoDoc](https://godoc.org/github.com/pusher/oauth2_proxy?status.svg)](https://godoc.org/github.com/pusher/oauth2_proxy)
6+[![MIT licensed](https://img.shields.io/badge/license-MIT-blue.svg)](./LICENSE)
27
38A reverse proxy and static file server that provides authentication using Providers (Google, GitHub, and others)
49to validate accounts by email, domain or group.
@@ -7,8 +12,6 @@ to validate accounts by email, domain or group.
712Versions v3.0.0 and up are from this fork and will have diverged from any changes in the original fork.
813A list of changes can be seen in the [CHANGELOG](CHANGELOG.md).
914
10−[![Build Status](https://secure.travis-ci.org/pusher/oauth2_proxy.svg?branch=master)](http://travis-ci.org/pusher/oauth2_proxy)
11−
1215![Sign In Page](https://cloud.githubusercontent.com/assets/45028/4970624/7feb7dd8-6886-11e4-93e0-c9904af44ea8.png)
1316
1417## Installation
configure+4
@@ -5,6 +5,10 @@ GREEN='\033[0;32m'
55BLUE='\033[0;34m'
66NC='\033[0m'
77
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+
812declare -A tools=()
913declare -A desired=()
1014
dist.sh+43−42
@@ -1,45 +1,46 @@
1−#!/bin/bash
2−# build binary distributions for linux/amd64 and darwin/amd64
3−set -e
4−
5−DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
6−echo "working dir $DIR"
7−mkdir -p $DIR/dist
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
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
1+#!/usr/bin/env bash
2+
3+set -o errexit
4+
5+if [[ -z ${BINARY} ]] || [[ -z ${VERSION} ]]; then
6+ echo "Missing required env var: BINARY=X VERSION=X $(basename $0)"
7+ exit 1
8+fi
369
37−checksum_file="sha256sum.txt"
38−cd $DIR/dist
39−if [ -f $checksum_file ]; then
40− rm $checksum_file
10+# Check for Go version 1.13.*
11+GO_VERSION=$(go version | awk '{print $3}')
12+if [[ ! "${GO_VERSION}" =~ ^go1.13.* ]]; then
13+ echo "Go version must be >= go1.13"
14+ exit 1
4115fi
42−touch $checksum_file
43−for checksum in "${sha256sum[@]}"; do
44− echo "$checksum" >> $checksum_file
16+
17+ARCHS=(darwin-amd64 linux-amd64 linux-arm64 linux-armv6 windows-amd64)
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 ..
4546done
docs/0_index.md+1−1
@@ -5,7 +5,7 @@ permalink: /
55nav_order: 0
66---
77
8−# oauth2_proxy
8+![OAuth2 Proxy](/logos/OAuth2_Proxy_horizontal.svg)
99
1010A reverse proxy and static file server that provides authentication using Providers (Google, GitHub, and others)
1111to validate accounts by email, domain or group.
docs/1_installation.md+3−3
@@ -9,7 +9,7 @@ nav_order: 1
99
10101. Choose how to deploy:
1111
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`)
1313
1414 b. Build with `$ go get github.com/pusher/oauth2_proxy` which will put the binary in `$GOROOT/bin`
1515
@@ -18,8 +18,8 @@ nav_order: 1
1818Prebuilt 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`.
1919
2020```
21−sha256sum -c sha256sum.txt 2>&1 | grep OK
22−oauth2_proxy-3.2.0.linux-amd64: OK
21+$ sha256sum -c sha256sum.txt 2>&1 | grep OK
22+oauth2_proxy-4.0.0.linux-amd64: OK
2323```
2424
25252. [Select a Provider and Register an OAuth Application with a Provider](auth-configuration)
docs/2_auth.md+2
@@ -81,6 +81,8 @@ Note: The user is checked against the group members list on initial authenticati
8181 --client-secret=<value from step 6>
8282```
8383
84+Note: When using the Azure Auth provider with nginx and the cookie session store you may find the cookie is too large and doesn't get passed through correctly. Increasing the proxy_buffer_size in nginx or implementing the [redis session storage](configuration#redis-storage) should resolve this.
85+
8486### Facebook Auth Provider
8587
86881. Create a new FB App from <https://developers.facebook.com/>
docs/_config.yml+1
@@ -14,6 +14,7 @@
1414# You can create any custom variable you would like, and they will be accessible
1515# in the templates via {{ site.myvariable }}.
1616title: OAuth2_Proxy
17+logo: /logos/OAuth2_Proxy_horizontal.svg
1718description: >- # this means to ignore newlines until "baseurl:"
1819 OAuth2_Proxy documentation site
1920baseurl: "/oauth2_proxy" # the subpath of your site, e.g. /blog
docs/configuration/configuration.md+2−1
@@ -44,6 +44,7 @@ An example [oauth2_proxy.cfg]({{ site.gitweb }}/contrib/oauth2_proxy.cfg.example
4444| `-extra-jwt-issuers` | string | if `-skip-jwt-bearer-tokens` is set, a list of extra JWT `issuer=audience` pairs (where the issuer URL has a `.well-known/openid-configuration` or a `.well-known/jwks.json`) | |
4545| `-exclude-logging-paths` | string | comma separated list of paths to exclude from logging, eg: `"/ping,/path2"` |`""` (no paths excluded) |
4646| `-flush-interval` | duration | period between flushing response buffers when streaming responses | `"1s"` |
47+| `-force-https` | bool | enforce https redirect | `false` |
4748| `-banner` | string | custom banner string. Use `"-"` to disable default banner. | |
4849| `-footer` | string | custom footer string. Use `"-"` to disable default footer. | |
4950| `-gcp-healthchecks` | bool | will enable `/liveness_check`, `/readiness_check`, and `/` (with the proper user-agent) endpoints that will make it work well with GCP App Engine and GKE Ingresses | false |
@@ -116,7 +117,7 @@ See below for provider specific options
116117
117118### Upstreams Configuration
118119
119−`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.
120121
121122Static 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/`.
122123
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>
http.go+11
@@ -152,3 +152,14 @@ func (ln tcpKeepAliveListener) Accept() (c net.Conn, err error) {
152152 tc.SetKeepAlivePeriod(3 * time.Minute)
153153 return tc, nil
154154}
155+
156+func redirectToHTTPS(opts *Options, h http.Handler) http.Handler {
157+ return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
158+ proto := r.Header.Get("X-Forwarded-Proto")
159+ if opts.ForceHTTPS && (r.TLS == nil || (proto != "" && strings.ToLower(proto) != "https")) {
160+ http.Redirect(w, r, opts.HTTPSAddress, http.StatusPermanentRedirect)
161+ }
162+
163+ h.ServeHTTP(w, r)
164+ })
165+}
http_test.go+50
@@ -106,3 +106,53 @@ func TestGCPHealthcheckNotIngressPut(t *testing.T) {
106106
107107 assert.Equal(t, "test", rw.Body.String())
108108}
109+
110+func TestRedirectToHTTPSTrue(t *testing.T) {
111+ opts := NewOptions()
112+ opts.ForceHTTPS = true
113+ handler := func(w http.ResponseWriter, req *http.Request) {
114+ w.Write([]byte("test"))
115+ }
116+
117+ h := redirectToHTTPS(opts, http.HandlerFunc(handler))
118+ rw := httptest.NewRecorder()
119+ r, _ := http.NewRequest("GET", "/", nil)
120+ h.ServeHTTP(rw, r)
121+
122+ assert.Equal(t, http.StatusPermanentRedirect, rw.Code, "status code should be %d, got: %d", http.StatusPermanentRedirect, rw.Code)
123+}
124+
125+func TestRedirectToHTTPSFalse(t *testing.T) {
126+ opts := NewOptions()
127+ handler := func(w http.ResponseWriter, req *http.Request) {
128+ w.Write([]byte("test"))
129+ }
130+
131+ h := redirectToHTTPS(opts, http.HandlerFunc(handler))
132+ rw := httptest.NewRecorder()
133+ r, _ := http.NewRequest("GET", "/", nil)
134+ h.ServeHTTP(rw, r)
135+
136+ assert.Equal(t, http.StatusOK, rw.Code, "status code should be %d, got: %d", http.StatusOK, rw.Code)
137+}
138+
139+func TestRedirectNotWhenHTTPS(t *testing.T) {
140+ opts := NewOptions()
141+ opts.ForceHTTPS = true
142+ handler := func(w http.ResponseWriter, req *http.Request) {
143+ w.Write([]byte("test"))
144+ }
145+
146+ h := redirectToHTTPS(opts, http.HandlerFunc(handler))
147+ s := httptest.NewTLSServer(h)
148+ defer s.Close()
149+
150+ opts.HTTPSAddress = s.URL
151+ client := s.Client()
152+ res, err := client.Get(s.URL)
153+ if err != nil {
154+ t.Fatalf("request to test server failed with error: %v", err)
155+ }
156+
157+ assert.Equal(t, http.StatusOK, res.StatusCode, "status code should be %d, got: %d", http.StatusOK, res.StatusCode)
158+}
main.go+3−2
@@ -32,6 +32,7 @@ func main() {
3232
3333 flagSet.String("http-address", "127.0.0.1:4180", "[http://]<addr>:<port> or unix://<path> to listen on for HTTP clients")
3434 flagSet.String("https-address", ":443", "<addr>:<port> to listen on for HTTPS clients")
35+ flagSet.Bool("force-https", false, "force HTTPS redirect for HTTP requests")
3536 flagSet.String("tls-cert-file", "", "path to certificate file")
3637 flagSet.String("tls-key-file", "", "path to private key file")
3738 flagSet.String("redirect-url", "", "the OAuth Redirect URL. ie: \"https://internalapp.yourcompany.com/oauth2/callback\"")
@@ -185,9 +186,9 @@ func main() {
185186
186187 var handler http.Handler
187188 if opts.GCPHealthChecks {
188− handler = gcpHealthcheck(LoggingHandler(oauthproxy))
189+ handler = redirectToHTTPS(opts, gcpHealthcheck(LoggingHandler(oauthproxy)))
189190 } else {
190− handler = LoggingHandler(oauthproxy)
191+ handler = redirectToHTTPS(opts, LoggingHandler(oauthproxy))
191192 }
192193 s := &Server{
193194 Handler: handler,
options.go+2
@@ -34,6 +34,7 @@ type Options struct {
3434 ProxyWebSockets bool `flag:"proxy-websockets" cfg:"proxy_websockets" env:"OAUTH2_PROXY_PROXY_WEBSOCKETS"`
3535 HTTPAddress string `flag:"http-address" cfg:"http_address" env:"OAUTH2_PROXY_HTTP_ADDRESS"`
3636 HTTPSAddress string `flag:"https-address" cfg:"https_address" env:"OAUTH2_PROXY_HTTPS_ADDRESS"`
37+ ForceHTTPS bool `flag:"force-https" cfg:"force_https" env:"OAUTH2_PROXY_FORCE_HTTPS"`
3738 RedirectURL string `flag:"redirect-url" cfg:"redirect_url" env:"OAUTH2_PROXY_REDIRECT_URL"`
3839 ClientID string `flag:"client-id" cfg:"client_id" env:"OAUTH2_PROXY_CLIENT_ID"`
3940 ClientSecret string `flag:"client-secret" cfg:"client_secret" env:"OAUTH2_PROXY_CLIENT_SECRET"`
@@ -145,6 +146,7 @@ func NewOptions() *Options {
145146 ProxyWebSockets: true,
146147 HTTPAddress: "127.0.0.1:4180",
147148 HTTPSAddress: ":443",
149+ ForceHTTPS: false,
148150 DisplayHtpasswdForm: true,
149151 CookieOptions: options.CookieOptions{
150152 CookieName: "_oauth2_proxy",
pkg/requests/requests.go+15−6
@@ -18,17 +18,23 @@ func Request(req *http.Request) (*simplejson.Json, error) {
1818 return nil, err
1919 }
2020 body, err := ioutil.ReadAll(resp.Body)
21− resp.Body.Close()
21+ if body != nil {
22+ defer resp.Body.Close()
23+ }
24+
2225 logger.Printf("%d %s %s %s", resp.StatusCode, req.Method, req.URL, body)
26+
2327 if err != nil {
24− return nil, err
28+ return nil, fmt.Errorf("problem reading http request body: %w", err)
2529 }
30+
2631 if resp.StatusCode != 200 {
2732 return nil, fmt.Errorf("got %d %s", resp.StatusCode, body)
2833 }
34+
2935 data, err := simplejson.NewJson(body)
3036 if err != nil {
31− return nil, err
37+ return nil, fmt.Errorf("error unmarshalling json: %w", err)
3238 }
3339 return data, nil
3440}
@@ -41,10 +47,13 @@ func RequestJSON(req *http.Request, v interface{}) error {
4147 return err
4248 }
4349 body, err := ioutil.ReadAll(resp.Body)
44− resp.Body.Close()
50+ if body != nil {
51+ defer resp.Body.Close()
52+ }
53+
4554 logger.Printf("%d %s %s %s", resp.StatusCode, req.Method, req.URL, body)
4655 if err != nil {
47− return err
56+ return fmt.Errorf("error reading body from http response: %w", err)
4857 }
4958 if resp.StatusCode != 200 {
5059 return fmt.Errorf("got %d %s", resp.StatusCode, body)
@@ -56,7 +65,7 @@ func RequestJSON(req *http.Request, v interface{}) error {
5665func RequestUnparsedResponse(url string, header http.Header) (resp *http.Response, err error) {
5766 req, err := http.NewRequest("GET", url, nil)
5867 if err != nil {
59− return nil, err
68+ return nil, fmt.Errorf("error performing get request: %w", err)
6069 }
6170 req.Header = header
6271
pkg/requests/requests_test.go+18−12
@@ -8,20 +8,21 @@ import (
88 "testing"
99
1010 "github.com/bitly/go-simplejson"
11−
1211 "github.com/stretchr/testify/assert"
12+ "github.com/stretchr/testify/require"
1313)
1414
15−func testBackend(responseCode int, payload string) *httptest.Server {
15+func testBackend(t *testing.T, responseCode int, payload string) *httptest.Server {
1616 return httptest.NewServer(http.HandlerFunc(
1717 func(w http.ResponseWriter, r *http.Request) {
1818 w.WriteHeader(responseCode)
19− w.Write([]byte(payload))
19+ _, err := w.Write([]byte(payload))
20+ require.NoError(t, err)
2021 }))
2122}
2223
2324func TestRequest(t *testing.T) {
24− backend := testBackend(200, "{\"foo\": \"bar\"}")
25+ backend := testBackend(t, 200, "{\"foo\": \"bar\"}")
2526 defer backend.Close()
2627
2728 req, _ := http.NewRequest("GET", backend.URL, nil)
@@ -35,7 +36,7 @@ func TestRequest(t *testing.T) {
3536func TestRequestFailure(t *testing.T) {
3637 // Create a backend to generate a test URL, then close it to cause a
3738 // connection error.
38− backend := testBackend(200, "{\"foo\": \"bar\"}")
39+ backend := testBackend(t, 200, "{\"foo\": \"bar\"}")
3940 backend.Close()
4041
4142 req, err := http.NewRequest("GET", backend.URL, nil)
@@ -49,7 +50,7 @@ func TestRequestFailure(t *testing.T) {
4950}
5051
5152func TestHttpErrorCode(t *testing.T) {
52− backend := testBackend(404, "{\"foo\": \"bar\"}")
53+ backend := testBackend(t, 404, "{\"foo\": \"bar\"}")
5354 defer backend.Close()
5455
5556 req, err := http.NewRequest("GET", backend.URL, nil)
@@ -60,7 +61,7 @@ func TestHttpErrorCode(t *testing.T) {
6061}
6162
6263func TestJsonParsingError(t *testing.T) {
63− backend := testBackend(200, "not well-formed JSON")
64+ backend := testBackend(t, 200, "not well-formed JSON")
6465 defer backend.Close()
6566
6667 req, err := http.NewRequest("GET", backend.URL, nil)
@@ -77,7 +78,8 @@ func TestRequestUnparsedResponseUsingAccessTokenParameter(t *testing.T) {
7778 token := r.FormValue("access_token")
7879 if r.URL.Path == "/" && token == "my_token" {
7980 w.WriteHeader(200)
80− w.Write([]byte("some payload"))
81+ _, err := w.Write([]byte("some payload"))
82+ require.NoError(t, err)
8183 } else {
8284 w.WriteHeader(403)
8385 }
@@ -86,16 +88,17 @@ func TestRequestUnparsedResponseUsingAccessTokenParameter(t *testing.T) {
8688
8789 response, err := RequestUnparsedResponse(
8890 backend.URL+"?access_token=my_token", nil)
91+ defer response.Body.Close()
92+
8993 assert.Equal(t, nil, err)
9094 assert.Equal(t, 200, response.StatusCode)
9195 body, err := ioutil.ReadAll(response.Body)
9296 assert.Equal(t, nil, err)
93− response.Body.Close()
9497 assert.Equal(t, "some payload", string(body))
9598}
9699
97100func TestRequestUnparsedResponseUsingAccessTokenParameterFailedResponse(t *testing.T) {
98− backend := testBackend(200, "some payload")
101+ backend := testBackend(t, 200, "some payload")
99102 // Close the backend now to force a request failure.
100103 backend.Close()
101104
@@ -110,7 +113,8 @@ func TestRequestUnparsedResponseUsingHeaders(t *testing.T) {
110113 func(w http.ResponseWriter, r *http.Request) {
111114 if r.URL.Path == "/" && r.Header["Auth"][0] == "my_token" {
112115 w.WriteHeader(200)
113− w.Write([]byte("some payload"))
116+ _, err := w.Write([]byte("some payload"))
117+ require.NoError(t, err)
114118 } else {
115119 w.WriteHeader(403)
116120 }
@@ -120,10 +124,12 @@ func TestRequestUnparsedResponseUsingHeaders(t *testing.T) {
120124 headers := make(http.Header)
121125 headers.Set("Auth", "my_token")
122126 response, err := RequestUnparsedResponse(backend.URL, headers)
127+ defer response.Body.Close()
128+
123129 assert.Equal(t, nil, err)
124130 assert.Equal(t, 200, response.StatusCode)
125131 body, err := ioutil.ReadAll(response.Body)
126132 assert.Equal(t, nil, err)
127− response.Body.Close()
133+
128134 assert.Equal(t, "some payload", string(body))
129135}
providers/azure.go+65
@@ -1,10 +1,14 @@
11package providers
22
33import (
4+ "bytes"
5+ "encoding/json"
46 "errors"
57 "fmt"
8+ "io/ioutil"
69 "net/http"
710 "net/url"
11+ "time"
812
913 "github.com/bitly/go-simplejson"
1014 "github.com/pusher/oauth2_proxy/pkg/apis/sessions"
@@ -65,6 +69,67 @@ func (p *AzureProvider) Configure(tenant string) {
6569 }
6670}
6771
72+func (p *AzureProvider) Redeem(redirectURL, code string) (s *sessions.SessionState, err error) {
73+ if code == "" {
74+ err = errors.New("missing code")
75+ return
76+ }
77+
78+ params := url.Values{}
79+ params.Add("redirect_uri", redirectURL)
80+ params.Add("client_id", p.ClientID)
81+ params.Add("client_secret", p.ClientSecret)
82+ params.Add("code", code)
83+ params.Add("grant_type", "authorization_code")
84+ if p.ProtectedResource != nil && p.ProtectedResource.String() != "" {
85+ params.Add("resource", p.ProtectedResource.String())
86+ }
87+
88+ var req *http.Request
89+ req, err = http.NewRequest("POST", p.RedeemURL.String(), bytes.NewBufferString(params.Encode()))
90+ if err != nil {
91+ return
92+ }
93+ req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
94+
95+ var resp *http.Response
96+ resp, err = http.DefaultClient.Do(req)
97+ if err != nil {
98+ return nil, err
99+ }
100+ var body []byte
101+ body, err = ioutil.ReadAll(resp.Body)
102+ resp.Body.Close()
103+ if err != nil {
104+ return
105+ }
106+
107+ if resp.StatusCode != 200 {
108+ err = fmt.Errorf("got %d from %q %s", resp.StatusCode, p.RedeemURL.String(), body)
109+ return
110+ }
111+
112+ var jsonResponse struct {
113+ AccessToken string `json:"access_token"`
114+ RefreshToken string `json:"refresh_token"`
115+ ExpiresOn int64 `json:"expires_on,string"`
116+ IDToken string `json:"id_token"`
117+ }
118+ err = json.Unmarshal(body, &jsonResponse)
119+ if err != nil {
120+ return
121+ }
122+
123+ s = &sessions.SessionState{
124+ AccessToken: jsonResponse.AccessToken,
125+ IDToken: jsonResponse.IDToken,
126+ CreatedAt: time.Now(),
127+ ExpiresOn: time.Unix(jsonResponse.ExpiresOn, 0),
128+ RefreshToken: jsonResponse.RefreshToken,
129+ }
130+ return
131+}
132+
68133func getAzureHeader(accessToken string) http.Header {
69134 header := make(http.Header)
70135 header.Set("Authorization", fmt.Sprintf("Bearer %s", accessToken))
providers/azure_test.go+22−1
@@ -5,6 +5,7 @@ import (
55 "net/http/httptest"
66 "net/url"
77 "testing"
8+ "time"
89
910 "github.com/pusher/oauth2_proxy/pkg/apis/sessions"
1011 "github.com/stretchr/testify/assert"
@@ -20,6 +21,7 @@ func testAzureProvider(hostname string) *AzureProvider {
2021 ValidateURL: &url.URL{},
2122 ProtectedResource: &url.URL{},
2223 Scope: ""})
24+
2325 if hostname != "" {
2426 updateURL(p.Data().LoginURL, hostname)
2527 updateURL(p.Data().RedeemURL, hostname)
@@ -111,8 +113,11 @@ func testAzureBackend(payload string) *httptest.Server {
111113
112114 return httptest.NewServer(http.HandlerFunc(
113115 func(w http.ResponseWriter, r *http.Request) {
114− if r.URL.Path != path || r.URL.RawQuery != query {
116+ if (r.URL.Path != path || r.URL.RawQuery != query) && r.Method != "POST" {
115117 w.WriteHeader(404)
118+ } else if r.Method == "POST" && r.Body != nil {
119+ w.WriteHeader(200)
120+ w.Write([]byte(payload))
116121 } else if r.Header.Get("Authorization") != "Bearer imaginary_access_token" {
117122 w.WriteHeader(403)
118123 } else {
@@ -199,3 +204,19 @@ func TestAzureProviderGetEmailAddressIncorrectOtherMails(t *testing.T) {
199204 assert.Equal(t, "type assertion to string failed", err.Error())
200205 assert.Equal(t, "", email)
201206}
207+
208+func TestAzureProviderRedeemReturnsIdToken(t *testing.T) {
209+ b := testAzureBackend(`{ "id_token": "testtoken1234", "expires_on": "1136239445", "refresh_token": "refresh1234" }`)
210+ defer b.Close()
211+ timestamp, err := time.Parse(time.RFC3339, "2006-01-02T22:04:05Z")
212+ assert.Equal(t, nil, err)
213+
214+ bURL, _ := url.Parse(b.URL)
215+ p := testAzureProvider(bURL.Host)
216+ p.Data().RedeemURL.Path = "/common/oauth2/token"
217+ s, err := p.Redeem("https://localhost", "1234")
218+ assert.Equal(t, nil, err)
219+ assert.Equal(t, "testtoken1234", s.IDToken)
220+ assert.Equal(t, timestamp, s.ExpiresOn.UTC())
221+ assert.Equal(t, "refresh1234", s.RefreshToken)
222+}
validator_test.go+62−13
@@ -8,30 +8,34 @@ import (
88)
99
1010type ValidatorTest struct {
11− authEmailFile *os.File
12− done chan bool
13− updateSeen bool
11+ authEmailFileName string
12+ done chan bool
13+ updateSeen bool
1414}
1515
1616func NewValidatorTest(t *testing.T) *ValidatorTest {
1717 vt := &ValidatorTest{}
1818 var err error
19− vt.authEmailFile, err = ioutil.TempFile("", "test_auth_emails_")
19+ f, err := ioutil.TempFile("", "test_auth_emails_")
2020 if err != nil {
21− t.Fatal("failed to create temp file: " + err.Error())
21+ t.Fatalf("failed to create temp file: %v", err)
2222 }
23+ if err := f.Close(); err != nil {
24+ t.Fatalf("failed to close temp file: %v", err)
25+ }
26+ vt.authEmailFileName = f.Name()
2327 vt.done = make(chan bool, 1)
2428 return vt
2529}
2630
2731func (vt *ValidatorTest) TearDown() {
2832 vt.done <- true
29− os.Remove(vt.authEmailFile.Name())
33+ os.Remove(vt.authEmailFileName)
3034}
3135
3236func (vt *ValidatorTest) NewValidator(domains []string,
3337 updated chan<- bool) func(string) bool {
34− return newValidatorImpl(domains, vt.authEmailFile.Name(),
38+ return newValidatorImpl(domains, vt.authEmailFileName,
3539 vt.done, func() {
3640 if vt.updateSeen == false {
3741 updated <- true
@@ -40,13 +44,18 @@ func (vt *ValidatorTest) NewValidator(domains []string,
4044 })
4145}
4246
43−// This will close vt.authEmailFile.
4447func (vt *ValidatorTest) WriteEmails(t *testing.T, emails []string) {
45− defer vt.authEmailFile.Close()
46− vt.authEmailFile.WriteString(strings.Join(emails, "\n"))
47− if err := vt.authEmailFile.Close(); err != nil {
48− t.Fatal("failed to close temp file " +
49− vt.authEmailFile.Name() + ": " + err.Error())
48+ f, err := os.OpenFile(vt.authEmailFileName, os.O_WRONLY, 0600)
49+ if err != nil {
50+ t.Fatalf("failed to open auth email file: %v", err)
51+ }
52+
53+ if _, err := f.WriteString(strings.Join(emails, "\n")); err != nil {
54+ t.Fatalf("failed to write emails to auth email file: %v", err)
55+ }
56+
57+ if err := f.Close(); err != nil {
58+ t.Fatalf("failed to close auth email file: %v", err)
5059 }
5160}
5261
@@ -160,3 +169,43 @@ func TestValidatorIgnoreSpacesInAuthEmails(t *testing.T) {
160169 t.Error("email should validate")
161170 }
162171}
172+
173+func TestValidatorOverwriteEmailListDirectly(t *testing.T) {
174+ vt := NewValidatorTest(t)
175+ defer vt.TearDown()
176+
177+ vt.WriteEmails(t, []string{
178+ "xyzzy@example.com",
179+ "plugh@example.com",
180+ })
181+ domains := []string(nil)
182+ updated := make(chan bool)
183+ validator := vt.NewValidator(domains, updated)
184+
185+ if !validator("xyzzy@example.com") {
186+ t.Error("first email in list should validate")
187+ }
188+ if !validator("plugh@example.com") {
189+ t.Error("second email in list should validate")
190+ }
191+ if validator("xyzzy.plugh@example.com") {
192+ t.Error("email not in list that matches no domains " +
193+ "should not validate")
194+ }
195+
196+ vt.WriteEmails(t, []string{
197+ "xyzzy.plugh@example.com",
198+ "plugh@example.com",
199+ })
200+ <-updated
201+
202+ if validator("xyzzy@example.com") {
203+ t.Error("email removed from list should not validate")
204+ }
205+ if !validator("plugh@example.com") {
206+ t.Error("email retained in list should validate")
207+ }
208+ if !validator("xyzzy.plugh@example.com") {
209+ t.Error("email added to list should validate")
210+ }
211+}
validator_watcher_copy_test.godeleted−48
@@ -1,48 +0,0 @@
1−// +build go1.3,!plan9,!solaris,!windows
2−
3−// Turns out you can't copy over an existing file on Windows.
4−
5−package main
6−
7−import (
8− "io/ioutil"
9− "os"
10− "testing"
11−)
12−
13−func (vt *ValidatorTest) UpdateEmailFileViaCopyingOver(
14− t *testing.T, emails []string) {
15− origFile := vt.authEmailFile
16− var err error
17− vt.authEmailFile, err = ioutil.TempFile("", "test_auth_emails_")
18− if err != nil {
19− t.Fatal("failed to create temp file for copy: " + err.Error())
20− }
21− vt.WriteEmails(t, emails)
22− err = os.Rename(vt.authEmailFile.Name(), origFile.Name())
23− if err != nil {
24− t.Fatal("failed to copy over temp file: " + err.Error())
25− }
26− vt.authEmailFile = origFile
27−}
28−
29−func TestValidatorOverwriteEmailListViaCopyingOver(t *testing.T) {
30− vt := NewValidatorTest(t)
31− defer vt.TearDown()
32−
33− vt.WriteEmails(t, []string{"xyzzy@example.com"})
34− domains := []string(nil)
35− updated := make(chan bool)
36− validator := vt.NewValidator(domains, updated)
37−
38− if !validator("xyzzy@example.com") {
39− t.Error("email in list should validate")
40− }
41−
42− vt.UpdateEmailFileViaCopyingOver(t, []string{"plugh@example.com"})
43− <-updated
44−
45− if validator("xyzzy@example.com") {
46− t.Error("email removed from list should not validate")
47− }
48−}
validator_watcher_test.godeleted−102
@@ -1,102 +0,0 @@
1−// +build go1.3,!plan9,!solaris
2−
3−package main
4−
5−import (
6− "io/ioutil"
7− "os"
8− "testing"
9−)
10−
11−func (vt *ValidatorTest) UpdateEmailFile(t *testing.T, emails []string) {
12− var err error
13− vt.authEmailFile, err = os.OpenFile(
14− vt.authEmailFile.Name(), os.O_WRONLY|os.O_CREATE, 0600)
15− if err != nil {
16− t.Fatal("failed to re-open temp file for updates")
17− }
18− vt.WriteEmails(t, emails)
19−}
20−
21−func (vt *ValidatorTest) UpdateEmailFileViaRenameAndReplace(
22− t *testing.T, emails []string) {
23− origFile := vt.authEmailFile
24− var err error
25− vt.authEmailFile, err = ioutil.TempFile("", "test_auth_emails_")
26− if err != nil {
27− t.Fatal("failed to create temp file for rename and replace: " +
28− err.Error())
29− }
30− vt.WriteEmails(t, emails)
31−
32− movedName := origFile.Name() + "-moved"
33− err = os.Rename(origFile.Name(), movedName)
34− err = os.Rename(vt.authEmailFile.Name(), origFile.Name())
35− if err != nil {
36− t.Fatal("failed to rename and replace temp file: " +
37− err.Error())
38− }
39− vt.authEmailFile = origFile
40− os.Remove(movedName)
41−}
42−
43−func TestValidatorOverwriteEmailListDirectly(t *testing.T) {
44− vt := NewValidatorTest(t)
45− defer vt.TearDown()
46−
47− vt.WriteEmails(t, []string{
48− "xyzzy@example.com",
49− "plugh@example.com",
50− })
51− domains := []string(nil)
52− updated := make(chan bool)
53− validator := vt.NewValidator(domains, updated)
54−
55− if !validator("xyzzy@example.com") {
56− t.Error("first email in list should validate")
57− }
58− if !validator("plugh@example.com") {
59− t.Error("second email in list should validate")
60− }
61− if validator("xyzzy.plugh@example.com") {
62− t.Error("email not in list that matches no domains " +
63− "should not validate")
64− }
65−
66− vt.UpdateEmailFile(t, []string{
67− "xyzzy.plugh@example.com",
68− "plugh@example.com",
69− })
70− <-updated
71−
72− if validator("xyzzy@example.com") {
73− t.Error("email removed from list should not validate")
74− }
75− if !validator("plugh@example.com") {
76− t.Error("email retained in list should validate")
77− }
78− if !validator("xyzzy.plugh@example.com") {
79− t.Error("email added to list should validate")
80− }
81−}
82−
83−func TestValidatorOverwriteEmailListViaRenameAndReplace(t *testing.T) {
84− vt := NewValidatorTest(t)
85− defer vt.TearDown()
86−
87− vt.WriteEmails(t, []string{"xyzzy@example.com"})
88− domains := []string(nil)
89− updated := make(chan bool, 1)
90− validator := vt.NewValidator(domains, updated)
91−
92− if !validator("xyzzy@example.com") {
93− t.Error("email in list should validate")
94− }
95−
96− vt.UpdateEmailFileViaRenameAndReplace(t, []string{"plugh@example.com"})
97− <-updated
98−
99− if validator("xyzzy@example.com") {
100− t.Error("email removed from list should not validate")
101− }
102−}