mirror of
https://github.com/int128/kubelogin.git
synced 2026-03-13 22:30:49 +00:00
Compare commits
5 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
9bf8a89577 | ||
|
|
a91c020f46 | ||
|
|
d4fb49613d | ||
|
|
64b1d52208 | ||
|
|
a298058e3f |
@@ -10,7 +10,7 @@ jobs:
|
||||
- run: go get github.com/golang/lint/golint
|
||||
- run: golint
|
||||
- run: go build -v
|
||||
- run: make -C e2e/testdata
|
||||
- run: make -C e2e/authserver/testdata
|
||||
- run: go test -v ./...
|
||||
|
||||
release:
|
||||
|
||||
12
README.md
12
README.md
@@ -1,6 +1,6 @@
|
||||
# kubelogin [](https://circleci.com/gh/int128/kubelogin)
|
||||
|
||||
This is a helper command for [Kubernetes OpenID Connect (OIDC) authentication](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens).
|
||||
This is a command for [Kubernetes OpenID Connect (OIDC) authentication](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens).
|
||||
It gets a token from the OIDC provider and writes it to the kubeconfig.
|
||||
|
||||
This may work with various OIDC providers such as Keycloak, Google Identity Platform and Azure AD.
|
||||
@@ -10,7 +10,7 @@ This may work with various OIDC providers such as Keycloak, Google Identity Plat
|
||||
|
||||
You need to setup the OIDC provider and [Kubernetes OIDC authentication](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#openid-connect-tokens).
|
||||
|
||||
After setup or when the token has been expired, just run `kubelogin`:
|
||||
After initial setup or when the token has been expired, just run `kubelogin`:
|
||||
|
||||
```
|
||||
% kubelogin
|
||||
@@ -198,7 +198,7 @@ Help Options:
|
||||
-h, --help Show this help message
|
||||
```
|
||||
|
||||
This supports the following `auth-provider` keys in kubeconfig.
|
||||
This supports the following keys of `auth-provider` in kubeconfig.
|
||||
See also [kubectl authentication](https://kubernetes.io/docs/reference/access-authn-authz/authentication/#using-kubectl).
|
||||
|
||||
Key | Direction | Value
|
||||
@@ -208,6 +208,7 @@ Key | Direction | Value
|
||||
`client-secret` | IN (Required) | Client Secret of the provider.
|
||||
`idp-certificate-authority` | IN (Optional) | CA certificate path of the provider.
|
||||
`idp-certificate-authority-data` | IN (Optional) | Base64 encoded CA certificate of the provider.
|
||||
`extra-scopes` | IN (Optional) | Scopes to request to the provider (comma separated).
|
||||
`id-token` | OUT | ID token got from the provider.
|
||||
`refresh-token` | OUT | Refresh token got from the provider.
|
||||
|
||||
@@ -272,10 +273,11 @@ go get github.com/int128/kubelogin
|
||||
|
||||
```sh
|
||||
cd $GOPATH/src/github.com/int128/kubelogin
|
||||
make -C e2e/testdata
|
||||
make -C e2e/authserver/testdata
|
||||
go test -v ./...
|
||||
```
|
||||
|
||||
### Release
|
||||
|
||||
CircleCI publishes the build to GitHub. See [.circleci/config.yml](.circleci/config.yml).
|
||||
CircleCI publishes the build to GitHub.
|
||||
See [.circleci/config.yml](.circleci/config.yml).
|
||||
|
||||
@@ -70,6 +70,7 @@ func (c *CLI) Run(ctx context.Context) error {
|
||||
Issuer: authProvider.IDPIssuerURL(),
|
||||
ClientID: authProvider.ClientID(),
|
||||
ClientSecret: authProvider.ClientSecret(),
|
||||
ExtraScopes: authProvider.ExtraScopes(),
|
||||
Client: &http.Client{Transport: &http.Transport{TLSClientConfig: tlsConfig}},
|
||||
ServerPort: 8000,
|
||||
SkipOpenBrowser: c.SkipOpenBrowser,
|
||||
|
||||
49
e2e/authserver/authserver.go
Normal file
49
e2e/authserver/authserver.go
Normal file
@@ -0,0 +1,49 @@
|
||||
package authserver
|
||||
|
||||
import (
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
// Addr is address to listen.
|
||||
const Addr = "localhost:9000"
|
||||
|
||||
// CACert is path to the CA certificate.
|
||||
// This should be generated by Makefile before test.
|
||||
const CACert = "authserver/testdata/ca.crt"
|
||||
|
||||
// ServerCert is path to the server certificate.
|
||||
// This should be generated by Makefile before test.
|
||||
const ServerCert = "authserver/testdata/server.crt"
|
||||
|
||||
// ServerKey is path to the server key.
|
||||
// This should be generated by Makefile before test.
|
||||
const ServerKey = "authserver/testdata/server.key"
|
||||
|
||||
// Config represents server configuration.
|
||||
type Config struct {
|
||||
Issuer string
|
||||
Scope string
|
||||
Cert string
|
||||
Key string
|
||||
}
|
||||
|
||||
// Start starts a HTTP server.
|
||||
func (c *Config) Start(t *testing.T) *http.Server {
|
||||
s := &http.Server{
|
||||
Addr: Addr,
|
||||
Handler: newHandler(t, c),
|
||||
}
|
||||
go func() {
|
||||
var err error
|
||||
if c.Cert != "" && c.Key != "" {
|
||||
err = s.ListenAndServeTLS(c.Cert, c.Key)
|
||||
} else {
|
||||
err = s.ListenAndServe()
|
||||
}
|
||||
if err != nil && err != http.ErrServerClosed {
|
||||
t.Error(err)
|
||||
}
|
||||
}()
|
||||
return s
|
||||
}
|
||||
@@ -1,45 +1,47 @@
|
||||
package e2e
|
||||
package authserver
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/rsa"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"html/template"
|
||||
"log"
|
||||
"math/big"
|
||||
"net/http"
|
||||
"testing"
|
||||
"text/template"
|
||||
"time"
|
||||
|
||||
jwt "github.com/dgrijalva/jwt-go"
|
||||
)
|
||||
|
||||
// AuthHandler provides the stub handler for OIDC authentication.
|
||||
type AuthHandler struct {
|
||||
// Values in templates
|
||||
type handler struct {
|
||||
discovery *template.Template
|
||||
token *template.Template
|
||||
jwks *template.Template
|
||||
authCode string
|
||||
|
||||
Issuer string
|
||||
AuthCode string
|
||||
Scope string // Default to openid
|
||||
IDToken string
|
||||
PrivateKey struct{ N, E string }
|
||||
|
||||
// Response templates
|
||||
discoveryJSON *template.Template
|
||||
tokenJSON *template.Template
|
||||
jwksJSON *template.Template
|
||||
}
|
||||
|
||||
// NewAuthHandler returns a new AuthHandler.
|
||||
func NewAuthHandler(t *testing.T, issuer string) *AuthHandler {
|
||||
h := &AuthHandler{
|
||||
Issuer: issuer,
|
||||
AuthCode: "0b70006b-f62a-4438-aba5-c0b96775d8e5",
|
||||
discoveryJSON: template.Must(template.ParseFiles("testdata/oidc-discovery.json")),
|
||||
tokenJSON: template.Must(template.ParseFiles("testdata/oidc-token.json")),
|
||||
jwksJSON: template.Must(template.ParseFiles("testdata/oidc-jwks.json")),
|
||||
func newHandler(t *testing.T, c *Config) *handler {
|
||||
h := handler{
|
||||
discovery: readTemplate(t, "oidc-discovery.json"),
|
||||
token: readTemplate(t, "oidc-token.json"),
|
||||
jwks: readTemplate(t, "oidc-jwks.json"),
|
||||
authCode: "3d24a8bd-35e6-457d-999e-e04bb1dfcec7",
|
||||
Issuer: c.Issuer,
|
||||
Scope: c.Scope,
|
||||
}
|
||||
if h.Scope == "" {
|
||||
h.Scope = "openid"
|
||||
}
|
||||
|
||||
token := jwt.NewWithClaims(jwt.SigningMethodRS256, jwt.StandardClaims{
|
||||
Issuer: h.Issuer,
|
||||
Issuer: c.Issuer,
|
||||
Audience: "kubernetes",
|
||||
ExpiresAt: time.Now().Add(time.Hour).Unix(),
|
||||
})
|
||||
@@ -53,24 +55,43 @@ func NewAuthHandler(t *testing.T, issuer string) *AuthHandler {
|
||||
}
|
||||
h.PrivateKey.E = base64.RawURLEncoding.EncodeToString(big.NewInt(int64(k.E)).Bytes())
|
||||
h.PrivateKey.N = base64.RawURLEncoding.EncodeToString(k.N.Bytes())
|
||||
return h
|
||||
return &h
|
||||
}
|
||||
|
||||
func (s *AuthHandler) serveHTTP(w http.ResponseWriter, r *http.Request) error {
|
||||
func readTemplate(t *testing.T, name string) *template.Template {
|
||||
t.Helper()
|
||||
tpl, err := template.ParseFiles("authserver/testdata/" + name)
|
||||
if err != nil {
|
||||
t.Fatalf("Could not read template %s: %s", name, err)
|
||||
}
|
||||
return tpl
|
||||
}
|
||||
|
||||
func (h *handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if err := h.serveHTTP(w, r); err != nil {
|
||||
log.Printf("[auth-server] Error: %s", err)
|
||||
w.WriteHeader(500)
|
||||
}
|
||||
}
|
||||
|
||||
func (h *handler) serveHTTP(w http.ResponseWriter, r *http.Request) error {
|
||||
m := r.Method
|
||||
p := r.URL.Path
|
||||
log.Printf("[auth-server] %s %s", m, r.RequestURI)
|
||||
switch {
|
||||
case m == "GET" && p == "/.well-known/openid-configuration":
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
if err := s.discoveryJSON.Execute(w, s); err != nil {
|
||||
if err := h.discovery.Execute(w, h); err != nil {
|
||||
return err
|
||||
}
|
||||
case m == "GET" && p == "/protocol/openid-connect/auth":
|
||||
// Authentication Response
|
||||
// http://openid.net/specs/openid-connect-core-1_0.html#AuthResponse
|
||||
q := r.URL.Query()
|
||||
to := fmt.Sprintf("%s?state=%s&code=%s", q.Get("redirect_uri"), q.Get("state"), s.AuthCode)
|
||||
if h.Scope != q.Get("scope") {
|
||||
return fmt.Errorf("scope wants %s but %s", h.Scope, q.Get("scope"))
|
||||
}
|
||||
to := fmt.Sprintf("%s?state=%s&code=%s", q.Get("redirect_uri"), q.Get("state"), h.authCode)
|
||||
http.Redirect(w, r, to, 302)
|
||||
case m == "POST" && p == "/protocol/openid-connect/token":
|
||||
// Token Response
|
||||
@@ -78,16 +99,16 @@ func (s *AuthHandler) serveHTTP(w http.ResponseWriter, r *http.Request) error {
|
||||
if err := r.ParseForm(); err != nil {
|
||||
return err
|
||||
}
|
||||
if s.AuthCode != r.Form.Get("code") {
|
||||
return fmt.Errorf("code wants %s but %s", s.AuthCode, r.Form.Get("code"))
|
||||
if h.authCode != r.Form.Get("code") {
|
||||
return fmt.Errorf("code wants %s but %s", h.authCode, r.Form.Get("code"))
|
||||
}
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
if err := s.tokenJSON.Execute(w, s); err != nil {
|
||||
if err := h.token.Execute(w, h); err != nil {
|
||||
return err
|
||||
}
|
||||
case m == "GET" && p == "/protocol/openid-connect/certs":
|
||||
w.Header().Add("Content-Type", "application/json")
|
||||
if err := s.jwksJSON.Execute(w, s); err != nil {
|
||||
if err := h.jwks.Execute(w, h); err != nil {
|
||||
return err
|
||||
}
|
||||
default:
|
||||
@@ -95,10 +116,3 @@ func (s *AuthHandler) serveHTTP(w http.ResponseWriter, r *http.Request) error {
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (s *AuthHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if err := s.serveHTTP(w, r); err != nil {
|
||||
log.Printf("[auth-server] Error: %s", err)
|
||||
w.WriteHeader(500)
|
||||
}
|
||||
}
|
||||
@@ -1,40 +1,40 @@
|
||||
.PHONY: clean
|
||||
|
||||
all: authserver.crt authserver-ca.crt
|
||||
all: server.crt ca.crt
|
||||
|
||||
clean:
|
||||
rm -v authserver*
|
||||
rm -v ca.* server.*
|
||||
|
||||
authserver-ca.key:
|
||||
ca.key:
|
||||
openssl genrsa -out $@ 1024
|
||||
|
||||
authserver-ca.csr: openssl.cnf authserver-ca.key
|
||||
ca.csr: openssl.cnf ca.key
|
||||
openssl req -config openssl.cnf \
|
||||
-new \
|
||||
-key authserver-ca.key \
|
||||
-key ca.key \
|
||||
-subj "/CN=Hello CA" \
|
||||
-out $@
|
||||
openssl req -noout -text -in $@
|
||||
|
||||
authserver-ca.crt: authserver-ca.csr authserver-ca.key
|
||||
ca.crt: ca.csr ca.key
|
||||
openssl x509 -req \
|
||||
-signkey authserver-ca.key \
|
||||
-in authserver-ca.csr \
|
||||
-signkey ca.key \
|
||||
-in ca.csr \
|
||||
-out $@
|
||||
openssl x509 -text -in $@
|
||||
|
||||
authserver.key:
|
||||
server.key:
|
||||
openssl genrsa -out $@ 1024
|
||||
|
||||
authserver.csr: openssl.cnf authserver.key
|
||||
server.csr: openssl.cnf server.key
|
||||
openssl req -config openssl.cnf \
|
||||
-new \
|
||||
-key authserver.key \
|
||||
-key server.key \
|
||||
-subj "/CN=localhost" \
|
||||
-out $@
|
||||
openssl req -noout -text -in $@
|
||||
|
||||
authserver.crt: openssl.cnf authserver.csr authserver-ca.key authserver-ca.crt
|
||||
server.crt: openssl.cnf server.csr ca.key ca.crt
|
||||
rm -fr ./CA
|
||||
mkdir -p ./CA
|
||||
touch CA/index.txt
|
||||
@@ -43,8 +43,8 @@ authserver.crt: openssl.cnf authserver.csr authserver-ca.key authserver-ca.crt
|
||||
openssl ca -config openssl.cnf \
|
||||
-extensions v3_req \
|
||||
-batch \
|
||||
-cert authserver-ca.crt \
|
||||
-keyfile authserver-ca.key \
|
||||
-in authserver.csr \
|
||||
-cert ca.crt \
|
||||
-keyfile ca.key \
|
||||
-in server.csr \
|
||||
-out $@
|
||||
openssl x509 -text -in $@
|
||||
@@ -5,6 +5,7 @@ import (
|
||||
"crypto/tls"
|
||||
"crypto/x509"
|
||||
"encoding/base64"
|
||||
"fmt"
|
||||
"io/ioutil"
|
||||
"net/http"
|
||||
"os"
|
||||
@@ -12,13 +13,10 @@ import (
|
||||
"time"
|
||||
|
||||
"github.com/int128/kubelogin/cli"
|
||||
"github.com/int128/kubelogin/e2e/authserver"
|
||||
"golang.org/x/sync/errgroup"
|
||||
)
|
||||
|
||||
const tlsCACert = "testdata/authserver-ca.crt"
|
||||
const tlsServerCert = "testdata/authserver.crt"
|
||||
const tlsServerKey = "testdata/authserver.key"
|
||||
|
||||
// End-to-end test.
|
||||
//
|
||||
// 1. Start the auth server at port 9000.
|
||||
@@ -30,38 +28,62 @@ func TestE2E(t *testing.T) {
|
||||
data := map[string]struct {
|
||||
kubeconfigValues kubeconfigValues
|
||||
cli cli.CLI
|
||||
startServer func(*testing.T, http.Handler) *http.Server
|
||||
authClientTLS *tls.Config
|
||||
serverConfig authserver.Config
|
||||
clientTLS *tls.Config
|
||||
}{
|
||||
"NoTLS": {
|
||||
kubeconfigValues{Issuer: "http://localhost:9000"},
|
||||
cli.CLI{},
|
||||
startServer,
|
||||
authserver.Config{Issuer: "http://localhost:9000"},
|
||||
&tls.Config{},
|
||||
},
|
||||
"ExtraScope": {
|
||||
kubeconfigValues{
|
||||
Issuer: "http://localhost:9000",
|
||||
ExtraScopes: "profile groups",
|
||||
},
|
||||
cli.CLI{},
|
||||
authserver.Config{
|
||||
Issuer: "http://localhost:9000",
|
||||
Scope: "profile groups openid",
|
||||
},
|
||||
&tls.Config{},
|
||||
},
|
||||
"SkipTLSVerify": {
|
||||
kubeconfigValues{Issuer: "https://localhost:9000"},
|
||||
cli.CLI{SkipTLSVerify: true},
|
||||
startServerTLS,
|
||||
authserver.Config{
|
||||
Issuer: "https://localhost:9000",
|
||||
Cert: authserver.ServerCert,
|
||||
Key: authserver.ServerKey,
|
||||
},
|
||||
&tls.Config{InsecureSkipVerify: true},
|
||||
},
|
||||
"CACert": {
|
||||
kubeconfigValues{
|
||||
Issuer: "https://localhost:9000",
|
||||
IDPCertificateAuthority: tlsCACert,
|
||||
IDPCertificateAuthority: authserver.CACert,
|
||||
},
|
||||
cli.CLI{},
|
||||
startServerTLS,
|
||||
&tls.Config{RootCAs: readCert(t, tlsCACert)},
|
||||
authserver.Config{
|
||||
Issuer: "https://localhost:9000",
|
||||
Cert: authserver.ServerCert,
|
||||
Key: authserver.ServerKey,
|
||||
},
|
||||
&tls.Config{RootCAs: readCert(t, authserver.CACert)},
|
||||
},
|
||||
"CACertData": {
|
||||
kubeconfigValues{
|
||||
Issuer: "https://localhost:9000",
|
||||
IDPCertificateAuthorityData: base64.StdEncoding.EncodeToString(read(t, tlsCACert)),
|
||||
IDPCertificateAuthorityData: base64.StdEncoding.EncodeToString(read(t, authserver.CACert)),
|
||||
},
|
||||
cli.CLI{},
|
||||
startServerTLS,
|
||||
&tls.Config{RootCAs: readCert(t, tlsCACert)},
|
||||
authserver.Config{
|
||||
Issuer: "https://localhost:9000",
|
||||
Cert: authserver.ServerCert,
|
||||
Key: authserver.ServerKey,
|
||||
},
|
||||
&tls.Config{RootCAs: readCert(t, authserver.CACert)},
|
||||
},
|
||||
}
|
||||
|
||||
@@ -69,8 +91,8 @@ func TestE2E(t *testing.T) {
|
||||
t.Run(name, func(t *testing.T) {
|
||||
ctx, cancel := context.WithCancel(context.Background())
|
||||
defer cancel()
|
||||
authServer := c.startServer(t, NewAuthHandler(t, c.kubeconfigValues.Issuer))
|
||||
defer authServer.Shutdown(ctx)
|
||||
server := c.serverConfig.Start(t)
|
||||
defer server.Shutdown(ctx)
|
||||
kubeconfig := createKubeconfig(t, &c.kubeconfigValues)
|
||||
defer os.Remove(kubeconfig)
|
||||
c.cli.KubeConfig = kubeconfig
|
||||
@@ -80,17 +102,10 @@ func TestE2E(t *testing.T) {
|
||||
eg.Go(func() error {
|
||||
return c.cli.Run(ctx)
|
||||
})
|
||||
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
client := http.Client{Transport: &http.Transport{TLSClientConfig: c.authClientTLS}}
|
||||
res, err := client.Get("http://localhost:8000/")
|
||||
if err != nil {
|
||||
t.Fatalf("Could not send a request: %s", err)
|
||||
if err := openBrowserRequest(c.clientTLS); err != nil {
|
||||
cancel()
|
||||
t.Error(err)
|
||||
}
|
||||
if res.StatusCode != 200 {
|
||||
t.Fatalf("StatusCode wants 200 but %d", res.StatusCode)
|
||||
}
|
||||
|
||||
if err := eg.Wait(); err != nil {
|
||||
t.Fatalf("CLI returned error: %s", err)
|
||||
}
|
||||
@@ -99,30 +114,17 @@ func TestE2E(t *testing.T) {
|
||||
}
|
||||
}
|
||||
|
||||
func startServer(t *testing.T, h http.Handler) *http.Server {
|
||||
s := &http.Server{
|
||||
Addr: "localhost:9000",
|
||||
Handler: h,
|
||||
func openBrowserRequest(tlsConfig *tls.Config) error {
|
||||
time.Sleep(50 * time.Millisecond)
|
||||
client := http.Client{Transport: &http.Transport{TLSClientConfig: tlsConfig}}
|
||||
res, err := client.Get("http://localhost:8000/")
|
||||
if err != nil {
|
||||
return fmt.Errorf("Could not send a request: %s", err)
|
||||
}
|
||||
go func() {
|
||||
if err := s.ListenAndServe(); err != nil && err != http.ErrServerClosed {
|
||||
t.Error(err)
|
||||
}
|
||||
}()
|
||||
return s
|
||||
}
|
||||
|
||||
func startServerTLS(t *testing.T, h http.Handler) *http.Server {
|
||||
s := &http.Server{
|
||||
Addr: "localhost:9000",
|
||||
Handler: h,
|
||||
if res.StatusCode != 200 {
|
||||
return fmt.Errorf("StatusCode wants 200 but %d", res.StatusCode)
|
||||
}
|
||||
go func() {
|
||||
if err := s.ListenAndServeTLS(tlsServerCert, tlsServerKey); err != nil && err != http.ErrServerClosed {
|
||||
t.Error(err)
|
||||
}
|
||||
}()
|
||||
return s
|
||||
return nil
|
||||
}
|
||||
|
||||
func read(t *testing.T, name string) []byte {
|
||||
|
||||
@@ -9,6 +9,7 @@ import (
|
||||
|
||||
type kubeconfigValues struct {
|
||||
Issuer string
|
||||
ExtraScopes string
|
||||
IDPCertificateAuthority string
|
||||
IDPCertificateAuthorityData string
|
||||
}
|
||||
|
||||
3
e2e/testdata/kubeconfig.yaml
vendored
3
e2e/testdata/kubeconfig.yaml
vendored
@@ -19,6 +19,9 @@ users:
|
||||
client-id: kubernetes
|
||||
client-secret: a3c508c3-73c9-42e2-ab14-487a1bf67c33
|
||||
idp-issuer-url: {{ .Issuer }}
|
||||
#{{ if .ExtraScopes }}
|
||||
extra-scopes: {{ .ExtraScopes }}
|
||||
#{{ end }}
|
||||
#{{ if .IDPCertificateAuthority }}
|
||||
idp-certificate-authority: {{ .IDPCertificateAuthority }}
|
||||
#{{ end }}
|
||||
|
||||
@@ -2,6 +2,7 @@ package kubeconfig
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"k8s.io/client-go/tools/clientcmd/api"
|
||||
)
|
||||
@@ -55,6 +56,14 @@ func (c *OIDCAuthProvider) IDPCertificateAuthorityData() string {
|
||||
return c.Config["idp-certificate-authority-data"]
|
||||
}
|
||||
|
||||
// ExtraScopes returns the extra-scopes.
|
||||
func (c *OIDCAuthProvider) ExtraScopes() []string {
|
||||
if c.Config["extra-scopes"] == "" {
|
||||
return []string{}
|
||||
}
|
||||
return strings.Split(c.Config["extra-scopes"], ",")
|
||||
}
|
||||
|
||||
// SetIDToken replaces the id-token.
|
||||
func (c *OIDCAuthProvider) SetIDToken(idToken string) {
|
||||
c.Config["id-token"] = idToken
|
||||
|
||||
Reference in New Issue
Block a user