mirror of
https://github.com/int128/kubelogin.git
synced 2026-03-02 08:50:19 +00:00
Compare commits
9 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
bfc1568057 | ||
|
|
8758d55bb3 | ||
|
|
d9be392f5a | ||
|
|
af840a519c | ||
|
|
285b3b15a8 | ||
|
|
123d7c8124 | ||
|
|
e2a6b5d4e2 | ||
|
|
ce93c739f8 | ||
|
|
dc646c88f9 |
@@ -2,7 +2,7 @@ version: 2
|
||||
jobs:
|
||||
build:
|
||||
docker:
|
||||
- image: circleci/golang:1.13.4
|
||||
- image: circleci/golang:1.14.1
|
||||
steps:
|
||||
- run: mkdir -p ~/bin
|
||||
- run: echo 'export PATH="$HOME/bin:$PATH"' >> $BASH_ENV
|
||||
|
||||
2
.github/workflows/acceptance-test.yaml
vendored
2
.github/workflows/acceptance-test.yaml
vendored
@@ -8,7 +8,7 @@ jobs:
|
||||
steps:
|
||||
- uses: actions/setup-go@v1
|
||||
with:
|
||||
go-version: 1.13
|
||||
go-version: 1.14.1
|
||||
id: go
|
||||
- uses: actions/checkout@v1
|
||||
- uses: actions/cache@v1
|
||||
|
||||
2
Makefile
2
Makefile
@@ -57,7 +57,7 @@ clean:
|
||||
ci-setup-linux-amd64:
|
||||
mkdir -p ~/bin
|
||||
# https://github.com/golangci/golangci-lint
|
||||
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b ~/bin v1.21.0
|
||||
curl -sfL https://install.goreleaser.com/github.com/golangci/golangci-lint.sh | sh -s -- -b ~/bin v1.24.0
|
||||
# https://github.com/int128/goxzst
|
||||
curl -sfL -o /tmp/goxzst.zip https://github.com/int128/goxzst/releases/download/v0.3.0/goxzst_linux_amd64.zip
|
||||
unzip /tmp/goxzst.zip -d ~/bin
|
||||
|
||||
114
README.md
114
README.md
@@ -28,12 +28,9 @@ brew install int128/kubelogin/kubelogin
|
||||
kubectl krew install oidc-login
|
||||
|
||||
# GitHub Releases
|
||||
curl -LO https://github.com/int128/kubelogin/releases/download/v1.16.0/kubelogin_linux_amd64.zip
|
||||
curl -LO https://github.com/int128/kubelogin/releases/download/v1.18.0/kubelogin_linux_amd64.zip
|
||||
unzip kubelogin_linux_amd64.zip
|
||||
ln -s kubelogin kubectl-oidc_login
|
||||
|
||||
# Docker
|
||||
docker run --rm quay.io/int128/kubelogin:v1.16.0
|
||||
```
|
||||
|
||||
You need to set up the OIDC provider, cluster role binding, Kubernetes API server and kubeconfig.
|
||||
@@ -119,21 +116,22 @@ Usage:
|
||||
kubelogin get-token [flags]
|
||||
|
||||
Flags:
|
||||
--oidc-issuer-url string Issuer URL of the provider (mandatory)
|
||||
--oidc-client-id string Client ID of the provider (mandatory)
|
||||
--oidc-client-secret string Client secret of the provider
|
||||
--oidc-extra-scope strings Scopes to request to the provider
|
||||
--certificate-authority string Path to a cert file for the certificate authority
|
||||
--certificate-authority-data string Base64 encoded data for the certificate authority
|
||||
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
|
||||
--token-cache-dir string Path to a directory for caching tokens (default "~/.kube/cache/oidc-login")
|
||||
--grant-type string The authorization grant type to use. One of (auto|authcode|authcode-keyboard|password) (default "auto")
|
||||
--listen-address strings Address to bind to the local server. If multiple addresses are given, it will try binding in order (default [127.0.0.1:8000,127.0.0.1:18000])
|
||||
--listen-port ints (Deprecated: use --listen-address)
|
||||
--skip-open-browser If true, it does not open the browser on authentication
|
||||
--username string If set, perform the resource owner password credentials grant
|
||||
--password string If set, use the password instead of asking it
|
||||
-h, --help help for get-token
|
||||
--oidc-issuer-url string Issuer URL of the provider (mandatory)
|
||||
--oidc-client-id string Client ID of the provider (mandatory)
|
||||
--oidc-client-secret string Client secret of the provider
|
||||
--oidc-extra-scope strings Scopes to request to the provider
|
||||
--certificate-authority string Path to a cert file for the certificate authority
|
||||
--certificate-authority-data string Base64 encoded data for the certificate authority
|
||||
--insecure-skip-tls-verify If true, the server's certificate will not be checked for validity. This will make your HTTPS connections insecure
|
||||
--token-cache-dir string Path to a directory for caching tokens (default "~/.kube/cache/oidc-login")
|
||||
--grant-type string The authorization grant type to use. One of (auto|authcode|authcode-keyboard|password) (default "auto")
|
||||
--listen-address strings Address to bind to the local server. If multiple addresses are given, it will try binding in order (default [127.0.0.1:8000,127.0.0.1:18000])
|
||||
--listen-port ints (Deprecated: use --listen-address)
|
||||
--skip-open-browser If true, it does not open the browser on authentication
|
||||
--oidc-auth-request-extra-params stringToString Extra query parameters to send with an authentication request (default [])
|
||||
--username string If set, perform the resource owner password credentials grant
|
||||
--password string If set, use the password instead of asking it
|
||||
-h, --help help for get-token
|
||||
|
||||
Global Flags:
|
||||
--add_dir_header If true, adds the file directory to the header
|
||||
@@ -175,39 +173,6 @@ You can use your self-signed certificate for the provider.
|
||||
You can set the following environment variables if you are behind a proxy: `HTTP_PROXY`, `HTTPS_PROXY` and `NO_PROXY`.
|
||||
See also [net/http#ProxyFromEnvironment](https://golang.org/pkg/net/http/#ProxyFromEnvironment).
|
||||
|
||||
### Docker
|
||||
|
||||
You can run [the Docker image](https://quay.io/repository/int128/kubelogin) instead of the binary.
|
||||
The kubeconfig looks like:
|
||||
|
||||
```yaml
|
||||
users:
|
||||
- name: oidc
|
||||
user:
|
||||
exec:
|
||||
apiVersion: client.authentication.k8s.io/v1beta1
|
||||
command: docker
|
||||
args:
|
||||
- run
|
||||
- --rm
|
||||
- -v
|
||||
- /tmp/.token-cache:/.token-cache
|
||||
- -p
|
||||
- 8000:8000
|
||||
- quay.io/int128/kubelogin:v1.16.0
|
||||
- get-token
|
||||
- --token-cache-dir=/.token-cache
|
||||
- --listen-address=0.0.0.0:8000
|
||||
- --oidc-issuer-url=ISSUER_URL
|
||||
- --oidc-client-id=YOUR_CLIENT_ID
|
||||
- --oidc-client-secret=YOUR_CLIENT_SECRET
|
||||
```
|
||||
|
||||
Known limitations:
|
||||
|
||||
- It cannot open the browser automatically.
|
||||
- The container port and listen port must be equal for consistency of the redirect URI.
|
||||
|
||||
### Authentication flows
|
||||
|
||||
#### Authorization code flow
|
||||
@@ -227,6 +192,12 @@ You can change the listening address.
|
||||
- --listen-address=127.0.0.1:23456
|
||||
```
|
||||
|
||||
You can add extra parameters to the authentication request.
|
||||
|
||||
```yaml
|
||||
- --oidc-auth-request-extra-params=ttl=86400
|
||||
```
|
||||
|
||||
#### Authorization code flow with keyboard interactive
|
||||
|
||||
If you cannot access the browser, instead use the authorization code flow with keyboard interactive.
|
||||
@@ -247,6 +218,12 @@ Enter code: YOUR_CODE
|
||||
Note that this flow uses the redirect URI `urn:ietf:wg:oauth:2.0:oob` and
|
||||
some OIDC providers do not support it.
|
||||
|
||||
You can add extra parameters to the authentication request.
|
||||
|
||||
```yaml
|
||||
- --oidc-auth-request-extra-params=ttl=86400
|
||||
```
|
||||
|
||||
#### Resource owner password credentials grant flow
|
||||
|
||||
Kubelogin performs the resource owner password credentials grant flow
|
||||
@@ -285,6 +262,39 @@ Username: foo
|
||||
Password:
|
||||
```
|
||||
|
||||
### Docker
|
||||
|
||||
You can run [the Docker image](https://quay.io/repository/int128/kubelogin) instead of the binary.
|
||||
The kubeconfig looks like:
|
||||
|
||||
```yaml
|
||||
users:
|
||||
- name: oidc
|
||||
user:
|
||||
exec:
|
||||
apiVersion: client.authentication.k8s.io/v1beta1
|
||||
command: docker
|
||||
args:
|
||||
- run
|
||||
- --rm
|
||||
- -v
|
||||
- /tmp/.token-cache:/.token-cache
|
||||
- -p
|
||||
- 8000:8000
|
||||
- quay.io/int128/kubelogin:v1.18.0
|
||||
- get-token
|
||||
- --token-cache-dir=/.token-cache
|
||||
- --listen-address=0.0.0.0:8000
|
||||
- --oidc-issuer-url=ISSUER_URL
|
||||
- --oidc-client-id=YOUR_CLIENT_ID
|
||||
- --oidc-client-secret=YOUR_CLIENT_SECRET
|
||||
```
|
||||
|
||||
Known limitations:
|
||||
|
||||
- It cannot open the browser automatically.
|
||||
- The container port and listen port must be equal for consistency of the redirect URI.
|
||||
|
||||
|
||||
## Related works
|
||||
|
||||
|
||||
@@ -1,7 +1,21 @@
|
||||
# kubelogin/acceptance_test
|
||||
|
||||
This is an acceptance test to verify behavior of kubelogin using a real Kubernetes cluster and OpenID Connect provider.
|
||||
It runs on [GitHub Actions](https://github.com/int128/kubelogin/actions?query=workflow%3Aacceptance-test).
|
||||
This is an acceptance test for walkthrough of the OIDC initial setup and plugin behavior using a real Kubernetes cluster and OpenID Connect provider, running on [GitHub Actions](https://github.com/int128/kubelogin/actions?query=workflow%3Aacceptance-test).
|
||||
|
||||
It is intended to verify the following points:
|
||||
|
||||
- User can set up Kubernetes OIDC authentication and this plugin.
|
||||
- User can access a cluster after login.
|
||||
|
||||
It performs the test using the following components:
|
||||
|
||||
- Kubernetes cluster (Kind)
|
||||
- OIDC provider (Dex)
|
||||
- Browser (Chrome)
|
||||
- kubectl command
|
||||
|
||||
|
||||
## How it works
|
||||
|
||||
Let's take a look at the diagram.
|
||||
|
||||
@@ -28,6 +42,32 @@ It performs the test by the following steps:
|
||||
1. Check if kubectl exited with code 0.
|
||||
|
||||
|
||||
## Run locally
|
||||
|
||||
You need to set up the following components:
|
||||
|
||||
- Docker
|
||||
- Kind
|
||||
- Chrome or Chromium
|
||||
|
||||
You need to add the following line to `/etc/hosts` so that the browser can access the Dex.
|
||||
|
||||
```
|
||||
127.0.0.1 dex-server
|
||||
```
|
||||
|
||||
Run the test.
|
||||
|
||||
```shell script
|
||||
# run the test
|
||||
make
|
||||
|
||||
# clean up
|
||||
make delete-cluster
|
||||
make delete-dex
|
||||
```
|
||||
|
||||
|
||||
## Technical consideration
|
||||
|
||||
### Network and DNS
|
||||
@@ -67,25 +107,3 @@ As a result,
|
||||
- Set the issuer URL to kubectl. See [`kubeconfig_oidc.yaml`](kubeconfig_oidc.yaml).
|
||||
- Set the issuer URL to kube-apiserver. See [`cluster.yaml`](cluster.yaml).
|
||||
- Set `BROWSER` environment variable to run [`chromelogin`](chromelogin) by `xdg-open`.
|
||||
|
||||
|
||||
## Run locally
|
||||
|
||||
You need to set up Docker and Kind.
|
||||
|
||||
You need to add the following line to `/etc/hosts`:
|
||||
|
||||
```
|
||||
127.0.0.1 dex-server
|
||||
```
|
||||
|
||||
Run:
|
||||
|
||||
```shell script
|
||||
# run the test
|
||||
make
|
||||
|
||||
# clean up
|
||||
make delete-cluster
|
||||
make delete-dex
|
||||
```
|
||||
|
||||
10
go.mod
10
go.mod
@@ -6,21 +6,21 @@ require (
|
||||
github.com/chromedp/chromedp v0.5.3
|
||||
github.com/coreos/go-oidc v2.2.1+incompatible
|
||||
github.com/dgrijalva/jwt-go v3.2.0+incompatible
|
||||
github.com/golang/mock v1.4.0
|
||||
github.com/golang/mock v1.4.3
|
||||
github.com/google/go-cmp v0.4.0
|
||||
github.com/google/wire v0.4.0
|
||||
github.com/int128/oauth2cli v1.9.0
|
||||
github.com/int128/oauth2cli v1.10.0
|
||||
github.com/pkg/browser v0.0.0-20180916011732-0a3d74bf9ce4
|
||||
github.com/pquerna/cachecontrol v0.0.0-20180517163645-1555304b9b35 // indirect
|
||||
github.com/spf13/cobra v0.0.6
|
||||
github.com/spf13/pflag v1.0.5
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586
|
||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975
|
||||
golang.org/x/oauth2 v0.0.0-20200107190931-bf48bf16ab8d
|
||||
golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e
|
||||
golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543
|
||||
gopkg.in/square/go-jose.v2 v2.3.1 // indirect
|
||||
gopkg.in/yaml.v2 v2.2.8
|
||||
k8s.io/apimachinery v0.17.3
|
||||
k8s.io/client-go v0.17.3
|
||||
k8s.io/apimachinery v0.17.4
|
||||
k8s.io/client-go v0.17.4
|
||||
k8s.io/klog v1.0.0
|
||||
)
|
||||
|
||||
24
go.sum
24
go.sum
@@ -70,8 +70,8 @@ github.com/golang/groupcache v0.0.0-20160516000752-02826c3e7903/go.mod h1:cIg4er
|
||||
github.com/golang/groupcache v0.0.0-20190129154638-5b532d6fd5ef/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc=
|
||||
github.com/golang/mock v1.1.1/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.2.0/go.mod h1:oTYuIxOrZwtPieC+H1uAHpcLFnEyAGVDL/k47Jfbm0A=
|
||||
github.com/golang/mock v1.4.0 h1:Rd1kQnQu0Hq3qvJppYSG0HtP+f5LPPUiDswTLiEegLg=
|
||||
github.com/golang/mock v1.4.0/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/mock v1.4.3 h1:GV+pQPG/EUUbkh47niozDcADz6go/dUwhVzdUQHIVRw=
|
||||
github.com/golang/mock v1.4.3/go.mod h1:UOMv5ysSaYNkG+OFQykRIcU/QvvxJf3p21QfJ2Bt3cw=
|
||||
github.com/golang/protobuf v0.0.0-20161109072736-4bd1920723d7/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
github.com/golang/protobuf v1.2.0 h1:P3YflyNX/ehuJFLhxviNdFxQPkGK5cDcApsge1SqnvM=
|
||||
github.com/golang/protobuf v1.2.0/go.mod h1:6lQm79b+lXiMfvg/cZm0SGofjICqVBUtrP5yJMmIC1U=
|
||||
@@ -112,8 +112,8 @@ github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NH
|
||||
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
|
||||
github.com/int128/listener v1.0.0 h1:a9H3m4jbXgXpxJUK3fxWrh37Iic/UU/kYOGE0WtjbbI=
|
||||
github.com/int128/listener v1.0.0/go.mod h1:sho0rrH7mNRRZH4hYOYx+xwRDGmtRndaUiu2z9iumes=
|
||||
github.com/int128/oauth2cli v1.9.0 h1:nCe8l0QLF5yvh4Ef4dxs7jnbyzDp8+YWTV9BX76YdCI=
|
||||
github.com/int128/oauth2cli v1.9.0/go.mod h1:m5tJro14TyPDlIg+RIlGVnavkm1kTooROlcFlnhteVo=
|
||||
github.com/int128/oauth2cli v1.10.0 h1:ypYxwjuBblyTRTdZTFQLgA08gYhVwsdlBEvuoNs6Xsw=
|
||||
github.com/int128/oauth2cli v1.10.0/go.mod h1:m5tJro14TyPDlIg+RIlGVnavkm1kTooROlcFlnhteVo=
|
||||
github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo=
|
||||
github.com/json-iterator/go v0.0.0-20180612202835-f2b4162afba3/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU=
|
||||
github.com/json-iterator/go v1.1.8 h1:QiWkFLKq0T7mpzwOTu6BzNDbfTE8OLrYhVKYMLF46Ok=
|
||||
@@ -213,8 +213,8 @@ golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnf
|
||||
golang.org/x/crypto v0.0.0-20190211182817-74369b46fc67/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2 h1:VklqNMn3ovrHsnt90PveolxSbWFaJdECFbxSq0Mqo2M=
|
||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586 h1:7KByu05hhLed2MO29w7p1XfZvZ13m8mub3shuVftRs0=
|
||||
golang.org/x/crypto v0.0.0-20190820162420-60c769a6c586/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
|
||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975 h1:/Tl7pH94bvbAAHBdZJT947M/+gp0+CqQXDtMRC0fseo=
|
||||
golang.org/x/crypto v0.0.0-20200220183623-bac4c82f6975/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto=
|
||||
golang.org/x/exp v0.0.0-20190121172915-509febef88a4/go.mod h1:CJ0aWSM057203Lf6IL+f9T1iT9GByDxfZKAQTCR3kQA=
|
||||
golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE=
|
||||
golang.org/x/lint v0.0.0-20190227174305-5b3e6a55c961/go.mod h1:wehouNa3lNwaWXcvxsM5YxQ5yQlVC4a0KAMCusXpPoU=
|
||||
@@ -316,12 +316,12 @@ gopkg.in/yaml.v2 v2.2.8 h1:obN1ZagJSUGI0Ek/LBmuj4SNLPfIny3KsKFopxRdj10=
|
||||
gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
|
||||
honnef.co/go/tools v0.0.0-20190102054323-c2f93a96b099/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
honnef.co/go/tools v0.0.0-20190106161140-3f1c8253044a/go.mod h1:rf3lG4BRIbNafJWhAfAdb/ePZxsR/4RtNHQocxwk9r4=
|
||||
k8s.io/api v0.17.3 h1:XAm3PZp3wnEdzekNkcmj/9Y1zdmQYJ1I4GKSBBZ8aG0=
|
||||
k8s.io/api v0.17.3/go.mod h1:YZ0OTkuw7ipbe305fMpIdf3GLXZKRigjtZaV5gzC2J0=
|
||||
k8s.io/apimachinery v0.17.3 h1:f+uZV6rm4/tHE7xXgLyToprg6xWairaClGVkm2t8omg=
|
||||
k8s.io/apimachinery v0.17.3/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g=
|
||||
k8s.io/client-go v0.17.3 h1:deUna1Ksx05XeESH6XGCyONNFfiQmDdqeqUvicvP6nU=
|
||||
k8s.io/client-go v0.17.3/go.mod h1:cLXlTMtWHkuK4tD360KpWz2gG2KtdWEr/OT02i3emRQ=
|
||||
k8s.io/api v0.17.4 h1:HbwOhDapkguO8lTAE8OX3hdF2qp8GtpC9CW/MQATXXo=
|
||||
k8s.io/api v0.17.4/go.mod h1:5qxx6vjmwUVG2nHQTKGlLts8Tbok8PzHl4vHtVFuZCA=
|
||||
k8s.io/apimachinery v0.17.4 h1:UzM+38cPUJnzqSQ+E1PY4YxMHIzQyCg29LOoGfo79Zw=
|
||||
k8s.io/apimachinery v0.17.4/go.mod h1:gxLnyZcGNdZTCLnq3fgzyg2A5BVCHTNDFrw8AmuJ+0g=
|
||||
k8s.io/client-go v0.17.4 h1:VVdVbpTY70jiNHS1eiFkUt7ZIJX3txd29nDxxXH4en8=
|
||||
k8s.io/client-go v0.17.4/go.mod h1:ouF6o5pz3is8qU0/qYL2RnoxOPqgfuidYLowytyLJmc=
|
||||
k8s.io/gengo v0.0.0-20190128074634-0689ccc1d7d6/go.mod h1:ezvh/TsK7cY6rbqRK0oQQ8IAqLxYwwyPxAX1Pzy0ii0=
|
||||
k8s.io/klog v0.0.0-20181102134211-b9b56d5dfc92/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
k8s.io/klog v0.3.0/go.mod h1:Gq+BEi5rUBO/HRz0bTSXDUcqjScdoY3a9IHpCEIOOfk=
|
||||
|
||||
@@ -97,7 +97,7 @@ func testCredentialPlugin(t *testing.T, tc credentialPluginTestCase) {
|
||||
serverURL, server := localserver.Start(t, idp.NewHandler(t, provider), tc.Keys)
|
||||
defer server.Shutdown(t, ctx)
|
||||
var idToken string
|
||||
setupAuthCodeFlow(t, provider, serverURL, "openid", &idToken)
|
||||
setupAuthCodeFlow(t, provider, serverURL, "openid", nil, &idToken)
|
||||
writerMock := newCredentialPluginWriterMock(t, ctrl, &idToken)
|
||||
browserMock := newBrowserMock(ctx, t, ctrl, tc.Keys)
|
||||
|
||||
@@ -214,7 +214,7 @@ func testCredentialPlugin(t *testing.T, tc credentialPluginTestCase) {
|
||||
validIDToken := newIDToken(t, serverURL, "YOUR_NONCE", tokenExpiryFuture)
|
||||
expiredIDToken := newIDToken(t, serverURL, "YOUR_NONCE", tokenExpiryPast)
|
||||
|
||||
setupAuthCodeFlow(t, provider, serverURL, "openid", &validIDToken)
|
||||
setupAuthCodeFlow(t, provider, serverURL, "openid", nil, &validIDToken)
|
||||
provider.EXPECT().Refresh("EXPIRED_REFRESH_TOKEN").
|
||||
Return(nil, &idp.ErrorResponse{Code: "invalid_request", Description: "token has expired"}).
|
||||
MaxTimes(2) // package oauth2 will retry refreshing the token
|
||||
@@ -249,7 +249,7 @@ func testCredentialPlugin(t *testing.T, tc credentialPluginTestCase) {
|
||||
serverURL, server := localserver.Start(t, idp.NewHandler(t, provider), tc.Keys)
|
||||
defer server.Shutdown(t, ctx)
|
||||
var idToken string
|
||||
setupAuthCodeFlow(t, provider, serverURL, "email profile openid", &idToken)
|
||||
setupAuthCodeFlow(t, provider, serverURL, "email profile openid", nil, &idToken)
|
||||
writerMock := newCredentialPluginWriterMock(t, ctrl, &idToken)
|
||||
browserMock := newBrowserMock(ctx, t, ctrl, tc.Keys)
|
||||
|
||||
@@ -262,6 +262,34 @@ func testCredentialPlugin(t *testing.T, tc credentialPluginTestCase) {
|
||||
args = append(args, tc.ExtraArgs...)
|
||||
runGetTokenCmd(t, ctx, browserMock, writerMock, args)
|
||||
})
|
||||
|
||||
t.Run("ExtraParams", func(t *testing.T) {
|
||||
t.Parallel()
|
||||
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
|
||||
defer cancel()
|
||||
ctrl := gomock.NewController(t)
|
||||
defer ctrl.Finish()
|
||||
|
||||
provider := mock_idp.NewMockProvider(ctrl)
|
||||
serverURL, server := localserver.Start(t, idp.NewHandler(t, provider), tc.Keys)
|
||||
defer server.Shutdown(t, ctx)
|
||||
var idToken string
|
||||
setupAuthCodeFlow(t, provider, serverURL, "openid", map[string]string{
|
||||
"ttl": "86400",
|
||||
"reauth": "false",
|
||||
}, &idToken)
|
||||
writerMock := newCredentialPluginWriterMock(t, ctrl, &idToken)
|
||||
browserMock := newBrowserMock(ctx, t, ctrl, tc.Keys)
|
||||
|
||||
args := []string{
|
||||
"--oidc-issuer-url", serverURL,
|
||||
"--oidc-client-id", "kubernetes",
|
||||
"--oidc-auth-request-extra-params", "ttl=86400",
|
||||
"--oidc-auth-request-extra-params", "reauth=false",
|
||||
}
|
||||
args = append(args, tc.ExtraArgs...)
|
||||
runGetTokenCmd(t, ctx, browserMock, writerMock, args)
|
||||
})
|
||||
}
|
||||
|
||||
func newCredentialPluginWriterMock(t *testing.T, ctrl *gomock.Controller, idToken *string) *mock_credentialpluginwriter.MockInterface {
|
||||
|
||||
@@ -33,13 +33,22 @@ func newIDToken(t *testing.T, issuer, nonce string, expiry time.Time) string {
|
||||
})
|
||||
}
|
||||
|
||||
func setupAuthCodeFlow(t *testing.T, provider *mock_idp.MockProvider, serverURL, scope string, idToken *string) {
|
||||
func setupAuthCodeFlow(t *testing.T, provider *mock_idp.MockProvider, serverURL, scope string, extraParams map[string]string, idToken *string) {
|
||||
var nonce string
|
||||
provider.EXPECT().Discovery().Return(idp.NewDiscoveryResponse(serverURL))
|
||||
provider.EXPECT().GetCertificates().Return(idp.NewCertificatesResponse(jwt.PrivateKey))
|
||||
provider.EXPECT().AuthenticateCode(scope, gomock.Any()).
|
||||
DoAndReturn(func(_, gotNonce string) (string, error) {
|
||||
nonce = gotNonce
|
||||
provider.EXPECT().AuthenticateCode(gomock.Any()).
|
||||
DoAndReturn(func(req idp.AuthenticationRequest) (string, error) {
|
||||
if req.Scope != scope {
|
||||
t.Errorf("scope wants `%s` but was `%s`", scope, req.Scope)
|
||||
}
|
||||
for k, v := range extraParams {
|
||||
got := req.RawQuery.Get(k)
|
||||
if got != v {
|
||||
t.Errorf("parameter %s wants `%s` but was `%s`", k, v, got)
|
||||
}
|
||||
}
|
||||
nonce = req.Nonce
|
||||
return "YOUR_AUTH_CODE", nil
|
||||
})
|
||||
provider.EXPECT().Exchange("YOUR_AUTH_CODE").
|
||||
|
||||
@@ -74,8 +74,14 @@ func (h *Handler) serveHTTP(w http.ResponseWriter, r *http.Request) error {
|
||||
// 3.1.2.1. Authentication Request
|
||||
// https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest
|
||||
q := r.URL.Query()
|
||||
redirectURI, scope, state, nonce := q.Get("redirect_uri"), q.Get("scope"), q.Get("state"), q.Get("nonce")
|
||||
code, err := h.provider.AuthenticateCode(scope, nonce)
|
||||
redirectURI, state := q.Get("redirect_uri"), q.Get("state")
|
||||
code, err := h.provider.AuthenticateCode(AuthenticationRequest{
|
||||
RedirectURI: redirectURI,
|
||||
State: state,
|
||||
Scope: q.Get("scope"),
|
||||
Nonce: q.Get("nonce"),
|
||||
RawQuery: q,
|
||||
})
|
||||
if err != nil {
|
||||
return xerrors.Errorf("authentication error: %w", err)
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import (
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"math/big"
|
||||
"net/url"
|
||||
)
|
||||
|
||||
// Provider provides discovery and authentication methods.
|
||||
@@ -17,7 +18,7 @@ import (
|
||||
type Provider interface {
|
||||
Discovery() *DiscoveryResponse
|
||||
GetCertificates() *CertificatesResponse
|
||||
AuthenticateCode(scope, nonce string) (code string, err error)
|
||||
AuthenticateCode(req AuthenticationRequest) (code string, err error)
|
||||
Exchange(code string) (*TokenResponse, error)
|
||||
AuthenticatePassword(username, password, scope string) (*TokenResponse, error)
|
||||
Refresh(refreshToken string) (*TokenResponse, error)
|
||||
@@ -89,6 +90,14 @@ func NewCertificatesResponse(idTokenKeyPair *rsa.PrivateKey) *CertificatesRespon
|
||||
}
|
||||
}
|
||||
|
||||
type AuthenticationRequest struct {
|
||||
RedirectURI string
|
||||
State string
|
||||
Scope string // space separated string
|
||||
Nonce string
|
||||
RawQuery url.Values
|
||||
}
|
||||
|
||||
type TokenResponse struct {
|
||||
AccessToken string `json:"access_token"`
|
||||
TokenType string `json:"token_type"`
|
||||
|
||||
@@ -34,18 +34,18 @@ func (m *MockProvider) EXPECT() *MockProviderMockRecorder {
|
||||
}
|
||||
|
||||
// AuthenticateCode mocks base method
|
||||
func (m *MockProvider) AuthenticateCode(arg0, arg1 string) (string, error) {
|
||||
func (m *MockProvider) AuthenticateCode(arg0 idp.AuthenticationRequest) (string, error) {
|
||||
m.ctrl.T.Helper()
|
||||
ret := m.ctrl.Call(m, "AuthenticateCode", arg0, arg1)
|
||||
ret := m.ctrl.Call(m, "AuthenticateCode", arg0)
|
||||
ret0, _ := ret[0].(string)
|
||||
ret1, _ := ret[1].(error)
|
||||
return ret0, ret1
|
||||
}
|
||||
|
||||
// AuthenticateCode indicates an expected call of AuthenticateCode
|
||||
func (mr *MockProviderMockRecorder) AuthenticateCode(arg0, arg1 interface{}) *gomock.Call {
|
||||
func (mr *MockProviderMockRecorder) AuthenticateCode(arg0 interface{}) *gomock.Call {
|
||||
mr.mock.ctrl.T.Helper()
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthenticateCode", reflect.TypeOf((*MockProvider)(nil).AuthenticateCode), arg0, arg1)
|
||||
return mr.mock.ctrl.RecordCallWithMethodType(mr.mock, "AuthenticateCode", reflect.TypeOf((*MockProvider)(nil).AuthenticateCode), arg0)
|
||||
}
|
||||
|
||||
// AuthenticatePassword mocks base method
|
||||
|
||||
@@ -50,7 +50,7 @@ func testStandalone(t *testing.T, idpTLS keys.Keys) {
|
||||
defer server.Shutdown(t, ctx)
|
||||
browserMock := newBrowserMock(ctx, t, ctrl, idpTLS)
|
||||
var idToken string
|
||||
setupAuthCodeFlow(t, provider, serverURL, "openid", &idToken)
|
||||
setupAuthCodeFlow(t, provider, serverURL, "openid", nil, &idToken)
|
||||
kubeConfigFilename := kubeconfig.Create(t, &kubeconfig.Values{
|
||||
Issuer: serverURL,
|
||||
IDPCertificateAuthority: idpTLS.CACertPath,
|
||||
@@ -175,7 +175,7 @@ func testStandalone(t *testing.T, idpTLS keys.Keys) {
|
||||
serverURL, server := localserver.Start(t, idp.NewHandler(t, provider), idpTLS)
|
||||
defer server.Shutdown(t, ctx)
|
||||
var idToken string
|
||||
setupAuthCodeFlow(t, provider, serverURL, "openid", &idToken)
|
||||
setupAuthCodeFlow(t, provider, serverURL, "openid", nil, &idToken)
|
||||
provider.EXPECT().Refresh("EXPIRED_REFRESH_TOKEN").
|
||||
Return(nil, &idp.ErrorResponse{Code: "invalid_request", Description: "token has expired"}).
|
||||
MaxTimes(2) // package oauth2 will retry refreshing the token
|
||||
@@ -210,7 +210,7 @@ func testStandalone(t *testing.T, idpTLS keys.Keys) {
|
||||
serverURL, server := localserver.Start(t, idp.NewHandler(t, provider), idpTLS)
|
||||
defer server.Shutdown(t, ctx)
|
||||
var idToken string
|
||||
setupAuthCodeFlow(t, provider, serverURL, "openid", &idToken)
|
||||
setupAuthCodeFlow(t, provider, serverURL, "openid", nil, &idToken)
|
||||
browserMock := newBrowserMock(ctx, t, ctrl, idpTLS)
|
||||
|
||||
kubeConfigFilename := kubeconfig.Create(t, &kubeconfig.Values{
|
||||
@@ -242,7 +242,7 @@ func testStandalone(t *testing.T, idpTLS keys.Keys) {
|
||||
serverURL, server := localserver.Start(t, idp.NewHandler(t, provider), idpTLS)
|
||||
defer server.Shutdown(t, ctx)
|
||||
var idToken string
|
||||
setupAuthCodeFlow(t, provider, serverURL, "profile groups openid", &idToken)
|
||||
setupAuthCodeFlow(t, provider, serverURL, "profile groups openid", nil, &idToken)
|
||||
browserMock := newBrowserMock(ctx, t, ctrl, idpTLS)
|
||||
|
||||
kubeConfigFilename := kubeconfig.Create(t, &kubeconfig.Values{
|
||||
|
||||
@@ -212,6 +212,8 @@ func TestCmd_Run(t *testing.T) {
|
||||
"--listen-address", "127.0.0.1:10080",
|
||||
"--listen-address", "127.0.0.1:20080",
|
||||
"--skip-open-browser",
|
||||
"--oidc-auth-request-extra-params", "ttl=86400",
|
||||
"--oidc-auth-request-extra-params", "reauth=true",
|
||||
"--username", "USER",
|
||||
"--password", "PASS",
|
||||
},
|
||||
@@ -226,8 +228,9 @@ func TestCmd_Run(t *testing.T) {
|
||||
SkipTLSVerify: true,
|
||||
GrantOptionSet: authentication.GrantOptionSet{
|
||||
AuthCodeOption: &authentication.AuthCodeOption{
|
||||
BindAddress: []string{"127.0.0.1:10080", "127.0.0.1:20080"},
|
||||
SkipOpenBrowser: true,
|
||||
BindAddress: []string{"127.0.0.1:10080", "127.0.0.1:20080"},
|
||||
SkipOpenBrowser: true,
|
||||
AuthRequestExtraParams: map[string]string{"ttl": "86400", "reauth": "true"},
|
||||
},
|
||||
},
|
||||
},
|
||||
@@ -238,13 +241,16 @@ func TestCmd_Run(t *testing.T) {
|
||||
"--oidc-issuer-url", "https://issuer.example.com",
|
||||
"--oidc-client-id", "YOUR_CLIENT_ID",
|
||||
"--grant-type", "authcode-keyboard",
|
||||
"--oidc-auth-request-extra-params", "ttl=86400",
|
||||
},
|
||||
in: credentialplugin.Input{
|
||||
TokenCacheDir: defaultTokenCacheDir,
|
||||
IssuerURL: "https://issuer.example.com",
|
||||
ClientID: "YOUR_CLIENT_ID",
|
||||
GrantOptionSet: authentication.GrantOptionSet{
|
||||
AuthCodeKeyboardOption: &authentication.AuthCodeKeyboardOption{},
|
||||
AuthCodeKeyboardOption: &authentication.AuthCodeKeyboardOption{
|
||||
AuthRequestExtraParams: map[string]string{"ttl": "86400"},
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
|
||||
@@ -44,12 +44,13 @@ func (o *rootOptions) register(f *pflag.FlagSet) {
|
||||
}
|
||||
|
||||
type authenticationOptions struct {
|
||||
GrantType string
|
||||
ListenAddress []string
|
||||
ListenPort []int // deprecated
|
||||
SkipOpenBrowser bool
|
||||
Username string
|
||||
Password string
|
||||
GrantType string
|
||||
ListenAddress []string
|
||||
ListenPort []int // deprecated
|
||||
SkipOpenBrowser bool
|
||||
AuthRequestExtraParams map[string]string
|
||||
Username string
|
||||
Password string
|
||||
}
|
||||
|
||||
// determineListenAddress returns the addresses from the flags.
|
||||
@@ -80,6 +81,7 @@ func (o *authenticationOptions) register(f *pflag.FlagSet) {
|
||||
//TODO: remove the deprecated flag
|
||||
f.IntSliceVar(&o.ListenPort, "listen-port", nil, "(Deprecated: use --listen-address)")
|
||||
f.BoolVar(&o.SkipOpenBrowser, "skip-open-browser", false, "If true, it does not open the browser on authentication")
|
||||
f.StringToStringVar(&o.AuthRequestExtraParams, "oidc-auth-request-extra-params", nil, "Extra query parameters to send with an authentication request")
|
||||
f.StringVar(&o.Username, "username", "", "If set, perform the resource owner password credentials grant")
|
||||
f.StringVar(&o.Password, "password", "", "If set, use the password instead of asking it")
|
||||
}
|
||||
@@ -88,11 +90,14 @@ func (o *authenticationOptions) grantOptionSet() (s authentication.GrantOptionSe
|
||||
switch {
|
||||
case o.GrantType == "authcode" || (o.GrantType == "auto" && o.Username == ""):
|
||||
s.AuthCodeOption = &authentication.AuthCodeOption{
|
||||
BindAddress: o.determineListenAddress(),
|
||||
SkipOpenBrowser: o.SkipOpenBrowser,
|
||||
BindAddress: o.determineListenAddress(),
|
||||
SkipOpenBrowser: o.SkipOpenBrowser,
|
||||
AuthRequestExtraParams: o.AuthRequestExtraParams,
|
||||
}
|
||||
case o.GrantType == "authcode-keyboard":
|
||||
s.AuthCodeKeyboardOption = &authentication.AuthCodeKeyboardOption{}
|
||||
s.AuthCodeKeyboardOption = &authentication.AuthCodeKeyboardOption{
|
||||
AuthRequestExtraParams: o.AuthRequestExtraParams,
|
||||
}
|
||||
case o.GrantType == "password" || (o.GrantType == "auto" && o.Username != ""):
|
||||
s.ROPCOption = &authentication.ROPCOption{
|
||||
Username: o.Username,
|
||||
|
||||
@@ -29,11 +29,12 @@ type Interface interface {
|
||||
}
|
||||
|
||||
type AuthCodeURLInput struct {
|
||||
State string
|
||||
Nonce string
|
||||
CodeChallenge string
|
||||
CodeChallengeMethod string
|
||||
RedirectURI string
|
||||
State string
|
||||
Nonce string
|
||||
CodeChallenge string
|
||||
CodeChallengeMethod string
|
||||
RedirectURI string
|
||||
AuthRequestExtraParams map[string]string
|
||||
}
|
||||
|
||||
type ExchangeAuthCodeInput struct {
|
||||
@@ -44,12 +45,13 @@ type ExchangeAuthCodeInput struct {
|
||||
}
|
||||
|
||||
type GetTokenByAuthCodeInput struct {
|
||||
BindAddress []string
|
||||
State string
|
||||
Nonce string
|
||||
CodeChallenge string
|
||||
CodeChallengeMethod string
|
||||
CodeVerifier string
|
||||
BindAddress []string
|
||||
State string
|
||||
Nonce string
|
||||
CodeChallenge string
|
||||
CodeChallengeMethod string
|
||||
CodeVerifier string
|
||||
AuthRequestExtraParams map[string]string
|
||||
}
|
||||
|
||||
// TokenSet represents an output DTO of
|
||||
@@ -92,6 +94,10 @@ func (c *client) GetTokenByAuthCode(ctx context.Context, in GetTokenByAuthCodeIn
|
||||
LocalServerBindAddress: in.BindAddress,
|
||||
LocalServerReadyChan: localServerReadyChan,
|
||||
}
|
||||
for key, value := range in.AuthRequestExtraParams {
|
||||
config.AuthCodeOptions = append(config.AuthCodeOptions, oauth2.SetAuthURLParam(key, value))
|
||||
}
|
||||
|
||||
token, err := oauth2cli.GetToken(ctx, config)
|
||||
if err != nil {
|
||||
return nil, xerrors.Errorf("oauth2 error: %w", err)
|
||||
@@ -103,12 +109,16 @@ func (c *client) GetTokenByAuthCode(ctx context.Context, in GetTokenByAuthCodeIn
|
||||
func (c *client) GetAuthCodeURL(in AuthCodeURLInput) string {
|
||||
cfg := c.oauth2Config
|
||||
cfg.RedirectURL = in.RedirectURI
|
||||
return cfg.AuthCodeURL(in.State,
|
||||
opts := []oauth2.AuthCodeOption{
|
||||
oauth2.AccessTypeOffline,
|
||||
oidc.Nonce(in.Nonce),
|
||||
oauth2.SetAuthURLParam("code_challenge", in.CodeChallenge),
|
||||
oauth2.SetAuthURLParam("code_challenge_method", in.CodeChallengeMethod),
|
||||
)
|
||||
}
|
||||
for key, value := range in.AuthRequestExtraParams {
|
||||
opts = append(opts, oauth2.SetAuthURLParam(key, value))
|
||||
}
|
||||
return cfg.AuthCodeURL(in.State, opts...)
|
||||
}
|
||||
|
||||
// ExchangeAuthCode exchanges the authorization code and token.
|
||||
|
||||
@@ -32,12 +32,13 @@ func (u *AuthCode) Do(ctx context.Context, o *AuthCodeOption, client oidcclient.
|
||||
return nil, xerrors.Errorf("could not generate PKCE parameters: %w", err)
|
||||
}
|
||||
in := oidcclient.GetTokenByAuthCodeInput{
|
||||
BindAddress: o.BindAddress,
|
||||
State: state,
|
||||
Nonce: nonce,
|
||||
CodeChallenge: p.CodeChallenge,
|
||||
CodeChallengeMethod: p.CodeChallengeMethod,
|
||||
CodeVerifier: p.CodeVerifier,
|
||||
BindAddress: o.BindAddress,
|
||||
State: state,
|
||||
Nonce: nonce,
|
||||
CodeChallenge: p.CodeChallenge,
|
||||
CodeChallengeMethod: p.CodeChallengeMethod,
|
||||
CodeVerifier: p.CodeVerifier,
|
||||
AuthRequestExtraParams: o.AuthRequestExtraParams,
|
||||
}
|
||||
readyChan := make(chan string, 1)
|
||||
defer close(readyChan)
|
||||
|
||||
@@ -19,7 +19,7 @@ type AuthCodeKeyboard struct {
|
||||
Logger logger.Interface
|
||||
}
|
||||
|
||||
func (u *AuthCodeKeyboard) Do(ctx context.Context, _ *AuthCodeKeyboardOption, client oidcclient.Interface) (*Output, error) {
|
||||
func (u *AuthCodeKeyboard) Do(ctx context.Context, o *AuthCodeKeyboardOption, client oidcclient.Interface) (*Output, error) {
|
||||
u.Logger.V(1).Infof("performing the authorization code flow with keyboard interactive")
|
||||
state, err := oidc.NewState()
|
||||
if err != nil {
|
||||
@@ -34,11 +34,12 @@ func (u *AuthCodeKeyboard) Do(ctx context.Context, _ *AuthCodeKeyboardOption, cl
|
||||
return nil, xerrors.Errorf("could not generate PKCE parameters: %w", err)
|
||||
}
|
||||
authCodeURL := client.GetAuthCodeURL(oidcclient.AuthCodeURLInput{
|
||||
State: state,
|
||||
Nonce: nonce,
|
||||
CodeChallenge: p.CodeChallenge,
|
||||
CodeChallengeMethod: p.CodeChallengeMethod,
|
||||
RedirectURI: oobRedirectURI,
|
||||
State: state,
|
||||
Nonce: nonce,
|
||||
CodeChallenge: p.CodeChallenge,
|
||||
CodeChallengeMethod: p.CodeChallengeMethod,
|
||||
RedirectURI: oobRedirectURI,
|
||||
AuthRequestExtraParams: o.AuthRequestExtraParams,
|
||||
})
|
||||
u.Logger.Printf("Open %s", authCodeURL)
|
||||
code, err := u.Reader.ReadString(authCodeKeyboardPrompt)
|
||||
|
||||
@@ -29,9 +29,17 @@ func TestAuthCodeKeyboard_Do(t *testing.T) {
|
||||
defer ctrl.Finish()
|
||||
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
|
||||
defer cancel()
|
||||
o := &AuthCodeKeyboardOption{
|
||||
AuthRequestExtraParams: map[string]string{"ttl": "86400", "reauth": "true"},
|
||||
}
|
||||
mockOIDCClient := mock_oidcclient.NewMockInterface(ctrl)
|
||||
mockOIDCClient.EXPECT().
|
||||
GetAuthCodeURL(nonNil).
|
||||
Do(func(in oidcclient.AuthCodeURLInput) {
|
||||
if diff := cmp.Diff(o.AuthRequestExtraParams, in.AuthRequestExtraParams); diff != "" {
|
||||
t.Errorf("AuthRequestExtraParams mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
}).
|
||||
Return("https://issuer.example.com/auth")
|
||||
mockOIDCClient.EXPECT().
|
||||
ExchangeAuthCode(nonNil, nonNil).
|
||||
@@ -53,7 +61,7 @@ func TestAuthCodeKeyboard_Do(t *testing.T) {
|
||||
Reader: mockReader,
|
||||
Logger: logger.New(t),
|
||||
}
|
||||
got, err := u.Do(ctx, nil, mockOIDCClient)
|
||||
got, err := u.Do(ctx, o, mockOIDCClient)
|
||||
if err != nil {
|
||||
t.Errorf("Do returned error: %+v", err)
|
||||
}
|
||||
|
||||
@@ -28,13 +28,20 @@ func TestAuthCode_Do(t *testing.T) {
|
||||
ctx, cancel := context.WithTimeout(context.TODO(), timeout)
|
||||
defer cancel()
|
||||
o := &AuthCodeOption{
|
||||
BindAddress: []string{"127.0.0.1:8000"},
|
||||
SkipOpenBrowser: true,
|
||||
BindAddress: []string{"127.0.0.1:8000"},
|
||||
SkipOpenBrowser: true,
|
||||
AuthRequestExtraParams: map[string]string{"ttl": "86400", "reauth": "true"},
|
||||
}
|
||||
mockOIDCClient := mock_oidcclient.NewMockInterface(ctrl)
|
||||
mockOIDCClient.EXPECT().
|
||||
GetTokenByAuthCode(gomock.Any(), gomock.Any(), gomock.Any()).
|
||||
Do(func(_ context.Context, _ oidcclient.GetTokenByAuthCodeInput, readyChan chan<- string) {
|
||||
Do(func(_ context.Context, in oidcclient.GetTokenByAuthCodeInput, readyChan chan<- string) {
|
||||
if diff := cmp.Diff(o.BindAddress, in.BindAddress); diff != "" {
|
||||
t.Errorf("BindAddress mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
if diff := cmp.Diff(o.AuthRequestExtraParams, in.AuthRequestExtraParams); diff != "" {
|
||||
t.Errorf("AuthRequestExtraParams mismatch (-want +got):\n%s", diff)
|
||||
}
|
||||
readyChan <- "LOCAL_SERVER_URL"
|
||||
}).
|
||||
Return(&oidcclient.TokenSet{
|
||||
|
||||
@@ -47,11 +47,14 @@ type GrantOptionSet struct {
|
||||
}
|
||||
|
||||
type AuthCodeOption struct {
|
||||
SkipOpenBrowser bool
|
||||
BindAddress []string
|
||||
SkipOpenBrowser bool
|
||||
BindAddress []string
|
||||
AuthRequestExtraParams map[string]string
|
||||
}
|
||||
|
||||
type AuthCodeKeyboardOption struct{}
|
||||
type AuthCodeKeyboardOption struct {
|
||||
AuthRequestExtraParams map[string]string
|
||||
}
|
||||
|
||||
type ROPCOption struct {
|
||||
Username string
|
||||
|
||||
Reference in New Issue
Block a user