Skip to content
Projects
Groups
Snippets
Help
This project
Loading...
Sign in / Register
Toggle navigation
K
k3s
Project
Project
Details
Activity
Cycle Analytics
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Issues
0
Issues
0
List
Board
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Charts
Registry
Registry
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Jacklull
k3s
Commits
56d0aebe
Commit
56d0aebe
authored
Oct 08, 2018
by
Darren Shepherd
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Delete oidc
parent
c691249e
Hide whitespace changes
Inline
Side-by-side
Showing
13 changed files
with
0 additions
and
2672 deletions
+0
-2672
config.go
pkg/kubeapiserver/authenticator/config.go
+0
-59
authentication.go
pkg/kubeapiserver/options/authentication.go
+0
-78
BUILD
...8s.io/apiserver/plugin/pkg/authenticator/token/oidc/BUILD
+0
-51
OWNERS
...s.io/apiserver/plugin/pkg/authenticator/token/oidc/OWNERS
+0
-7
oidc.go
....io/apiserver/plugin/pkg/authenticator/token/oidc/oidc.go
+0
-703
oidc_test.go
...piserver/plugin/pkg/authenticator/token/oidc/oidc_test.go
+0
-1643
ecdsa_1.pem
.../plugin/pkg/authenticator/token/oidc/testdata/ecdsa_1.pem
+0
-7
ecdsa_2.pem
.../plugin/pkg/authenticator/token/oidc/testdata/ecdsa_2.pem
+0
-7
ecdsa_3.pem
.../plugin/pkg/authenticator/token/oidc/testdata/ecdsa_3.pem
+0
-7
gen.sh
...erver/plugin/pkg/authenticator/token/oidc/testdata/gen.sh
+0
-29
rsa_1.pem
...er/plugin/pkg/authenticator/token/oidc/testdata/rsa_1.pem
+0
-27
rsa_2.pem
...er/plugin/pkg/authenticator/token/oidc/testdata/rsa_2.pem
+0
-27
rsa_3.pem
...er/plugin/pkg/authenticator/token/oidc/testdata/rsa_3.pem
+0
-27
No files found.
pkg/kubeapiserver/authenticator/config.go
View file @
56d0aebe
...
@@ -34,7 +34,6 @@ import (
...
@@ -34,7 +34,6 @@ import (
utilfeature
"k8s.io/apiserver/pkg/util/feature"
utilfeature
"k8s.io/apiserver/pkg/util/feature"
"k8s.io/apiserver/plugin/pkg/authenticator/password/passwordfile"
"k8s.io/apiserver/plugin/pkg/authenticator/password/passwordfile"
"k8s.io/apiserver/plugin/pkg/authenticator/request/basicauth"
"k8s.io/apiserver/plugin/pkg/authenticator/request/basicauth"
"k8s.io/apiserver/plugin/pkg/authenticator/token/oidc"
"k8s.io/apiserver/plugin/pkg/authenticator/token/webhook"
"k8s.io/apiserver/plugin/pkg/authenticator/token/webhook"
// Initialize all known client auth plugins.
// Initialize all known client auth plugins.
...
@@ -51,15 +50,6 @@ type Config struct {
...
@@ -51,15 +50,6 @@ type Config struct {
BasicAuthFile
string
BasicAuthFile
string
ClientCAFile
string
ClientCAFile
string
TokenAuthFile
string
TokenAuthFile
string
OIDCIssuerURL
string
OIDCClientID
string
OIDCCAFile
string
OIDCUsernameClaim
string
OIDCUsernamePrefix
string
OIDCGroupsClaim
string
OIDCGroupsPrefix
string
OIDCSigningAlgs
[]
string
OIDCRequiredClaims
map
[
string
]
string
ServiceAccountKeyFiles
[]
string
ServiceAccountKeyFiles
[]
string
ServiceAccountLookup
bool
ServiceAccountLookup
bool
ServiceAccountIssuer
string
ServiceAccountIssuer
string
...
@@ -138,30 +128,6 @@ func (config Config) New() (authenticator.Request, error) {
...
@@ -138,30 +128,6 @@ func (config Config) New() (authenticator.Request, error) {
}
}
tokenAuthenticators
=
append
(
tokenAuthenticators
,
serviceAccountAuth
)
tokenAuthenticators
=
append
(
tokenAuthenticators
,
serviceAccountAuth
)
}
}
// NOTE(ericchiang): Keep the OpenID Connect after Service Accounts.
//
// Because both plugins verify JWTs whichever comes first in the union experiences
// cache misses for all requests using the other. While the service account plugin
// simply returns an error, the OpenID Connect plugin may query the provider to
// update the keys, causing performance hits.
if
len
(
config
.
OIDCIssuerURL
)
>
0
&&
len
(
config
.
OIDCClientID
)
>
0
{
oidcAuth
,
err
:=
newAuthenticatorFromOIDCIssuerURL
(
oidc
.
Options
{
IssuerURL
:
config
.
OIDCIssuerURL
,
ClientID
:
config
.
OIDCClientID
,
APIAudiences
:
config
.
APIAudiences
,
CAFile
:
config
.
OIDCCAFile
,
UsernameClaim
:
config
.
OIDCUsernameClaim
,
UsernamePrefix
:
config
.
OIDCUsernamePrefix
,
GroupsClaim
:
config
.
OIDCGroupsClaim
,
GroupsPrefix
:
config
.
OIDCGroupsPrefix
,
SupportedSigningAlgs
:
config
.
OIDCSigningAlgs
,
RequiredClaims
:
config
.
OIDCRequiredClaims
,
})
if
err
!=
nil
{
return
nil
,
err
}
tokenAuthenticators
=
append
(
tokenAuthenticators
,
oidcAuth
)
}
if
len
(
config
.
WebhookTokenAuthnConfigFile
)
>
0
{
if
len
(
config
.
WebhookTokenAuthnConfigFile
)
>
0
{
webhookTokenAuth
,
err
:=
newWebhookTokenAuthenticator
(
config
.
WebhookTokenAuthnConfigFile
,
config
.
WebhookTokenAuthnCacheTTL
,
config
.
APIAudiences
)
webhookTokenAuth
,
err
:=
newWebhookTokenAuthenticator
(
config
.
WebhookTokenAuthnConfigFile
,
config
.
WebhookTokenAuthnCacheTTL
,
config
.
APIAudiences
)
if
err
!=
nil
{
if
err
!=
nil
{
...
@@ -226,31 +192,6 @@ func newAuthenticatorFromTokenFile(tokenAuthFile string) (authenticator.Token, e
...
@@ -226,31 +192,6 @@ func newAuthenticatorFromTokenFile(tokenAuthFile string) (authenticator.Token, e
return
tokenAuthenticator
,
nil
return
tokenAuthenticator
,
nil
}
}
// newAuthenticatorFromOIDCIssuerURL returns an authenticator.Token or an error.
func
newAuthenticatorFromOIDCIssuerURL
(
opts
oidc
.
Options
)
(
authenticator
.
Token
,
error
)
{
const
noUsernamePrefix
=
"-"
if
opts
.
UsernamePrefix
==
""
&&
opts
.
UsernameClaim
!=
"email"
{
// Old behavior. If a usernamePrefix isn't provided, prefix all claims other than "email"
// with the issuerURL.
//
// See https://github.com/kubernetes/kubernetes/issues/31380
opts
.
UsernamePrefix
=
opts
.
IssuerURL
+
"#"
}
if
opts
.
UsernamePrefix
==
noUsernamePrefix
{
// Special value indicating usernames shouldn't be prefixed.
opts
.
UsernamePrefix
=
""
}
tokenAuthenticator
,
err
:=
oidc
.
New
(
opts
)
if
err
!=
nil
{
return
nil
,
err
}
return
tokenAuthenticator
,
nil
}
// newLegacyServiceAccountAuthenticator returns an authenticator.Token or an error
// newLegacyServiceAccountAuthenticator returns an authenticator.Token or an error
func
newLegacyServiceAccountAuthenticator
(
keyfiles
[]
string
,
lookup
bool
,
apiAudiences
authenticator
.
Audiences
,
serviceAccountGetter
serviceaccount
.
ServiceAccountTokenGetter
)
(
authenticator
.
Token
,
error
)
{
func
newLegacyServiceAccountAuthenticator
(
keyfiles
[]
string
,
lookup
bool
,
apiAudiences
authenticator
.
Audiences
,
serviceAccountGetter
serviceaccount
.
ServiceAccountTokenGetter
)
(
authenticator
.
Token
,
error
)
{
allPublicKeys
:=
[]
interface
{}{}
allPublicKeys
:=
[]
interface
{}{}
...
...
pkg/kubeapiserver/options/authentication.go
View file @
56d0aebe
...
@@ -29,7 +29,6 @@ import (
...
@@ -29,7 +29,6 @@ import (
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/authenticator"
genericapiserver
"k8s.io/apiserver/pkg/server"
genericapiserver
"k8s.io/apiserver/pkg/server"
genericoptions
"k8s.io/apiserver/pkg/server/options"
genericoptions
"k8s.io/apiserver/pkg/server/options"
cliflag
"k8s.io/component-base/cli/flag"
kubeauthenticator
"k8s.io/kubernetes/pkg/kubeapiserver/authenticator"
kubeauthenticator
"k8s.io/kubernetes/pkg/kubeapiserver/authenticator"
authzmodes
"k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
authzmodes
"k8s.io/kubernetes/pkg/kubeapiserver/authorizer/modes"
)
)
...
@@ -38,7 +37,6 @@ type BuiltInAuthenticationOptions struct {
...
@@ -38,7 +37,6 @@ type BuiltInAuthenticationOptions struct {
APIAudiences
[]
string
APIAudiences
[]
string
Anonymous
*
AnonymousAuthenticationOptions
Anonymous
*
AnonymousAuthenticationOptions
ClientCert
*
genericoptions
.
ClientCertAuthenticationOptions
ClientCert
*
genericoptions
.
ClientCertAuthenticationOptions
OIDC
*
OIDCAuthenticationOptions
PasswordFile
*
PasswordFileAuthenticationOptions
PasswordFile
*
PasswordFileAuthenticationOptions
RequestHeader
*
genericoptions
.
RequestHeaderAuthenticationOptions
RequestHeader
*
genericoptions
.
RequestHeaderAuthenticationOptions
ServiceAccounts
*
ServiceAccountAuthenticationOptions
ServiceAccounts
*
ServiceAccountAuthenticationOptions
...
@@ -53,18 +51,6 @@ type AnonymousAuthenticationOptions struct {
...
@@ -53,18 +51,6 @@ type AnonymousAuthenticationOptions struct {
Allow
bool
Allow
bool
}
}
type
OIDCAuthenticationOptions
struct
{
CAFile
string
ClientID
string
IssuerURL
string
UsernameClaim
string
UsernamePrefix
string
GroupsClaim
string
GroupsPrefix
string
SigningAlgs
[]
string
RequiredClaims
map
[
string
]
string
}
type
PasswordFileAuthenticationOptions
struct
{
type
PasswordFileAuthenticationOptions
struct
{
BasicAuthFile
string
BasicAuthFile
string
}
}
...
@@ -96,7 +82,6 @@ func (s *BuiltInAuthenticationOptions) WithAll() *BuiltInAuthenticationOptions {
...
@@ -96,7 +82,6 @@ func (s *BuiltInAuthenticationOptions) WithAll() *BuiltInAuthenticationOptions {
return
s
.
return
s
.
WithAnonymous
()
.
WithAnonymous
()
.
WithClientCert
()
.
WithClientCert
()
.
WithOIDC
()
.
WithPasswordFile
()
.
WithPasswordFile
()
.
WithRequestHeader
()
.
WithRequestHeader
()
.
WithServiceAccounts
()
.
WithServiceAccounts
()
.
...
@@ -114,11 +99,6 @@ func (s *BuiltInAuthenticationOptions) WithClientCert() *BuiltInAuthenticationOp
...
@@ -114,11 +99,6 @@ func (s *BuiltInAuthenticationOptions) WithClientCert() *BuiltInAuthenticationOp
return
s
return
s
}
}
func
(
s
*
BuiltInAuthenticationOptions
)
WithOIDC
()
*
BuiltInAuthenticationOptions
{
s
.
OIDC
=
&
OIDCAuthenticationOptions
{}
return
s
}
func
(
s
*
BuiltInAuthenticationOptions
)
WithPasswordFile
()
*
BuiltInAuthenticationOptions
{
func
(
s
*
BuiltInAuthenticationOptions
)
WithPasswordFile
()
*
BuiltInAuthenticationOptions
{
s
.
PasswordFile
=
&
PasswordFileAuthenticationOptions
{}
s
.
PasswordFile
=
&
PasswordFileAuthenticationOptions
{}
return
s
return
s
...
@@ -150,10 +130,6 @@ func (s *BuiltInAuthenticationOptions) WithWebHook() *BuiltInAuthenticationOptio
...
@@ -150,10 +130,6 @@ func (s *BuiltInAuthenticationOptions) WithWebHook() *BuiltInAuthenticationOptio
func
(
s
*
BuiltInAuthenticationOptions
)
Validate
()
[]
error
{
func
(
s
*
BuiltInAuthenticationOptions
)
Validate
()
[]
error
{
allErrors
:=
[]
error
{}
allErrors
:=
[]
error
{}
if
s
.
OIDC
!=
nil
&&
(
len
(
s
.
OIDC
.
IssuerURL
)
>
0
)
!=
(
len
(
s
.
OIDC
.
ClientID
)
>
0
)
{
allErrors
=
append
(
allErrors
,
fmt
.
Errorf
(
"oidc-issuer-url and oidc-client-id should be specified together"
))
}
if
s
.
ServiceAccounts
!=
nil
&&
len
(
s
.
ServiceAccounts
.
Issuer
)
>
0
&&
strings
.
Contains
(
s
.
ServiceAccounts
.
Issuer
,
":"
)
{
if
s
.
ServiceAccounts
!=
nil
&&
len
(
s
.
ServiceAccounts
.
Issuer
)
>
0
&&
strings
.
Contains
(
s
.
ServiceAccounts
.
Issuer
,
":"
)
{
if
_
,
err
:=
url
.
Parse
(
s
.
ServiceAccounts
.
Issuer
);
err
!=
nil
{
if
_
,
err
:=
url
.
Parse
(
s
.
ServiceAccounts
.
Issuer
);
err
!=
nil
{
allErrors
=
append
(
allErrors
,
fmt
.
Errorf
(
"service-account-issuer contained a ':' but was not a valid URL: %v"
,
err
))
allErrors
=
append
(
allErrors
,
fmt
.
Errorf
(
"service-account-issuer contained a ':' but was not a valid URL: %v"
,
err
))
...
@@ -181,48 +157,6 @@ func (s *BuiltInAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
...
@@ -181,48 +157,6 @@ func (s *BuiltInAuthenticationOptions) AddFlags(fs *pflag.FlagSet) {
s
.
ClientCert
.
AddFlags
(
fs
)
s
.
ClientCert
.
AddFlags
(
fs
)
}
}
if
s
.
OIDC
!=
nil
{
fs
.
StringVar
(
&
s
.
OIDC
.
IssuerURL
,
"oidc-issuer-url"
,
s
.
OIDC
.
IssuerURL
,
""
+
"The URL of the OpenID issuer, only HTTPS scheme will be accepted. "
+
"If set, it will be used to verify the OIDC JSON Web Token (JWT)."
)
fs
.
StringVar
(
&
s
.
OIDC
.
ClientID
,
"oidc-client-id"
,
s
.
OIDC
.
ClientID
,
"The client ID for the OpenID Connect client, must be set if oidc-issuer-url is set."
)
fs
.
StringVar
(
&
s
.
OIDC
.
CAFile
,
"oidc-ca-file"
,
s
.
OIDC
.
CAFile
,
""
+
"If set, the OpenID server's certificate will be verified by one of the authorities "
+
"in the oidc-ca-file, otherwise the host's root CA set will be used."
)
fs
.
StringVar
(
&
s
.
OIDC
.
UsernameClaim
,
"oidc-username-claim"
,
"sub"
,
""
+
"The OpenID claim to use as the user name. Note that claims other than the default ('sub') "
+
"is not guaranteed to be unique and immutable. This flag is experimental, please see "
+
"the authentication documentation for further details."
)
fs
.
StringVar
(
&
s
.
OIDC
.
UsernamePrefix
,
"oidc-username-prefix"
,
""
,
""
+
"If provided, all usernames will be prefixed with this value. If not provided, "
+
"username claims other than 'email' are prefixed by the issuer URL to avoid "
+
"clashes. To skip any prefixing, provide the value '-'."
)
fs
.
StringVar
(
&
s
.
OIDC
.
GroupsClaim
,
"oidc-groups-claim"
,
""
,
""
+
"If provided, the name of a custom OpenID Connect claim for specifying user groups. "
+
"The claim value is expected to be a string or array of strings. This flag is experimental, "
+
"please see the authentication documentation for further details."
)
fs
.
StringVar
(
&
s
.
OIDC
.
GroupsPrefix
,
"oidc-groups-prefix"
,
""
,
""
+
"If provided, all groups will be prefixed with this value to prevent conflicts with "
+
"other authentication strategies."
)
fs
.
StringSliceVar
(
&
s
.
OIDC
.
SigningAlgs
,
"oidc-signing-algs"
,
[]
string
{
"RS256"
},
""
+
"Comma-separated list of allowed JOSE asymmetric signing algorithms. JWTs with a "
+
"'alg' header value not in this list will be rejected. "
+
"Values are defined by RFC 7518 https://tools.ietf.org/html/rfc7518#section-3.1."
)
fs
.
Var
(
cliflag
.
NewMapStringStringNoSplit
(
&
s
.
OIDC
.
RequiredClaims
),
"oidc-required-claim"
,
""
+
"A key=value pair that describes a required claim in the ID Token. "
+
"If set, the claim is verified to be present in the ID Token with a matching value. "
+
"Repeat this flag to specify multiple claims."
)
}
if
s
.
PasswordFile
!=
nil
{
if
s
.
PasswordFile
!=
nil
{
fs
.
StringVar
(
&
s
.
PasswordFile
.
BasicAuthFile
,
"basic-auth-file"
,
s
.
PasswordFile
.
BasicAuthFile
,
""
+
fs
.
StringVar
(
&
s
.
PasswordFile
.
BasicAuthFile
,
"basic-auth-file"
,
s
.
PasswordFile
.
BasicAuthFile
,
""
+
"If set, the file that will be used to admit requests to the secure port of the API server "
+
"If set, the file that will be used to admit requests to the secure port of the API server "
+
...
@@ -289,18 +223,6 @@ func (s *BuiltInAuthenticationOptions) ToAuthenticationConfig() kubeauthenticato
...
@@ -289,18 +223,6 @@ func (s *BuiltInAuthenticationOptions) ToAuthenticationConfig() kubeauthenticato
ret
.
ClientCAFile
=
s
.
ClientCert
.
ClientCA
ret
.
ClientCAFile
=
s
.
ClientCert
.
ClientCA
}
}
if
s
.
OIDC
!=
nil
{
ret
.
OIDCCAFile
=
s
.
OIDC
.
CAFile
ret
.
OIDCClientID
=
s
.
OIDC
.
ClientID
ret
.
OIDCGroupsClaim
=
s
.
OIDC
.
GroupsClaim
ret
.
OIDCGroupsPrefix
=
s
.
OIDC
.
GroupsPrefix
ret
.
OIDCIssuerURL
=
s
.
OIDC
.
IssuerURL
ret
.
OIDCUsernameClaim
=
s
.
OIDC
.
UsernameClaim
ret
.
OIDCUsernamePrefix
=
s
.
OIDC
.
UsernamePrefix
ret
.
OIDCSigningAlgs
=
s
.
OIDC
.
SigningAlgs
ret
.
OIDCRequiredClaims
=
s
.
OIDC
.
RequiredClaims
}
if
s
.
PasswordFile
!=
nil
{
if
s
.
PasswordFile
!=
nil
{
ret
.
BasicAuthFile
=
s
.
PasswordFile
.
BasicAuthFile
ret
.
BasicAuthFile
=
s
.
PasswordFile
.
BasicAuthFile
}
}
...
...
staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/BUILD
deleted
100644 → 0
View file @
c691249e
package(default_visibility = ["//visibility:public"])
load(
"@io_bazel_rules_go//go:def.bzl",
"go_library",
"go_test",
)
go_test(
name = "go_default_test",
size = "small",
srcs = ["oidc_test.go"],
data = glob(["testdata/**"]),
embed = [":go_default_library"],
deps = [
"//staging/src/k8s.io/apiserver/pkg/authentication/authenticator:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//vendor/github.com/coreos/go-oidc:go_default_library",
"//vendor/gopkg.in/square/go-jose.v2:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
],
)
go_library(
name = "go_default_library",
srcs = ["oidc.go"],
importmap = "k8s.io/kubernetes/vendor/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc",
importpath = "k8s.io/apiserver/plugin/pkg/authenticator/token/oidc",
deps = [
"//staging/src/k8s.io/apimachinery/pkg/util/net:go_default_library",
"//staging/src/k8s.io/apimachinery/pkg/util/wait:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authentication/authenticator:go_default_library",
"//staging/src/k8s.io/apiserver/pkg/authentication/user:go_default_library",
"//staging/src/k8s.io/client-go/util/cert:go_default_library",
"//vendor/github.com/coreos/go-oidc:go_default_library",
"//vendor/k8s.io/klog:go_default_library",
],
)
filegroup(
name = "package-srcs",
srcs = glob(["**"]),
tags = ["automanaged"],
visibility = ["//visibility:private"],
)
filegroup(
name = "all-srcs",
srcs = [":package-srcs"],
tags = ["automanaged"],
)
staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/OWNERS
deleted
100644 → 0
View file @
c691249e
# See the OWNERS docs at https://go.k8s.io/owners
approvers:
- ericchiang
reviewers:
- ericchiang
- rithujohn191
staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/oidc.go
deleted
100644 → 0
View file @
c691249e
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
/*
oidc implements the authenticator.Token interface using the OpenID Connect protocol.
config := oidc.Options{
IssuerURL: "https://accounts.google.com",
ClientID: os.Getenv("GOOGLE_CLIENT_ID"),
UsernameClaim: "email",
}
tokenAuthenticator, err := oidc.New(config)
*/
package
oidc
import
(
"context"
"crypto/tls"
"crypto/x509"
"encoding/base64"
"encoding/json"
"errors"
"fmt"
"io/ioutil"
"net/http"
"net/url"
"strings"
"sync"
"sync/atomic"
"time"
oidc
"github.com/coreos/go-oidc"
"k8s.io/klog"
"k8s.io/apimachinery/pkg/util/net"
"k8s.io/apimachinery/pkg/util/wait"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/user"
certutil
"k8s.io/client-go/util/cert"
)
var
(
// synchronizeTokenIDVerifierForTest should be set to true to force a
// wait until the token ID verifiers are ready.
synchronizeTokenIDVerifierForTest
=
false
)
type
Options
struct
{
// IssuerURL is the URL the provider signs ID Tokens as. This will be the "iss"
// field of all tokens produced by the provider and is used for configuration
// discovery.
//
// The URL is usually the provider's URL without a path, for example
// "https://accounts.google.com" or "https://login.salesforce.com".
//
// The provider must implement configuration discovery.
// See: https://openid.net/specs/openid-connect-discovery-1_0.html#ProviderConfig
IssuerURL
string
// ClientID the JWT must be issued for, the "sub" field. This plugin only trusts a single
// client to ensure the plugin can be used with public providers.
//
// The plugin supports the "authorized party" OpenID Connect claim, which allows
// specialized providers to issue tokens to a client for a different client.
// See: https://openid.net/specs/openid-connect-core-1_0.html#IDToken
ClientID
string
// APIAudiences are the audiences that the API server identitifes as. The
// (API audiences unioned with the ClientIDs) should have a non-empty
// intersection with the request's target audience. This preserves the
// behavior of the OIDC authenticator pre-introduction of API audiences.
APIAudiences
authenticator
.
Audiences
// Path to a PEM encoded root certificate of the provider.
CAFile
string
// UsernameClaim is the JWT field to use as the user's username.
UsernameClaim
string
// UsernamePrefix, if specified, causes claims mapping to username to be prefix with
// the provided value. A value "oidc:" would result in usernames like "oidc:john".
UsernamePrefix
string
// GroupsClaim, if specified, causes the OIDCAuthenticator to try to populate the user's
// groups with an ID Token field. If the GroupsClaim field is present in an ID Token the value
// must be a string or list of strings.
GroupsClaim
string
// GroupsPrefix, if specified, causes claims mapping to group names to be prefixed with the
// value. A value "oidc:" would result in groups like "oidc:engineering" and "oidc:marketing".
GroupsPrefix
string
// SupportedSigningAlgs sets the accepted set of JOSE signing algorithms that
// can be used by the provider to sign tokens.
//
// https://tools.ietf.org/html/rfc7518#section-3.1
//
// This value defaults to RS256, the value recommended by the OpenID Connect
// spec:
//
// https://openid.net/specs/openid-connect-core-1_0.html#IDTokenValidation
SupportedSigningAlgs
[]
string
// RequiredClaims, if specified, causes the OIDCAuthenticator to verify that all the
// required claims key value pairs are present in the ID Token.
RequiredClaims
map
[
string
]
string
// now is used for testing. It defaults to time.Now.
now
func
()
time
.
Time
}
// initVerifier creates a new ID token verifier for the given configuration and issuer URL. On success, calls setVerifier with the
// resulting verifier.
func
initVerifier
(
ctx
context
.
Context
,
config
*
oidc
.
Config
,
iss
string
)
(
*
oidc
.
IDTokenVerifier
,
error
)
{
provider
,
err
:=
oidc
.
NewProvider
(
ctx
,
iss
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"init verifier failed: %v"
,
err
)
}
return
provider
.
Verifier
(
config
),
nil
}
// asyncIDTokenVerifier is an ID token verifier that allows async initialization
// of the issuer check. Must be passed by reference as it wraps sync.Mutex.
type
asyncIDTokenVerifier
struct
{
m
sync
.
Mutex
// v is the ID token verifier initialized asynchronously. It remains nil
// up until it is eventually initialized.
// Guarded by m
v
*
oidc
.
IDTokenVerifier
}
// newAsyncIDTokenVerifier creates a new asynchronous token verifier. The
// verifier is available immediately, but may remain uninitialized for some time
// after creation.
func
newAsyncIDTokenVerifier
(
ctx
context
.
Context
,
c
*
oidc
.
Config
,
iss
string
)
*
asyncIDTokenVerifier
{
t
:=
&
asyncIDTokenVerifier
{}
sync
:=
make
(
chan
struct
{})
// Polls indefinitely in an attempt to initialize the distributed claims
// verifier, or until context canceled.
initFn
:=
func
()
(
done
bool
,
err
error
)
{
klog
.
V
(
4
)
.
Infof
(
"oidc authenticator: attempting init: iss=%v"
,
iss
)
v
,
err
:=
initVerifier
(
ctx
,
c
,
iss
)
if
err
!=
nil
{
klog
.
Errorf
(
"oidc authenticator: async token verifier for issuer: %q: %v"
,
iss
,
err
)
return
false
,
nil
}
t
.
m
.
Lock
()
defer
t
.
m
.
Unlock
()
t
.
v
=
v
close
(
sync
)
return
true
,
nil
}
go
func
()
{
if
done
,
_
:=
initFn
();
!
done
{
go
wait
.
PollUntil
(
time
.
Second
*
10
,
initFn
,
ctx
.
Done
())
}
}()
if
synchronizeTokenIDVerifierForTest
{
<-
sync
}
return
t
}
// verifier returns the underlying ID token verifier, or nil if one is not yet initialized.
func
(
a
*
asyncIDTokenVerifier
)
verifier
()
*
oidc
.
IDTokenVerifier
{
a
.
m
.
Lock
()
defer
a
.
m
.
Unlock
()
return
a
.
v
}
type
Authenticator
struct
{
issuerURL
string
usernameClaim
string
usernamePrefix
string
groupsClaim
string
groupsPrefix
string
requiredClaims
map
[
string
]
string
clientIDs
authenticator
.
Audiences
apiAudiences
authenticator
.
Audiences
// Contains an *oidc.IDTokenVerifier. Do not access directly use the
// idTokenVerifier method.
verifier
atomic
.
Value
cancel
context
.
CancelFunc
// resolver is used to resolve distributed claims.
resolver
*
claimResolver
}
func
(
a
*
Authenticator
)
setVerifier
(
v
*
oidc
.
IDTokenVerifier
)
{
a
.
verifier
.
Store
(
v
)
}
func
(
a
*
Authenticator
)
idTokenVerifier
()
(
*
oidc
.
IDTokenVerifier
,
bool
)
{
if
v
:=
a
.
verifier
.
Load
();
v
!=
nil
{
return
v
.
(
*
oidc
.
IDTokenVerifier
),
true
}
return
nil
,
false
}
func
(
a
*
Authenticator
)
Close
()
{
a
.
cancel
()
}
func
New
(
opts
Options
)
(
*
Authenticator
,
error
)
{
return
newAuthenticator
(
opts
,
func
(
ctx
context
.
Context
,
a
*
Authenticator
,
config
*
oidc
.
Config
)
{
// Asynchronously attempt to initialize the authenticator. This enables
// self-hosted providers, providers that run on top of Kubernetes itself.
go
wait
.
PollUntil
(
time
.
Second
*
10
,
func
()
(
done
bool
,
err
error
)
{
provider
,
err
:=
oidc
.
NewProvider
(
ctx
,
a
.
issuerURL
)
if
err
!=
nil
{
klog
.
Errorf
(
"oidc authenticator: initializing plugin: %v"
,
err
)
return
false
,
nil
}
verifier
:=
provider
.
Verifier
(
config
)
a
.
setVerifier
(
verifier
)
return
true
,
nil
},
ctx
.
Done
())
})
}
// whitelist of signing algorithms to ensure users don't mistakenly pass something
// goofy.
var
allowedSigningAlgs
=
map
[
string
]
bool
{
oidc
.
RS256
:
true
,
oidc
.
RS384
:
true
,
oidc
.
RS512
:
true
,
oidc
.
ES256
:
true
,
oidc
.
ES384
:
true
,
oidc
.
ES512
:
true
,
oidc
.
PS256
:
true
,
oidc
.
PS384
:
true
,
oidc
.
PS512
:
true
,
}
func
newAuthenticator
(
opts
Options
,
initVerifier
func
(
ctx
context
.
Context
,
a
*
Authenticator
,
config
*
oidc
.
Config
))
(
*
Authenticator
,
error
)
{
url
,
err
:=
url
.
Parse
(
opts
.
IssuerURL
)
if
err
!=
nil
{
return
nil
,
err
}
if
url
.
Scheme
!=
"https"
{
return
nil
,
fmt
.
Errorf
(
"'oidc-issuer-url' (%q) has invalid scheme (%q), require 'https'"
,
opts
.
IssuerURL
,
url
.
Scheme
)
}
if
opts
.
UsernameClaim
==
""
{
return
nil
,
errors
.
New
(
"no username claim provided"
)
}
supportedSigningAlgs
:=
opts
.
SupportedSigningAlgs
if
len
(
supportedSigningAlgs
)
==
0
{
// RS256 is the default recommended by OpenID Connect and an 'alg' value
// providers are required to implement.
supportedSigningAlgs
=
[]
string
{
oidc
.
RS256
}
}
for
_
,
alg
:=
range
supportedSigningAlgs
{
if
!
allowedSigningAlgs
[
alg
]
{
return
nil
,
fmt
.
Errorf
(
"oidc: unsupported signing alg: %q"
,
alg
)
}
}
var
roots
*
x509
.
CertPool
if
opts
.
CAFile
!=
""
{
roots
,
err
=
certutil
.
NewPool
(
opts
.
CAFile
)
if
err
!=
nil
{
return
nil
,
fmt
.
Errorf
(
"Failed to read the CA file: %v"
,
err
)
}
}
else
{
klog
.
Info
(
"OIDC: No x509 certificates provided, will use host's root CA set"
)
}
// Copied from http.DefaultTransport.
tr
:=
net
.
SetTransportDefaults
(
&
http
.
Transport
{
// According to golang's doc, if RootCAs is nil,
// TLS uses the host's root CA set.
TLSClientConfig
:
&
tls
.
Config
{
RootCAs
:
roots
},
})
client
:=
&
http
.
Client
{
Transport
:
tr
,
Timeout
:
30
*
time
.
Second
}
ctx
,
cancel
:=
context
.
WithCancel
(
context
.
Background
())
ctx
=
oidc
.
ClientContext
(
ctx
,
client
)
now
:=
opts
.
now
if
now
==
nil
{
now
=
time
.
Now
}
verifierConfig
:=
&
oidc
.
Config
{
ClientID
:
opts
.
ClientID
,
SupportedSigningAlgs
:
supportedSigningAlgs
,
Now
:
now
,
}
var
resolver
*
claimResolver
if
opts
.
GroupsClaim
!=
""
{
resolver
=
newClaimResolver
(
opts
.
GroupsClaim
,
client
,
verifierConfig
)
}
authenticator
:=
&
Authenticator
{
issuerURL
:
opts
.
IssuerURL
,
usernameClaim
:
opts
.
UsernameClaim
,
usernamePrefix
:
opts
.
UsernamePrefix
,
groupsClaim
:
opts
.
GroupsClaim
,
groupsPrefix
:
opts
.
GroupsPrefix
,
requiredClaims
:
opts
.
RequiredClaims
,
clientIDs
:
authenticator
.
Audiences
{
opts
.
ClientID
},
apiAudiences
:
opts
.
APIAudiences
,
cancel
:
cancel
,
resolver
:
resolver
,
}
initVerifier
(
ctx
,
authenticator
,
verifierConfig
)
return
authenticator
,
nil
}
// untrustedIssuer extracts an untrusted "iss" claim from the given JWT token,
// or returns an error if the token can not be parsed. Since the JWT is not
// verified, the returned issuer should not be trusted.
func
untrustedIssuer
(
token
string
)
(
string
,
error
)
{
parts
:=
strings
.
Split
(
token
,
"."
)
if
len
(
parts
)
!=
3
{
return
""
,
fmt
.
Errorf
(
"malformed token"
)
}
payload
,
err
:=
base64
.
RawURLEncoding
.
DecodeString
(
parts
[
1
])
if
err
!=
nil
{
return
""
,
fmt
.
Errorf
(
"error decoding token: %v"
,
err
)
}
claims
:=
struct
{
// WARNING: this JWT is not verified. Do not trust these claims.
Issuer
string
`json:"iss"`
}{}
if
err
:=
json
.
Unmarshal
(
payload
,
&
claims
);
err
!=
nil
{
return
""
,
fmt
.
Errorf
(
"while unmarshaling token: %v"
,
err
)
}
// Coalesce the legacy GoogleIss with the new one.
//
// http://openid.net/specs/openid-connect-core-1_0.html#GoogleIss
if
claims
.
Issuer
==
"accounts.google.com"
{
return
"https://accounts.google.com"
,
nil
}
return
claims
.
Issuer
,
nil
}
func
hasCorrectIssuer
(
iss
,
tokenData
string
)
bool
{
uiss
,
err
:=
untrustedIssuer
(
tokenData
)
if
err
!=
nil
{
return
false
}
if
uiss
!=
iss
{
return
false
}
return
true
}
// endpoint represents an OIDC distributed claims endpoint.
type
endpoint
struct
{
// URL to use to request the distributed claim. This URL is expected to be
// prefixed by one of the known issuer URLs.
URL
string
`json:"endpoint,omitempty"`
// AccessToken is the bearer token to use for access. If empty, it is
// not used. Access token is optional per the OIDC distributed claims
// specification.
// See: http://openid.net/specs/openid-connect-core-1_0.html#DistributedExample
AccessToken
string
`json:"access_token,omitempty"`
// JWT is the container for aggregated claims. Not supported at the moment.
// See: http://openid.net/specs/openid-connect-core-1_0.html#AggregatedExample
JWT
string
`json:"JWT,omitempty"`
}
// claimResolver expands distributed claims by calling respective claim source
// endpoints.
type
claimResolver
struct
{
// claim is the distributed claim that may be resolved.
claim
string
// client is the to use for resolving distributed claims
client
*
http
.
Client
// config is the OIDC configuration used for resolving distributed claims.
config
*
oidc
.
Config
// verifierPerIssuer contains, for each issuer, the appropriate verifier to use
// for this claim. It is assumed that there will be very few entries in
// this map.
// Guarded by m.
verifierPerIssuer
map
[
string
]
*
asyncIDTokenVerifier
m
sync
.
Mutex
}
// newClaimResolver creates a new resolver for distributed claims.
func
newClaimResolver
(
claim
string
,
client
*
http
.
Client
,
config
*
oidc
.
Config
)
*
claimResolver
{
return
&
claimResolver
{
claim
:
claim
,
client
:
client
,
config
:
config
,
verifierPerIssuer
:
map
[
string
]
*
asyncIDTokenVerifier
{}}
}
// Verifier returns either the verifier for the specified issuer, or error.
func
(
r
*
claimResolver
)
Verifier
(
iss
string
)
(
*
oidc
.
IDTokenVerifier
,
error
)
{
r
.
m
.
Lock
()
av
:=
r
.
verifierPerIssuer
[
iss
]
if
av
==
nil
{
// This lazy init should normally be very quick.
// TODO: Make this context cancelable.
ctx
:=
oidc
.
ClientContext
(
context
.
Background
(),
r
.
client
)
av
=
newAsyncIDTokenVerifier
(
ctx
,
r
.
config
,
iss
)
r
.
verifierPerIssuer
[
iss
]
=
av
}
r
.
m
.
Unlock
()
v
:=
av
.
verifier
()
if
v
==
nil
{
return
nil
,
fmt
.
Errorf
(
"verifier not initialized for issuer: %q"
,
iss
)
}
return
v
,
nil
}
// expand extracts the distributed claims from claim names and claim sources.
// The extracted claim value is pulled up into the supplied claims.
//
// Distributed claims are of the form as seen below, and are defined in the
// OIDC Connect Core 1.0, section 5.6.2.
// See: https://openid.net/specs/openid-connect-core-1_0.html#AggregatedDistributedClaims
//
// {
// ... (other normal claims)...
// "_claim_names": {
// "groups": "src1"
// },
// "_claim_sources": {
// "src1": {
// "endpoint": "https://www.example.com",
// "access_token": "f005ba11"
// },
// },
// }
func
(
r
*
claimResolver
)
expand
(
c
claims
)
error
{
const
(
// The claim containing a map of endpoint references per claim.
// OIDC Connect Core 1.0, section 5.6.2.
claimNamesKey
=
"_claim_names"
// The claim containing endpoint specifications.
// OIDC Connect Core 1.0, section 5.6.2.
claimSourcesKey
=
"_claim_sources"
)
_
,
ok
:=
c
[
r
.
claim
]
if
ok
{
// There already is a normal claim, skip resolving.
return
nil
}
names
,
ok
:=
c
[
claimNamesKey
]
if
!
ok
{
// No _claim_names, no keys to look up.
return
nil
}
claimToSource
:=
map
[
string
]
string
{}
if
err
:=
json
.
Unmarshal
([]
byte
(
names
),
&
claimToSource
);
err
!=
nil
{
return
fmt
.
Errorf
(
"oidc: error parsing distributed claim names: %v"
,
err
)
}
rawSources
,
ok
:=
c
[
claimSourcesKey
]
if
!
ok
{
// Having _claim_names claim, but no _claim_sources is not an expected
// state.
return
fmt
.
Errorf
(
"oidc: no claim sources"
)
}
var
sources
map
[
string
]
endpoint
if
err
:=
json
.
Unmarshal
([]
byte
(
rawSources
),
&
sources
);
err
!=
nil
{
// The claims sources claim is malformed, this is not an expected state.
return
fmt
.
Errorf
(
"oidc: could not parse claim sources: %v"
,
err
)
}
src
,
ok
:=
claimToSource
[
r
.
claim
]
if
!
ok
{
// No distributed claim present.
return
nil
}
ep
,
ok
:=
sources
[
src
]
if
!
ok
{
return
fmt
.
Errorf
(
"id token _claim_names contained a source %s missing in _claims_sources"
,
src
)
}
if
ep
.
URL
==
""
{
// This is maybe an aggregated claim (ep.JWT != "").
return
nil
}
return
r
.
resolve
(
ep
,
c
)
}
// resolve requests distributed claims from all endpoints passed in,
// and inserts the lookup results into allClaims.
func
(
r
*
claimResolver
)
resolve
(
endpoint
endpoint
,
allClaims
claims
)
error
{
// TODO: cache resolved claims.
jwt
,
err
:=
getClaimJWT
(
r
.
client
,
endpoint
.
URL
,
endpoint
.
AccessToken
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"while getting distributed claim %q: %v"
,
r
.
claim
,
err
)
}
untrustedIss
,
err
:=
untrustedIssuer
(
jwt
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"getting untrusted issuer from endpoint %v failed for claim %q: %v"
,
endpoint
.
URL
,
r
.
claim
,
err
)
}
v
,
err
:=
r
.
Verifier
(
untrustedIss
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"verifying untrusted issuer %v failed: %v"
,
untrustedIss
,
err
)
}
t
,
err
:=
v
.
Verify
(
context
.
Background
(),
jwt
)
if
err
!=
nil
{
return
fmt
.
Errorf
(
"verify distributed claim token: %v"
,
err
)
}
var
distClaims
claims
if
err
:=
t
.
Claims
(
&
distClaims
);
err
!=
nil
{
return
fmt
.
Errorf
(
"could not parse distributed claims for claim %v: %v"
,
r
.
claim
,
err
)
}
value
,
ok
:=
distClaims
[
r
.
claim
]
if
!
ok
{
return
fmt
.
Errorf
(
"jwt returned by distributed claim endpoint %s did not contain claim: %v"
,
endpoint
,
r
.
claim
)
}
allClaims
[
r
.
claim
]
=
value
return
nil
}
func
(
a
*
Authenticator
)
AuthenticateToken
(
ctx
context
.
Context
,
token
string
)
(
*
authenticator
.
Response
,
bool
,
error
)
{
if
reqAuds
,
ok
:=
authenticator
.
AudiencesFrom
(
ctx
);
ok
{
if
len
(
reqAuds
.
Intersect
(
a
.
clientIDs
))
==
0
&&
len
(
reqAuds
.
Intersect
(
a
.
apiAudiences
))
==
0
{
return
nil
,
false
,
nil
}
}
if
!
hasCorrectIssuer
(
a
.
issuerURL
,
token
)
{
return
nil
,
false
,
nil
}
verifier
,
ok
:=
a
.
idTokenVerifier
()
if
!
ok
{
return
nil
,
false
,
fmt
.
Errorf
(
"oidc: authenticator not initialized"
)
}
idToken
,
err
:=
verifier
.
Verify
(
ctx
,
token
)
if
err
!=
nil
{
return
nil
,
false
,
fmt
.
Errorf
(
"oidc: verify token: %v"
,
err
)
}
var
c
claims
if
err
:=
idToken
.
Claims
(
&
c
);
err
!=
nil
{
return
nil
,
false
,
fmt
.
Errorf
(
"oidc: parse claims: %v"
,
err
)
}
if
a
.
resolver
!=
nil
{
if
err
:=
a
.
resolver
.
expand
(
c
);
err
!=
nil
{
return
nil
,
false
,
fmt
.
Errorf
(
"oidc: could not expand distributed claims: %v"
,
err
)
}
}
var
username
string
if
err
:=
c
.
unmarshalClaim
(
a
.
usernameClaim
,
&
username
);
err
!=
nil
{
return
nil
,
false
,
fmt
.
Errorf
(
"oidc: parse username claims %q: %v"
,
a
.
usernameClaim
,
err
)
}
if
a
.
usernameClaim
==
"email"
{
// If the email_verified claim is present, ensure the email is valid.
// https://openid.net/specs/openid-connect-core-1_0.html#StandardClaims
if
hasEmailVerified
:=
c
.
hasClaim
(
"email_verified"
);
hasEmailVerified
{
var
emailVerified
bool
if
err
:=
c
.
unmarshalClaim
(
"email_verified"
,
&
emailVerified
);
err
!=
nil
{
return
nil
,
false
,
fmt
.
Errorf
(
"oidc: parse 'email_verified' claim: %v"
,
err
)
}
// If the email_verified claim is present we have to verify it is set to `true`.
if
!
emailVerified
{
return
nil
,
false
,
fmt
.
Errorf
(
"oidc: email not verified"
)
}
}
}
if
a
.
usernamePrefix
!=
""
{
username
=
a
.
usernamePrefix
+
username
}
info
:=
&
user
.
DefaultInfo
{
Name
:
username
}
if
a
.
groupsClaim
!=
""
{
if
_
,
ok
:=
c
[
a
.
groupsClaim
];
ok
{
// Some admins want to use string claims like "role" as the group value.
// Allow the group claim to be a single string instead of an array.
//
// See: https://github.com/kubernetes/kubernetes/issues/33290
var
groups
stringOrArray
if
err
:=
c
.
unmarshalClaim
(
a
.
groupsClaim
,
&
groups
);
err
!=
nil
{
return
nil
,
false
,
fmt
.
Errorf
(
"oidc: parse groups claim %q: %v"
,
a
.
groupsClaim
,
err
)
}
info
.
Groups
=
[]
string
(
groups
)
}
}
if
a
.
groupsPrefix
!=
""
{
for
i
,
group
:=
range
info
.
Groups
{
info
.
Groups
[
i
]
=
a
.
groupsPrefix
+
group
}
}
// check to ensure all required claims are present in the ID token and have matching values.
for
claim
,
value
:=
range
a
.
requiredClaims
{
if
!
c
.
hasClaim
(
claim
)
{
return
nil
,
false
,
fmt
.
Errorf
(
"oidc: required claim %s not present in ID token"
,
claim
)
}
// NOTE: Only string values are supported as valid required claim values.
var
claimValue
string
if
err
:=
c
.
unmarshalClaim
(
claim
,
&
claimValue
);
err
!=
nil
{
return
nil
,
false
,
fmt
.
Errorf
(
"oidc: parse claim %s: %v"
,
claim
,
err
)
}
if
claimValue
!=
value
{
return
nil
,
false
,
fmt
.
Errorf
(
"oidc: required claim %s value does not match. Got = %s, want = %s"
,
claim
,
claimValue
,
value
)
}
}
return
&
authenticator
.
Response
{
User
:
info
},
true
,
nil
}
// getClaimJWT gets a distributed claim JWT from url, using the supplied access
// token as bearer token. If the access token is "", the authorization header
// will not be set.
// TODO: Allow passing in JSON hints to the IDP.
func
getClaimJWT
(
client
*
http
.
Client
,
url
,
accessToken
string
)
(
string
,
error
)
{
ctx
,
cancel
:=
context
.
WithCancel
(
context
.
Background
())
defer
cancel
()
// TODO: Allow passing request body with configurable information.
req
,
err
:=
http
.
NewRequest
(
"GET"
,
url
,
nil
)
if
err
!=
nil
{
return
""
,
fmt
.
Errorf
(
"while calling %v: %v"
,
url
,
err
)
}
if
accessToken
!=
""
{
req
.
Header
.
Set
(
"Authorization"
,
fmt
.
Sprintf
(
"Bearer %v"
,
accessToken
))
}
req
=
req
.
WithContext
(
ctx
)
response
,
err
:=
client
.
Do
(
req
)
if
err
!=
nil
{
return
""
,
err
}
// Report non-OK status code as an error.
if
response
.
StatusCode
<
http
.
StatusOK
||
response
.
StatusCode
>
http
.
StatusIMUsed
{
return
""
,
fmt
.
Errorf
(
"error while getting distributed claim JWT: %v"
,
response
.
Status
)
}
defer
response
.
Body
.
Close
()
responseBytes
,
err
:=
ioutil
.
ReadAll
(
response
.
Body
)
if
err
!=
nil
{
return
""
,
fmt
.
Errorf
(
"could not decode distributed claim response"
)
}
return
string
(
responseBytes
),
nil
}
type
stringOrArray
[]
string
func
(
s
*
stringOrArray
)
UnmarshalJSON
(
b
[]
byte
)
error
{
var
a
[]
string
if
err
:=
json
.
Unmarshal
(
b
,
&
a
);
err
==
nil
{
*
s
=
a
return
nil
}
var
str
string
if
err
:=
json
.
Unmarshal
(
b
,
&
str
);
err
!=
nil
{
return
err
}
*
s
=
[]
string
{
str
}
return
nil
}
type
claims
map
[
string
]
json
.
RawMessage
func
(
c
claims
)
unmarshalClaim
(
name
string
,
v
interface
{})
error
{
val
,
ok
:=
c
[
name
]
if
!
ok
{
return
fmt
.
Errorf
(
"claim not present"
)
}
return
json
.
Unmarshal
([]
byte
(
val
),
v
)
}
func
(
c
claims
)
hasClaim
(
name
string
)
bool
{
if
_
,
ok
:=
c
[
name
];
!
ok
{
return
false
}
return
true
}
staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/oidc_test.go
deleted
100644 → 0
View file @
c691249e
/*
Copyright 2015 The Kubernetes Authors.
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
*/
package
oidc
import
(
"bytes"
"context"
"crypto"
"crypto/x509"
"encoding/hex"
"encoding/json"
"encoding/pem"
"fmt"
"io/ioutil"
"net/http"
"net/http/httptest"
"os"
"reflect"
"strings"
"testing"
"text/template"
"time"
oidc
"github.com/coreos/go-oidc"
jose
"gopkg.in/square/go-jose.v2"
"k8s.io/apiserver/pkg/authentication/authenticator"
"k8s.io/apiserver/pkg/authentication/user"
"k8s.io/klog"
)
// utilities for loading JOSE keys.
func
loadRSAKey
(
t
*
testing
.
T
,
filepath
string
,
alg
jose
.
SignatureAlgorithm
)
*
jose
.
JSONWebKey
{
return
loadKey
(
t
,
filepath
,
alg
,
func
(
b
[]
byte
)
(
interface
{},
error
)
{
key
,
err
:=
x509
.
ParsePKCS1PrivateKey
(
b
)
if
err
!=
nil
{
return
nil
,
err
}
return
key
.
Public
(),
nil
})
}
func
loadRSAPrivKey
(
t
*
testing
.
T
,
filepath
string
,
alg
jose
.
SignatureAlgorithm
)
*
jose
.
JSONWebKey
{
return
loadKey
(
t
,
filepath
,
alg
,
func
(
b
[]
byte
)
(
interface
{},
error
)
{
return
x509
.
ParsePKCS1PrivateKey
(
b
)
})
}
func
loadECDSAKey
(
t
*
testing
.
T
,
filepath
string
,
alg
jose
.
SignatureAlgorithm
)
*
jose
.
JSONWebKey
{
return
loadKey
(
t
,
filepath
,
alg
,
func
(
b
[]
byte
)
(
interface
{},
error
)
{
key
,
err
:=
x509
.
ParseECPrivateKey
(
b
)
if
err
!=
nil
{
return
nil
,
err
}
return
key
.
Public
(),
nil
})
}
func
loadECDSAPrivKey
(
t
*
testing
.
T
,
filepath
string
,
alg
jose
.
SignatureAlgorithm
)
*
jose
.
JSONWebKey
{
return
loadKey
(
t
,
filepath
,
alg
,
func
(
b
[]
byte
)
(
interface
{},
error
)
{
return
x509
.
ParseECPrivateKey
(
b
)
})
}
func
loadKey
(
t
*
testing
.
T
,
filepath
string
,
alg
jose
.
SignatureAlgorithm
,
unmarshal
func
([]
byte
)
(
interface
{},
error
))
*
jose
.
JSONWebKey
{
data
,
err
:=
ioutil
.
ReadFile
(
filepath
)
if
err
!=
nil
{
t
.
Fatalf
(
"load file: %v"
,
err
)
}
block
,
_
:=
pem
.
Decode
(
data
)
if
block
==
nil
{
t
.
Fatalf
(
"file contained no PEM encoded data: %s"
,
filepath
)
}
priv
,
err
:=
unmarshal
(
block
.
Bytes
)
if
err
!=
nil
{
t
.
Fatalf
(
"unmarshal key: %v"
,
err
)
}
key
:=
&
jose
.
JSONWebKey
{
Key
:
priv
,
Use
:
"sig"
,
Algorithm
:
string
(
alg
)}
thumbprint
,
err
:=
key
.
Thumbprint
(
crypto
.
SHA256
)
if
err
!=
nil
{
t
.
Fatalf
(
"computing thumbprint: %v"
,
err
)
}
key
.
KeyID
=
hex
.
EncodeToString
(
thumbprint
)
return
key
}
// staticKeySet implements oidc.KeySet.
type
staticKeySet
struct
{
keys
[]
*
jose
.
JSONWebKey
}
func
(
s
*
staticKeySet
)
VerifySignature
(
ctx
context
.
Context
,
jwt
string
)
(
payload
[]
byte
,
err
error
)
{
jws
,
err
:=
jose
.
ParseSigned
(
jwt
)
if
err
!=
nil
{
return
nil
,
err
}
if
len
(
jws
.
Signatures
)
==
0
{
return
nil
,
fmt
.
Errorf
(
"jwt contained no signatures"
)
}
kid
:=
jws
.
Signatures
[
0
]
.
Header
.
KeyID
for
_
,
key
:=
range
s
.
keys
{
if
key
.
KeyID
==
kid
{
return
jws
.
Verify
(
key
)
}
}
return
nil
,
fmt
.
Errorf
(
"no keys matches jwk keyid"
)
}
var
(
expired
,
_
=
time
.
Parse
(
time
.
RFC3339Nano
,
"2009-11-10T22:00:00Z"
)
now
,
_
=
time
.
Parse
(
time
.
RFC3339Nano
,
"2009-11-10T23:00:00Z"
)
valid
,
_
=
time
.
Parse
(
time
.
RFC3339Nano
,
"2009-11-11T00:00:00Z"
)
)
type
claimsTest
struct
{
name
string
options
Options
now
time
.
Time
signingKey
*
jose
.
JSONWebKey
pubKeys
[]
*
jose
.
JSONWebKey
claims
string
want
*
user
.
DefaultInfo
wantSkip
bool
wantErr
bool
wantInitErr
bool
claimToResponseMap
map
[
string
]
string
openIDConfig
string
reqAudiences
authenticator
.
Audiences
}
// Replace formats the contents of v into the provided template.
func
replace
(
tmpl
string
,
v
interface
{})
string
{
t
:=
template
.
Must
(
template
.
New
(
"test"
)
.
Parse
(
tmpl
))
buf
:=
bytes
.
NewBuffer
(
nil
)
t
.
Execute
(
buf
,
&
v
)
ret
:=
buf
.
String
()
klog
.
V
(
4
)
.
Infof
(
"Replaced: %v into: %v"
,
tmpl
,
ret
)
return
ret
}
// newClaimServer returns a new test HTTPS server, which is rigged to return
// OIDC responses to requests that resolve distributed claims. signer is the
// signer used for the served JWT tokens. claimToResponseMap is a map of
// responses that the server will return for each claim it is given.
func
newClaimServer
(
t
*
testing
.
T
,
keys
jose
.
JSONWebKeySet
,
signer
jose
.
Signer
,
claimToResponseMap
map
[
string
]
string
,
openIDConfig
*
string
)
*
httptest
.
Server
{
ts
:=
httptest
.
NewTLSServer
(
http
.
HandlerFunc
(
func
(
w
http
.
ResponseWriter
,
r
*
http
.
Request
)
{
klog
.
V
(
5
)
.
Infof
(
"request: %+v"
,
*
r
)
switch
r
.
URL
.
Path
{
case
"/.testing/keys"
:
w
.
Header
()
.
Set
(
"Content-Type"
,
"application/json"
)
keyBytes
,
err
:=
json
.
Marshal
(
keys
)
if
err
!=
nil
{
t
.
Fatalf
(
"unexpected error while marshaling keys: %v"
,
err
)
}
klog
.
V
(
5
)
.
Infof
(
"%v: returning: %+v"
,
r
.
URL
,
string
(
keyBytes
))
w
.
Write
(
keyBytes
)
case
"/.well-known/openid-configuration"
:
w
.
Header
()
.
Set
(
"Content-Type"
,
"application/json"
)
klog
.
V
(
5
)
.
Infof
(
"%v: returning: %+v"
,
r
.
URL
,
*
openIDConfig
)
w
.
Write
([]
byte
(
*
openIDConfig
))
// These claims are tested in the unit tests.
case
"/groups"
:
fallthrough
case
"/rabbits"
:
if
claimToResponseMap
==
nil
{
t
.
Errorf
(
"no claims specified in response"
)
}
claim
:=
r
.
URL
.
Path
[
1
:
]
// "/groups" -> "groups"
expectedAuth
:=
fmt
.
Sprintf
(
"Bearer %v_token"
,
claim
)
auth
:=
r
.
Header
.
Get
(
"Authorization"
)
if
auth
!=
expectedAuth
{
t
.
Errorf
(
"bearer token expected: %q, was %q"
,
expectedAuth
,
auth
)
}
jws
,
err
:=
signer
.
Sign
([]
byte
(
claimToResponseMap
[
claim
]))
if
err
!=
nil
{
t
.
Errorf
(
"while signing response token: %v"
,
err
)
}
token
,
err
:=
jws
.
CompactSerialize
()
if
err
!=
nil
{
t
.
Errorf
(
"while serializing response token: %v"
,
err
)
}
w
.
Write
([]
byte
(
token
))
default
:
w
.
WriteHeader
(
http
.
StatusNotFound
)
fmt
.
Fprintf
(
w
,
"unexpected URL: %v"
,
r
.
URL
)
}
}))
klog
.
V
(
4
)
.
Infof
(
"Serving OIDC at: %v"
,
ts
.
URL
)
return
ts
}
// writeTempCert writes out the supplied certificate into a temporary file in
// PEM-encoded format. Returns the name of the temporary file used. The caller
// is responsible for cleaning the file up.
func
writeTempCert
(
t
*
testing
.
T
,
cert
[]
byte
)
string
{
tempFile
,
err
:=
ioutil
.
TempFile
(
""
,
"ca.crt"
)
if
err
!=
nil
{
t
.
Fatalf
(
"could not open temp file: %v"
,
err
)
}
block
:=
&
pem
.
Block
{
Type
:
"CERTIFICATE"
,
Bytes
:
cert
,
}
if
err
:=
pem
.
Encode
(
tempFile
,
block
);
err
!=
nil
{
t
.
Fatalf
(
"could not write to temp file %v: %v"
,
tempFile
.
Name
(),
err
)
}
tempFile
.
Close
()
return
tempFile
.
Name
()
}
func
toKeySet
(
keys
[]
*
jose
.
JSONWebKey
)
jose
.
JSONWebKeySet
{
ret
:=
jose
.
JSONWebKeySet
{}
for
_
,
k
:=
range
keys
{
ret
.
Keys
=
append
(
ret
.
Keys
,
*
k
)
}
return
ret
}
func
(
c
*
claimsTest
)
run
(
t
*
testing
.
T
)
{
var
(
signer
jose
.
Signer
err
error
)
if
c
.
signingKey
!=
nil
{
// Initialize the signer only in the tests that make use of it. We can
// not defer this initialization because the test server uses it too.
signer
,
err
=
jose
.
NewSigner
(
jose
.
SigningKey
{
Algorithm
:
jose
.
SignatureAlgorithm
(
c
.
signingKey
.
Algorithm
),
Key
:
c
.
signingKey
,
},
nil
)
if
err
!=
nil
{
t
.
Fatalf
(
"initialize signer: %v"
,
err
)
}
}
// The HTTPS server used for requesting distributed groups claims.
ts
:=
newClaimServer
(
t
,
toKeySet
(
c
.
pubKeys
),
signer
,
c
.
claimToResponseMap
,
&
c
.
openIDConfig
)
defer
ts
.
Close
()
// Make the certificate of the helper server available to the authenticator
// by writing its root CA certificate into a temporary file.
tempFileName
:=
writeTempCert
(
t
,
ts
.
TLS
.
Certificates
[
0
]
.
Certificate
[
0
])
defer
os
.
Remove
(
tempFileName
)
c
.
options
.
CAFile
=
tempFileName
// Allow claims to refer to the serving URL of the test server. For this,
// substitute all references to {{.URL}} in appropriate places.
v
:=
struct
{
URL
string
}{
URL
:
ts
.
URL
}
c
.
claims
=
replace
(
c
.
claims
,
&
v
)
c
.
openIDConfig
=
replace
(
c
.
openIDConfig
,
&
v
)
c
.
options
.
IssuerURL
=
replace
(
c
.
options
.
IssuerURL
,
&
v
)
for
claim
,
response
:=
range
c
.
claimToResponseMap
{
c
.
claimToResponseMap
[
claim
]
=
replace
(
response
,
&
v
)
}
// Initialize the authenticator.
a
,
err
:=
newAuthenticator
(
c
.
options
,
func
(
ctx
context
.
Context
,
a
*
Authenticator
,
config
*
oidc
.
Config
)
{
// Set the verifier to use the public key set instead of reading from a remote.
a
.
setVerifier
(
oidc
.
NewVerifier
(
c
.
options
.
IssuerURL
,
&
staticKeySet
{
keys
:
c
.
pubKeys
},
config
,
))
})
if
err
!=
nil
{
if
!
c
.
wantInitErr
{
t
.
Fatalf
(
"initialize authenticator: %v"
,
err
)
}
return
}
if
c
.
wantInitErr
{
t
.
Fatalf
(
"wanted initialization error"
)
}
// Sign and serialize the claims in a JWT.
jws
,
err
:=
signer
.
Sign
([]
byte
(
c
.
claims
))
if
err
!=
nil
{
t
.
Fatalf
(
"sign claims: %v"
,
err
)
}
token
,
err
:=
jws
.
CompactSerialize
()
if
err
!=
nil
{
t
.
Fatalf
(
"serialize token: %v"
,
err
)
}
ctx
:=
context
.
Background
()
if
c
.
reqAudiences
!=
nil
{
ctx
=
authenticator
.
WithAudiences
(
ctx
,
c
.
reqAudiences
)
}
got
,
ok
,
err
:=
a
.
AuthenticateToken
(
ctx
,
token
)
if
err
!=
nil
{
if
!
c
.
wantErr
{
t
.
Fatalf
(
"authenticate token: %v"
,
err
)
}
return
}
if
c
.
wantErr
{
t
.
Fatalf
(
"expected error authenticating token"
)
}
if
!
ok
{
if
!
c
.
wantSkip
{
// We don't have any cases where we return (nil, false, nil)
t
.
Fatalf
(
"no error but token not authenticated"
)
}
return
}
if
c
.
wantSkip
{
t
.
Fatalf
(
"expected authenticator to skip token"
)
}
gotUser
:=
got
.
User
.
(
*
user
.
DefaultInfo
)
if
!
reflect
.
DeepEqual
(
gotUser
,
c
.
want
)
{
t
.
Fatalf
(
"wanted user=%#v, got=%#v"
,
c
.
want
,
gotUser
)
}
}
func
TestToken
(
t
*
testing
.
T
)
{
synchronizeTokenIDVerifierForTest
=
true
tests
:=
[]
claimsTest
{
{
name
:
"token"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
},
},
{
name
:
"no-username"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"exp": %d
}`
,
valid
.
Unix
()),
wantErr
:
true
,
},
{
name
:
"email"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"email"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"email": "jane@example.com",
"email_verified": true,
"exp": %d
}`
,
valid
.
Unix
()),
want
:
&
user
.
DefaultInfo
{
Name
:
"jane@example.com"
,
},
},
{
name
:
"email-not-verified"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"email"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"email": "jane@example.com",
"email_verified": false,
"exp": %d
}`
,
valid
.
Unix
()),
wantErr
:
true
,
},
{
// If "email_verified" isn't present, assume true
name
:
"no-email-verified-claim"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"email"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"email": "jane@example.com",
"exp": %d
}`
,
valid
.
Unix
()),
want
:
&
user
.
DefaultInfo
{
Name
:
"jane@example.com"
,
},
},
{
name
:
"invalid-email-verified-claim"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"email"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
// string value for "email_verified"
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"email": "jane@example.com",
"email_verified": "false",
"exp": %d
}`
,
valid
.
Unix
()),
wantErr
:
true
,
},
{
name
:
"groups"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
GroupsClaim
:
"groups"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"groups": ["team1", "team2"],
"exp": %d
}`
,
valid
.
Unix
()),
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
Groups
:
[]
string
{
"team1"
,
"team2"
},
},
},
{
name
:
"groups-distributed"
,
options
:
Options
{
IssuerURL
:
"{{.URL}}"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
GroupsClaim
:
"groups"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "{{.URL}}",
"aud": "my-client",
"username": "jane",
"_claim_names": {
"groups": "src1"
},
"_claim_sources": {
"src1": {
"endpoint": "{{.URL}}/groups",
"access_token": "groups_token"
}
},
"exp": %d
}`
,
valid
.
Unix
()),
claimToResponseMap
:
map
[
string
]
string
{
"groups"
:
fmt
.
Sprintf
(
`{
"iss": "{{.URL}}",
"aud": "my-client",
"groups": ["team1", "team2"],
"exp": %d
}`
,
valid
.
Unix
()),
},
openIDConfig
:
`{
"issuer": "{{.URL}}",
"jwks_uri": "{{.URL}}/.testing/keys"
}`
,
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
Groups
:
[]
string
{
"team1"
,
"team2"
},
},
},
{
name
:
"groups-distributed-malformed-claim-names"
,
options
:
Options
{
IssuerURL
:
"{{.URL}}"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
GroupsClaim
:
"groups"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "{{.URL}}",
"aud": "my-client",
"username": "jane",
"_claim_names": {
"groups": "nonexistent-claim-source"
},
"_claim_sources": {
"src1": {
"endpoint": "{{.URL}}/groups",
"access_token": "groups_token"
}
},
"exp": %d
}`
,
valid
.
Unix
()),
claimToResponseMap
:
map
[
string
]
string
{
"groups"
:
fmt
.
Sprintf
(
`{
"iss": "{{.URL}}",
"aud": "my-client",
"groups": ["team1", "team2"],
"exp": %d
}`
,
valid
.
Unix
()),
},
openIDConfig
:
`{
"issuer": "{{.URL}}",
"jwks_uri": "{{.URL}}/.testing/keys"
}`
,
wantErr
:
true
,
},
{
name
:
"groups-distributed-malformed-names-and-sources"
,
options
:
Options
{
IssuerURL
:
"{{.URL}}"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
GroupsClaim
:
"groups"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "{{.URL}}",
"aud": "my-client",
"username": "jane",
"_claim_names": {
"groups": "src1"
},
"exp": %d
}`
,
valid
.
Unix
()),
claimToResponseMap
:
map
[
string
]
string
{
"groups"
:
fmt
.
Sprintf
(
`{
"iss": "{{.URL}}",
"aud": "my-client",
"groups": ["team1", "team2"],
"exp": %d
}`
,
valid
.
Unix
()),
},
openIDConfig
:
`{
"issuer": "{{.URL}}",
"jwks_uri": "{{.URL}}/.testing/keys"
}`
,
wantErr
:
true
,
},
{
name
:
"groups-distributed-malformed-distributed-claim"
,
options
:
Options
{
IssuerURL
:
"{{.URL}}"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
GroupsClaim
:
"groups"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "{{.URL}}",
"aud": "my-client",
"username": "jane",
"_claim_names": {
"groups": "src1"
},
"_claim_sources": {
"src1": {
"endpoint": "{{.URL}}/groups",
"access_token": "groups_token"
}
},
"exp": %d
}`
,
valid
.
Unix
()),
claimToResponseMap
:
map
[
string
]
string
{
// Doesn't contain the "groups" claim as it promises.
"groups"
:
fmt
.
Sprintf
(
`{
"iss": "{{.URL}}",
"aud": "my-client",
"exp": %d
}`
,
valid
.
Unix
()),
},
openIDConfig
:
`{
"issuer": "{{.URL}}",
"jwks_uri": "{{.URL}}/.testing/keys"
}`
,
wantErr
:
true
,
},
{
name
:
"groups-distributed-unusual-name"
,
options
:
Options
{
IssuerURL
:
"{{.URL}}"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
GroupsClaim
:
"rabbits"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "{{.URL}}",
"aud": "my-client",
"username": "jane",
"_claim_names": {
"rabbits": "src1"
},
"_claim_sources": {
"src1": {
"endpoint": "{{.URL}}/rabbits",
"access_token": "rabbits_token"
}
},
"exp": %d
}`
,
valid
.
Unix
()),
claimToResponseMap
:
map
[
string
]
string
{
"rabbits"
:
fmt
.
Sprintf
(
`{
"iss": "{{.URL}}",
"aud": "my-client",
"rabbits": ["team1", "team2"],
"exp": %d
}`
,
valid
.
Unix
()),
},
openIDConfig
:
`{
"issuer": "{{.URL}}",
"jwks_uri": "{{.URL}}/.testing/keys"
}`
,
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
Groups
:
[]
string
{
"team1"
,
"team2"
},
},
},
{
name
:
"groups-distributed-wrong-audience"
,
options
:
Options
{
IssuerURL
:
"{{.URL}}"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
GroupsClaim
:
"groups"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "{{.URL}}",
"aud": "my-client",
"username": "jane",
"_claim_names": {
"groups": "src1"
},
"_claim_sources": {
"src1": {
"endpoint": "{{.URL}}/groups",
"access_token": "groups_token"
}
},
"exp": %d
}`
,
valid
.
Unix
()),
claimToResponseMap
:
map
[
string
]
string
{
// Note mismatching "aud"
"groups"
:
fmt
.
Sprintf
(
`{
"iss": "{{.URL}}",
"aud": "your-client",
"groups": ["team1", "team2"],
"exp": %d
}`
,
valid
.
Unix
()),
},
openIDConfig
:
`{
"issuer": "{{.URL}}",
"jwks_uri": "{{.URL}}/.testing/keys"
}`
,
// "aud" was "your-client", not "my-client"
wantErr
:
true
,
},
{
name
:
"groups-distributed-wrong-audience"
,
options
:
Options
{
IssuerURL
:
"{{.URL}}"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
GroupsClaim
:
"groups"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "{{.URL}}",
"aud": "my-client",
"username": "jane",
"_claim_names": {
"groups": "src1"
},
"_claim_sources": {
"src1": {
"endpoint": "{{.URL}}/groups",
"access_token": "groups_token"
}
},
"exp": %d
}`
,
valid
.
Unix
()),
claimToResponseMap
:
map
[
string
]
string
{
// Note expired timestamp.
"groups"
:
fmt
.
Sprintf
(
`{
"iss": "{{.URL}}",
"aud": "my-client",
"groups": ["team1", "team2"],
"exp": %d
}`
,
expired
.
Unix
()),
},
openIDConfig
:
`{
"issuer": "{{.URL}}",
"jwks_uri": "{{.URL}}/.testing/keys"
}`
,
// The distributed token is expired.
wantErr
:
true
,
},
{
// Specs are unclear about this behavior. We adopt a behavior where
// normal claim wins over a distributed claim by the same name.
name
:
"groups-distributed-normal-claim-wins"
,
options
:
Options
{
IssuerURL
:
"{{.URL}}"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
GroupsClaim
:
"groups"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "{{.URL}}",
"aud": "my-client",
"username": "jane",
"groups": "team1",
"_claim_names": {
"groups": "src1"
},
"_claim_sources": {
"src1": {
"endpoint": "{{.URL}}/groups",
"access_token": "groups_token"
}
},
"exp": %d
}`
,
valid
.
Unix
()),
claimToResponseMap
:
map
[
string
]
string
{
"groups"
:
fmt
.
Sprintf
(
`{
"iss": "{{.URL}}",
"aud": "my-client",
"groups": ["team2"],
"exp": %d
}`
,
valid
.
Unix
()),
},
openIDConfig
:
`{
"issuer": "{{.URL}}",
"jwks_uri": "{{.URL}}/.testing/keys"
}`
,
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
// "team1" is from the normal "groups" claim.
Groups
:
[]
string
{
"team1"
},
},
},
{
// Groups should be able to be a single string, not just a slice.
name
:
"group-string-claim"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
GroupsClaim
:
"groups"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"groups": "team1",
"exp": %d
}`
,
valid
.
Unix
()),
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
Groups
:
[]
string
{
"team1"
},
},
},
{
// Groups should be able to be a single string, not just a slice.
name
:
"group-string-claim-distributed"
,
options
:
Options
{
IssuerURL
:
"{{.URL}}"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
GroupsClaim
:
"groups"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "{{.URL}}",
"aud": "my-client",
"username": "jane",
"_claim_names": {
"groups": "src1"
},
"_claim_sources": {
"src1": {
"endpoint": "{{.URL}}/groups",
"access_token": "groups_token"
}
},
"exp": %d
}`
,
valid
.
Unix
()),
claimToResponseMap
:
map
[
string
]
string
{
"groups"
:
fmt
.
Sprintf
(
`{
"iss": "{{.URL}}",
"aud": "my-client",
"groups": "team1",
"exp": %d
}`
,
valid
.
Unix
()),
},
openIDConfig
:
`{
"issuer": "{{.URL}}",
"jwks_uri": "{{.URL}}/.testing/keys"
}`
,
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
Groups
:
[]
string
{
"team1"
},
},
},
{
name
:
"group-string-claim-aggregated-not-supported"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
GroupsClaim
:
"groups"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"_claim_names": {
"groups": "src1"
},
"_claim_sources": {
"src1": {
"JWT": "some.jwt.token"
}
},
"exp": %d
}`
,
valid
.
Unix
()),
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
},
},
{
// if the groups claim isn't provided, this shouldn't error out
name
:
"no-groups-claim"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
GroupsClaim
:
"groups"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
},
},
{
name
:
"invalid-groups-claim"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
GroupsClaim
:
"groups"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"groups": 42,
"exp": %d
}`
,
valid
.
Unix
()),
wantErr
:
true
,
},
{
name
:
"required-claim"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
GroupsClaim
:
"groups"
,
RequiredClaims
:
map
[
string
]
string
{
"hd"
:
"example.com"
,
"sub"
:
"test"
,
},
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"hd": "example.com",
"sub": "test",
"exp": %d
}`
,
valid
.
Unix
()),
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
},
},
{
name
:
"no-required-claim"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
GroupsClaim
:
"groups"
,
RequiredClaims
:
map
[
string
]
string
{
"hd"
:
"example.com"
,
},
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
wantErr
:
true
,
},
{
name
:
"invalid-required-claim"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
GroupsClaim
:
"groups"
,
RequiredClaims
:
map
[
string
]
string
{
"hd"
:
"example.com"
,
},
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"hd": "example.org",
"exp": %d
}`
,
valid
.
Unix
()),
wantErr
:
true
,
},
{
name
:
"invalid-signature"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_2.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
wantErr
:
true
,
},
{
name
:
"expired"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
expired
.
Unix
()),
wantErr
:
true
,
},
{
name
:
"invalid-aud"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "not-my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
wantErr
:
true
,
},
{
// ID tokens may contain multiple audiences:
// https://openid.net/specs/openid-connect-core-1_0.html#IDToken
name
:
"multiple-audiences"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": ["not-my-client", "my-client"],
"azp": "not-my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
},
},
{
name
:
"invalid-issuer"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
wantSkip
:
true
,
},
{
name
:
"username-prefix"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
UsernamePrefix
:
"oidc:"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
want
:
&
user
.
DefaultInfo
{
Name
:
"oidc:jane"
,
},
},
{
name
:
"groups-prefix"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
UsernamePrefix
:
"oidc:"
,
GroupsClaim
:
"groups"
,
GroupsPrefix
:
"groups:"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"groups": ["team1", "team2"],
"exp": %d
}`
,
valid
.
Unix
()),
want
:
&
user
.
DefaultInfo
{
Name
:
"oidc:jane"
,
Groups
:
[]
string
{
"groups:team1"
,
"groups:team2"
},
},
},
{
name
:
"groups-prefix-distributed"
,
options
:
Options
{
IssuerURL
:
"{{.URL}}"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
UsernamePrefix
:
"oidc:"
,
GroupsClaim
:
"groups"
,
GroupsPrefix
:
"groups:"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "{{.URL}}",
"aud": "my-client",
"username": "jane",
"_claim_names": {
"groups": "src1"
},
"_claim_sources": {
"src1": {
"endpoint": "{{.URL}}/groups",
"access_token": "groups_token"
}
},
"exp": %d
}`
,
valid
.
Unix
()),
claimToResponseMap
:
map
[
string
]
string
{
"groups"
:
fmt
.
Sprintf
(
`{
"iss": "{{.URL}}",
"aud": "my-client",
"groups": ["team1", "team2"],
"exp": %d
}`
,
valid
.
Unix
()),
},
openIDConfig
:
`{
"issuer": "{{.URL}}",
"jwks_uri": "{{.URL}}/.testing/keys"
}`
,
want
:
&
user
.
DefaultInfo
{
Name
:
"oidc:jane"
,
Groups
:
[]
string
{
"groups:team1"
,
"groups:team2"
},
},
},
{
name
:
"invalid-signing-alg"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
now
:
func
()
time
.
Time
{
return
now
},
},
// Correct key but invalid signature algorithm "PS256"
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
PS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
wantErr
:
true
,
},
{
name
:
"ps256"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
SupportedSigningAlgs
:
[]
string
{
"PS256"
},
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
PS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
PS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
},
},
{
name
:
"es512"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
SupportedSigningAlgs
:
[]
string
{
"ES512"
},
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadECDSAPrivKey
(
t
,
"testdata/ecdsa_2.pem"
,
jose
.
ES512
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadECDSAKey
(
t
,
"testdata/ecdsa_1.pem"
,
jose
.
ES512
),
loadECDSAKey
(
t
,
"testdata/ecdsa_2.pem"
,
jose
.
ES512
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
},
},
{
name
:
"not-https"
,
options
:
Options
{
IssuerURL
:
"http://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
now
:
func
()
time
.
Time
{
return
now
},
},
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
wantInitErr
:
true
,
},
{
name
:
"no-username-claim"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
now
:
func
()
time
.
Time
{
return
now
},
},
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
wantInitErr
:
true
,
},
{
name
:
"invalid-sig-alg"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"username"
,
SupportedSigningAlgs
:
[]
string
{
"HS256"
},
now
:
func
()
time
.
Time
{
return
now
},
},
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
wantInitErr
:
true
,
},
{
name
:
"accounts.google.com issuer"
,
options
:
Options
{
IssuerURL
:
"https://accounts.google.com"
,
ClientID
:
"my-client"
,
UsernameClaim
:
"email"
,
now
:
func
()
time
.
Time
{
return
now
},
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "accounts.google.com",
"email": "thomas.jefferson@gmail.com",
"aud": "my-client",
"exp": %d
}`
,
valid
.
Unix
()),
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
want
:
&
user
.
DefaultInfo
{
Name
:
"thomas.jefferson@gmail.com"
,
},
},
{
name
:
"good token with api req audience"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
APIAudiences
:
authenticator
.
Audiences
{
"api"
},
UsernameClaim
:
"username"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
reqAudiences
:
authenticator
.
Audiences
{
"api"
},
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
},
},
{
name
:
"good token with multiple api req audience"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
APIAudiences
:
authenticator
.
Audiences
{
"api"
,
"other"
},
UsernameClaim
:
"username"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
reqAudiences
:
authenticator
.
Audiences
{
"api"
},
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
},
},
{
name
:
"good token with client_id req audience"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
APIAudiences
:
authenticator
.
Audiences
{
"api"
},
UsernameClaim
:
"username"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
reqAudiences
:
authenticator
.
Audiences
{
"my-client"
},
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
},
},
{
name
:
"good token with client_id and api req audience"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
APIAudiences
:
authenticator
.
Audiences
{
"api"
},
UsernameClaim
:
"username"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
reqAudiences
:
authenticator
.
Audiences
{
"my-client"
,
"api"
},
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
},
},
{
name
:
"good token with client_id and api req audience"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
APIAudiences
:
authenticator
.
Audiences
{
"api"
},
UsernameClaim
:
"username"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
reqAudiences
:
authenticator
.
Audiences
{
"my-client"
,
"api"
},
want
:
&
user
.
DefaultInfo
{
Name
:
"jane"
,
},
},
{
name
:
"good token with client_id and bad req audience"
,
options
:
Options
{
IssuerURL
:
"https://auth.example.com"
,
ClientID
:
"my-client"
,
APIAudiences
:
authenticator
.
Audiences
{
"api"
},
UsernameClaim
:
"username"
,
now
:
func
()
time
.
Time
{
return
now
},
},
signingKey
:
loadRSAPrivKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
pubKeys
:
[]
*
jose
.
JSONWebKey
{
loadRSAKey
(
t
,
"testdata/rsa_1.pem"
,
jose
.
RS256
),
},
claims
:
fmt
.
Sprintf
(
`{
"iss": "https://auth.example.com",
"aud": "my-client",
"username": "jane",
"exp": %d
}`
,
valid
.
Unix
()),
reqAudiences
:
authenticator
.
Audiences
{
"other"
},
wantSkip
:
true
,
},
}
for
_
,
test
:=
range
tests
{
t
.
Run
(
test
.
name
,
test
.
run
)
}
}
func
TestUnmarshalClaimError
(
t
*
testing
.
T
)
{
// Ensure error strings returned by unmarshaling claims don't include the claim.
const
token
=
"96bb299a-02e9-11e8-8673-54ee7553240e"
payload
:=
fmt
.
Sprintf
(
`{
"token": "%s"
}`
,
token
)
var
c
claims
if
err
:=
json
.
Unmarshal
([]
byte
(
payload
),
&
c
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
var
n
int
err
:=
c
.
unmarshalClaim
(
"token"
,
&
n
)
if
err
==
nil
{
t
.
Fatal
(
"expected error"
)
}
if
strings
.
Contains
(
err
.
Error
(),
token
)
{
t
.
Fatalf
(
"unmarshal error included token"
)
}
}
func
TestUnmarshalClaim
(
t
*
testing
.
T
)
{
tests
:=
[]
struct
{
name
string
claims
string
do
func
(
claims
)
(
interface
{},
error
)
want
interface
{}
wantErr
bool
}{
{
name
:
"string claim"
,
claims
:
`{"aud":"foo"}`
,
do
:
func
(
c
claims
)
(
interface
{},
error
)
{
var
s
string
err
:=
c
.
unmarshalClaim
(
"aud"
,
&
s
)
return
s
,
err
},
want
:
"foo"
,
},
{
name
:
"mismatched types"
,
claims
:
`{"aud":"foo"}`
,
do
:
func
(
c
claims
)
(
interface
{},
error
)
{
var
n
int
err
:=
c
.
unmarshalClaim
(
"aud"
,
&
n
)
return
n
,
err
},
wantErr
:
true
,
},
{
name
:
"bool claim"
,
claims
:
`{"email":"foo@coreos.com","email_verified":true}`
,
do
:
func
(
c
claims
)
(
interface
{},
error
)
{
var
verified
bool
err
:=
c
.
unmarshalClaim
(
"email_verified"
,
&
verified
)
return
verified
,
err
},
want
:
true
,
},
{
name
:
"strings claim"
,
claims
:
`{"groups":["a","b","c"]}`
,
do
:
func
(
c
claims
)
(
interface
{},
error
)
{
var
groups
[]
string
err
:=
c
.
unmarshalClaim
(
"groups"
,
&
groups
)
return
groups
,
err
},
want
:
[]
string
{
"a"
,
"b"
,
"c"
},
},
}
for
_
,
test
:=
range
tests
{
t
.
Run
(
test
.
name
,
func
(
t
*
testing
.
T
)
{
var
c
claims
if
err
:=
json
.
Unmarshal
([]
byte
(
test
.
claims
),
&
c
);
err
!=
nil
{
t
.
Fatal
(
err
)
}
got
,
err
:=
test
.
do
(
c
)
if
err
!=
nil
{
if
test
.
wantErr
{
return
}
t
.
Fatalf
(
"unexpected error: %v"
,
err
)
}
if
test
.
wantErr
{
t
.
Fatalf
(
"expected error"
)
}
if
!
reflect
.
DeepEqual
(
got
,
test
.
want
)
{
t
.
Errorf
(
"wanted=%#v, got=%#v"
,
test
.
want
,
got
)
}
})
}
}
staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/testdata/ecdsa_1.pem
deleted
100644 → 0
View file @
c691249e
-----BEGIN EC PRIVATE KEY-----
MIHcAgEBBEIAvxtZy9aI/lEt6LVLIhVrWLSwmlMFThU/nZUPr88nA5yKzJMyKbW/
QA+umam9YtO78WwzriTGC9qwW6+t+liXrcCgBwYFK4EEACOhgYkDgYYABAGzIO9n
tdTx6oVg1O59ljYP4FHY9RNUy+wHeXFnB6fo9asGg9jwLMg/iX0F+whFkllQjNLf
kKp/9ATWQHrzSbzuqwB9UU5zfQ3ulhMwEBpxbM6aSi1HyYtc5pQn7KB6h1VXiuQK
CIj4kVYHClZuKz0om/XAJL4vWVDwJqDBN6m9Yi9ZLQ==
-----END EC PRIVATE KEY-----
staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/testdata/ecdsa_2.pem
deleted
100644 → 0
View file @
c691249e
-----BEGIN EC PRIVATE KEY-----
MIHcAgEBBEIBtxiwrrDmi1U+NxClUKIr2cvmL6PPLxjAULjPuORt0AWbqKakphSJ
43VmIbPBBCuLnN2PuVS9N8jLDlR1KUnnFSGgBwYFK4EEACOhgYkDgYYABAEgshGY
Oflwnz2SQOWIkvSPmijMhS4nWmLYedR2H/Dg9c9nuiyQqL3XpqkPnQQwqOgcXjMT
hTec2tiLcRS3Gj02yQEpe/6Do6if4K4cQ9KsNtVHsn0bibsqLtRuvI7xUu9JJAs7
vSLNUtmxVzFo4s4spnIjLT71uz1Vag/NrKwot7cz4g==
-----END EC PRIVATE KEY-----
staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/testdata/ecdsa_3.pem
deleted
100644 → 0
View file @
c691249e
-----BEGIN EC PRIVATE KEY-----
MIHcAgEBBEIAQTHP4V93rz1w1D+jF1Jvx5QHzkQQIYxbN1LPuvQjEoplQrjZ4Qiu
h9mKK6DBCaDlfSos+wnTOlZH1z1tpa9soPOgBwYFK4EEACOhgYkDgYYABAFn+QOY
a937Lp+WO1S+zJU9ITnzdvjqQtD/TjtJPQsllV8rD0QNXZb/pLFQFZtDEehiZKEu
WA0REGNs+rVMO63YZAAyDMwZTz87ulH23OR6EaoyDp9qEPx7kpxgaJqeIztla2t8
SLVpv/FPR92E/OmguT6sFI5mP0AhV8UVlLYuHaovnw==
-----END EC PRIVATE KEY-----
staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/testdata/gen.sh
deleted
100755 → 0
View file @
c691249e
#!/usr/bin/env bash
# Copyright 2018 The Kubernetes Authors.
#
# Licensed under the Apache License, Version 2.0 (the "License");
# you may not use this file except in compliance with the License.
# You may obtain a copy of the License at
#
# http://www.apache.org/licenses/LICENSE-2.0
#
# Unless required by applicable law or agreed to in writing, software
# distributed under the License is distributed on an "AS IS" BASIS,
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
# See the License for the specific language governing permissions and
# limitations under the License.
set
-e
rm
./
*
.pem
for
N
in
$(
seq
1 3
)
;
do
ssh-keygen
-t
rsa
-b
2048
-f
rsa_
"
$N
"
.pem
-N
''
done
for
N
in
$(
seq
1 3
)
;
do
ssh-keygen
-t
ecdsa
-b
521
-f
ecdsa_
"
$N
"
.pem
-N
''
done
rm
./
*
.pub
staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/testdata/rsa_1.pem
deleted
100644 → 0
View file @
c691249e
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEA0pXWMYjWRjBEds/fKj/u9r2E6SIDx0J+TAg+eyVeR20Ky9jZ
mIXW5zSxE/EKpNQpiBWm1e6G9kmhMuqjr7g455S7E+3rD3OVkdTT6SU5AKBNSFoR
XUd+G/YJEtRzrpEYNtEJHkxUxWuyfCHblHSt+wsrE6t0DccCqC87lKQiGb/QfC8u
P6ZS99SCjKBEFp1fZvyNkYwStFc2OH5fBGPXXb6SNsquvDeKX9NeWjXkmxDkbOg2
kSkel4s/zw5KwcW3JzERfEcLStrDQ8fRbJ1C3uC088sUk4q4APQmKI/8FTvJe431
Vne9sOSptphiqCjlR+Knja58rc/vt4TkSPZf2wIDAQABAoIBAQDO3UgjMtOi8Wlf
+YW1IEbjdXrp9XMWu9gLYpHWMPgzXAeeBfCDJv7b8uP8ve2By7TcrMBOKVnE+MF0
nhCb3nFv9KftxOsDK70DG7nrrpgXaGFisK+cHU3hs8hoCfF1y6yotKGrdLpVkR0t
Wak1ZYU/NlJjqSqBGj0e7/8sXivtc7oME8tBBRBCEa8OqPqaelCInfFF1rX5vmxX
pQjPpZoA+vroSJy8SYE0N5oqtGwOPT+9rVuDOL10eaMbGUcssZl8ofwuvzOYPMW4
KFSVtvdtKnACq94Qy6XQbK5hZbZXSpzxANKq8SFyG2N1wOlpu/ktdXqkyDs08AZY
c/KkpXspAoGBAPdC73GOZn/hxzkwZ2Dl+S9rgrLT3VbeuhMp6GXSdiT+f9babMuw
HlYw6uULmvL1gD/0GmyWrHopPFJxodBG5SlwYS5wl49slcxeKCjK26vbNfK2eCbu
9uMtED4dN/5NlaXF4hqy/FmSyaFhQT+5hvx8n/zvLsgpuSQ+SCiDAHMfAoGBANoH
FCZeCWzzUFhObYG9wxGJ9FBPQa0htafIYEgTwezlKPsrfXfCTnVg1lLkr6Z4IwYQ
9VufJZNAc5V0X9H/ceyKJYxhQ+E01NEVzVpoK8fOC4yCYSYtbJnqkOUQzZJzkjFT
mNcIa8o4UrBOWzMhMQa0AOZH4VrbtZDCZhid+hfFAoGAAbKh9kOmDIa+WXQtoYqy
tVKlqRivUmNhH7SP9fMGAKcGtbD2QkfJTYo0crIrtDNfWBETBV/be1NBKMfC9q0l
8azl3e3D/KYgOTEEUZNjAsEUk8AQ/yNw6opqrCKDOenKd0LulIRaGztYyxTh39Ak
TyOD7bauuY0fylHrKOwNWr0CgYEAsVZ0o0h1rjKyNUGFfLQWyFtHZ1Mv/lye3tvy
xG2dnMxAaxvSr+hR3NNpQH9WB7dL9ZExoNZvv7f6y6OelLaLuXQcWnR6u+E3AOIU
5+Y3RgtoBV+/GUh1PzQ1qrviGa77SDfQ54an9hGd4F27fHkQ4XzkBmqM+FQg+J/G
X1uPomkCgYBo4ZBEA20Wvf1k2iWOVdfsxZNeOLxwcN5x89dAvm0v6RHg2NMy+XKw
Rj+YRuudFdxfg39J/V/Md9qsvjW+4FthD8GhgPs22dksV+7j6ApWkYTmIKG4rmh3
RhHOr6uLg9BeShnlvMMaMJKf2eA7SaVtmuS6uBGgEUNaa3qEBq0R+Q==
-----END RSA PRIVATE KEY-----
staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/testdata/rsa_2.pem
deleted
100644 → 0
View file @
c691249e
-----BEGIN RSA PRIVATE KEY-----
MIIEowIBAAKCAQEAxmLv1fqjacxZu5jyfQzytwjdt9/BBDzPM6G+R92YvsB7o2FH
ISiySbT8pyj7wGUgDvBdc9+GT3thf2pKGl/THaYJCLbh90GXVMTTak8Najqp2Qod
kxIpjSOBgukmd0LUtUVGKJg7YTEqsxAUmn9mfFiUZ6ixHr0U2NAXu7TF4dw7C9bt
ORi9DKDfUhEiV3Vcyrc5d/9VdmWG6fs/kq0B/6AboiSIIGAKSugNi4BgV7rNZYwZ
37UT72CW28TQQuuAtmTgXWMkfeWZiQKD1P8Nl1byORpgivp2A+2pMNlcLuogVhGY
TIZstbeMqoPyLW57GitWDg1tbakwJdCR23gCKQIDAQABAoIBACZmDf//1FNtD01F
TGIx+GS/HZMyhvyX/I8E1ny4gpEhVo0IDil35BJqKqD8SMYzjKH3mk8MS8Xknrl3
zEIQnB9X/NWn+FLQakcpFba0+GbAVhHBaHoIAOzlm3LIR/67e8peTzcaSBwG1Tn1
eddxo1ecGZV6zFWjyX4xwPY/BjIyA/b1LewqIK6W/I4u3RwtEzqANV9ddVbFH53e
Y+i2X1HVuLm0LETsX4jB/G/ZDP6Y101gOwPFddm+h1ixZ2jrAkyTbvYL5ukIsU8Z
okIEZsd6nv08YN+LOXOPh0CxvgHI347RDzgfbDmHGqq8gh20+wLP/MV+dOiBBAJT
RfnoFcUCgYEA8SpMW64CNhRkH3Nv5A5ffSOa7NqiN7OdNEswgcgkAbjR5YsTATPg
p9iWqGcEodX1FWjnL2NLMdMvYxh4MwMCACIa8FQ2/RDEwViaVjxcOK64MIvyvnNq
NObx8pMClUBXWF/llxxTR+/CJWRdCABBm56lQPuuX/qEi/xqybHPcAcCgYEA0pb9
FGmGhDXl3bG3zNNIim+FuqG0xQoIjVPwdvkMI30h/ii6qs3jxwHlw6KBf8AI9wI+
bWbzhwcYVkS6It0Yj4q/mqOVHi89frrAQygsJkMQkdl8WiWwPeiiIdsHYTUcBv5+
i6YLs8ycnzMeFAxg8kuxrq6mm3yW6u5CuInsEE8CgYAWXqUMj/x2hbevzyZe0hJ7
ahURyUnovsljM2JBd44XdsxJbXgK0YQSLZ3z6vJcDJuaK8vd8mjkK0GnAHsNyEak
OoWjKzyahrapdI2EWD75pwNAxYpzrgL4+z8QECDaNUik0uhZ9u+mqY+ppkCW4Gc1
hyau+2l2T6eB0J0bLloeewKBgQC+fZn8JuBpI6AEg8ew3cYWg37CLZgpTEQkIzO3
StyyFXT0RL9l1cwerha6ensNphX16e+yYpgTIlXfY1fERZ776RQcu7Adl7nWsvNL
TEFzcuLAK60SlljwB0jxuwDX64SoxviNNewL/iAG2eRxWikvw0y8qHtI1tBlPpTX
/NqufQKBgD1jAPRgu+IN9IDXoqnMiXbSfrVsJ1ndyPCRBq4dfvawcbEDTZfkiid0
aNrdRoOETfLCynAcG6o6uS3+2sumzXLrMNr8MDF8NEa2Rh8nN3IjZqESV4CNgaV6
JhAlWFp+AvYv6N1iHK52nNAFiX/cfaMpWTUKqk2Z4VZCr5qhLUVs
-----END RSA PRIVATE KEY-----
staging/src/k8s.io/apiserver/plugin/pkg/authenticator/token/oidc/testdata/rsa_3.pem
deleted
100644 → 0
View file @
c691249e
-----BEGIN RSA PRIVATE KEY-----
MIIEpgIBAAKCAQEA1uO3LV0wHadHjQb8U8KGUpzlZTyBHlinL7lF5yKk2hXOyssT
Z0UVF6ofPq/L+ZN5VkTb1FJWMEBiX1BgXlboDdYKAYTl1QaeEQrfrAM4gp2FdWS4
fMUuagFdVXs8T2J4GGPEE83ybwz/YEM3p83Qojifvx/IjVtuHCMkUpcj92scjsCY
EeSKRjJicVBmJ1RK0ShEorHhnYmokKYsPl41mV5VdmWZOtmPK4jV05cBfg7eC7yI
cwwYhowLkYvd9d9H74otK5tD7KTxFG6JJ0N5zwf6XBRcXBWKstfOOZ5Qgo9z0Tkm
n3Klp9vwun25aaA1MlSSByByiCb7qvS8jhqMkQIDAQABAoIBAQCjN58AU9GiFFai
ZXXuYMgJo6YRON2RoSCLfRv9LTEtfHbvTUPVooEc0lidEoXJcRwuTGr0X/2a9KxD
XRd1UGk9aR98e+bd4QLaSvoM+v1HKEIgInqGOnbAiXzM2qe6XD5/t/dMW5cShjrK
cQOq7wbS0FN1pbx8sb92m7KREL9+wnXuOCHYtublRf7arsMkaZcpSBBaI+raMaZR
dUC+LmalIvR8+dNegducwWsdE8/Vh+xq97ZbNFlyut3JOvfuHmaAOvUsX/4touj2
dDkJmvzvmpTBG888t+6hv9eKWaacsTAKuPLThRBD53coTEvHK8iic9fOok65y5Bn
nFP/irUpAoGBAPUsPoAPwcNajZX/E4XeG/2eV+IHMxYR9gJwBzpoUfwHr5mR54HK
POia/z7/M2KVV9kMWBlbTumIHSEylEEDCCKNNIe1gHBxQ7uGuaf+vVXpVgjBigUz
7oiCjb5RdjevfiyudX/z0B9IQSI9djCXebifEHKpUxAOmU3oP0SEMULLAoGBAOBh
G+fDouMU7QN93NG0hssu44yc7xQhb7VLB+isrEQASVG2xWkd3iDrdRxwHAHZqxFd
4DRzDTFC7yeR+FEbVVkWQRaeDwFJM1zmRTXsYjBkK49GNzB4QEtHvPouuxMAQ4UD
zJ9a3LEDSs867R7XEbNF8j9NC9z0vk7w9bHTA1aTAoGBAODUUQBY8sQ1zy8lOf8B
/sMmKMti9Mshb2su1sIOFli7p6F5tkZEcnSQZs+bccDO2T92XXfrTsNDigr+egvg
Pt6IhQqKPB1hEM7wLmLLbU9Sag4fhXVd+TmAF4HW7EUGjvtkhOXwbQOy2+ANYswO
rJXMcGXltwE7kgRqnVI0s4PfAoGBALUrM5Dq0bZwyv6qvYVFMiEUdv6uKAwlA0Fq
l7Qy19UANjMYVEUPrK7/7stLahHEYu/e0I0I6HoCBX/5yHoUi9Emut88N/ld1W8J
LpDfkFhqSRGiLCWisqcWAWwwFzS8XcgkzS9N+iui8OBqP9NK7CvIKlUaLJ33r0Gm
JXuzWVqpAoGBAIQ8+YuvFfyhwXuWwQbzxVpWRURH0FRu8KfFIkFFbdyht6leYXuj
uxcrcHWzkEPSLO22BoJX8ECHq4LadAIjkkpr5GCikKCF+r/bq50RnECqvfoJ629J
gA87C8cLU3dXmSYd+vSg6icZyncTmXyyEV0dqoUGJ2M33kE6hYAbc/ic
-----END RSA PRIVATE KEY-----
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment