2023-05-08 21:07:38 +00:00
|
|
|
|
// Copyright 2020-2023 the Pinniped contributors. All Rights Reserved.
|
2020-11-19 04:30:05 +00:00
|
|
|
|
// SPDX-License-Identifier: Apache-2.0
|
|
|
|
|
|
|
|
|
|
package authorizationcode
|
|
|
|
|
|
|
|
|
|
import (
|
|
|
|
|
"context"
|
|
|
|
|
stderrors "errors"
|
|
|
|
|
"fmt"
|
2020-12-10 20:15:40 +00:00
|
|
|
|
"time"
|
2020-11-19 04:30:05 +00:00
|
|
|
|
|
|
|
|
|
"github.com/ory/fosite"
|
|
|
|
|
"github.com/ory/fosite/handler/oauth2"
|
2021-10-22 21:32:26 +00:00
|
|
|
|
v1 "k8s.io/api/core/v1"
|
2020-11-19 04:30:05 +00:00
|
|
|
|
"k8s.io/apimachinery/pkg/api/errors"
|
|
|
|
|
corev1client "k8s.io/client-go/kubernetes/typed/core/v1"
|
|
|
|
|
|
|
|
|
|
"go.pinniped.dev/internal/constable"
|
|
|
|
|
"go.pinniped.dev/internal/crud"
|
2020-12-01 22:53:22 +00:00
|
|
|
|
"go.pinniped.dev/internal/fositestorage"
|
2021-06-15 16:27:30 +00:00
|
|
|
|
"go.pinniped.dev/internal/oidc/clientregistry"
|
2021-10-06 22:28:13 +00:00
|
|
|
|
"go.pinniped.dev/internal/psession"
|
2020-11-19 04:30:05 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
const (
|
2020-12-04 23:40:17 +00:00
|
|
|
|
TypeLabelValue = "authcode"
|
|
|
|
|
|
2020-12-02 01:18:32 +00:00
|
|
|
|
ErrInvalidAuthorizeRequestData = constable.Error("authorization request data must be present")
|
2020-11-19 04:30:05 +00:00
|
|
|
|
ErrInvalidAuthorizeRequestVersion = constable.Error("authorization request data has wrong version")
|
|
|
|
|
|
2021-10-20 22:53:25 +00:00
|
|
|
|
// Version 1 was the initial release of storage.
|
|
|
|
|
// Version 2 is when we switched to storing psession.PinnipedSession inside the fosite request.
|
Create username scope, required for clients to get username in ID token
- For backwards compatibility with older Pinniped CLIs, the pinniped-cli
client does not need to request the username or groups scopes for them
to be granted. For dynamic clients, the usual OAuth2 rules apply:
the client must be allowed to request the scopes according to its
configuration, and the client must actually request the scopes in the
authorization request.
- If the username scope was not granted, then there will be no username
in the ID token, and the cluster-scoped token exchange will fail since
there would be no username in the resulting cluster-scoped ID token.
- The OIDC well-known discovery endpoint lists the username and groups
scopes in the scopes_supported list, and lists the username and groups
claims in the claims_supported list.
- Add username and groups scopes to the default list of scopes
put into kubeconfig files by "pinniped get kubeconfig" CLI command,
and the default list of scopes used by "pinniped login oidc" when
no list of scopes is specified in the kubeconfig file
- The warning header about group memberships changing during upstream
refresh will only be sent to the pinniped-cli client, since it is
only intended for kubectl and it could leak the username to the
client (which may not have the username scope granted) through the
warning message text.
- Add the user's username to the session storage as a new field, so that
during upstream refresh we can compare the original username from the
initial authorization to the refreshed username, even in the case when
the username scope was not granted (and therefore the username is not
stored in the ID token claims of the session storage)
- Bump the Supervisor session storage format version from 2 to 3
due to the username field being added to the session struct
- Extract commonly used string constants related to OIDC flows to api
package.
- Change some import names to make them consistent:
- Always import github.com/coreos/go-oidc/v3/oidc as "coreosoidc"
- Always import go.pinniped.dev/generated/latest/apis/supervisor/oidc
as "oidcapi"
- Always import go.pinniped.dev/internal/oidc as "oidc"
2022-08-08 23:29:22 +00:00
|
|
|
|
// Version 3 is when we added the Username field to the psession.CustomSessionData.
|
2022-12-14 00:18:51 +00:00
|
|
|
|
// Version 4 is when fosite added json tags to their openid.DefaultSession struct.
|
2023-05-08 21:07:38 +00:00
|
|
|
|
// Version 5 is when we added the UpstreamUsername and UpstreamGroups fields to psession.CustomSessionData.
|
|
|
|
|
authorizeCodeStorageVersion = "5"
|
2020-11-19 04:30:05 +00:00
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
var _ oauth2.AuthorizeCodeStorage = &authorizeCodeStorage{}
|
|
|
|
|
|
|
|
|
|
type authorizeCodeStorage struct {
|
|
|
|
|
storage crud.Storage
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-10 23:34:19 +00:00
|
|
|
|
type Session struct {
|
2020-12-01 19:01:23 +00:00
|
|
|
|
Active bool `json:"active"`
|
|
|
|
|
Request *fosite.Request `json:"request"`
|
|
|
|
|
Version string `json:"version"`
|
2020-11-19 04:30:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2020-12-10 20:15:40 +00:00
|
|
|
|
func New(secrets corev1client.SecretInterface, clock func() time.Time, sessionStorageLifetime time.Duration) oauth2.AuthorizeCodeStorage {
|
|
|
|
|
return &authorizeCodeStorage{storage: crud.New(TypeLabelValue, secrets, clock, sessionStorageLifetime)}
|
2020-11-19 04:30:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
2021-11-10 23:34:19 +00:00
|
|
|
|
// ReadFromSecret reads the contents of a Secret as a Session.
|
|
|
|
|
func ReadFromSecret(secret *v1.Secret) (*Session, error) {
|
2021-10-22 21:32:26 +00:00
|
|
|
|
session := NewValidEmptyAuthorizeCodeSession()
|
|
|
|
|
err := crud.FromSecret(TypeLabelValue, secret, session)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
if session.Version != authorizeCodeStorageVersion {
|
|
|
|
|
return nil, fmt.Errorf("%w: authorization code session has version %s instead of %s",
|
|
|
|
|
ErrInvalidAuthorizeRequestVersion, session.Version, authorizeCodeStorageVersion)
|
|
|
|
|
}
|
|
|
|
|
if session.Request.ID == "" {
|
|
|
|
|
return nil, fmt.Errorf("malformed authorization code session: %w", ErrInvalidAuthorizeRequestData)
|
|
|
|
|
}
|
|
|
|
|
return session, nil
|
|
|
|
|
}
|
|
|
|
|
|
2020-11-19 04:30:05 +00:00
|
|
|
|
func (a *authorizeCodeStorage) CreateAuthorizeCodeSession(ctx context.Context, signature string, requester fosite.Requester) error {
|
2020-12-01 19:01:23 +00:00
|
|
|
|
// This conversion assumes that we do not wrap the default type in any way
|
2020-11-19 04:30:05 +00:00
|
|
|
|
// i.e. we use the default fosite.OAuth2Provider.NewAuthorizeRequest implementation
|
|
|
|
|
// note that because this type is serialized and stored in Kube, we cannot easily change the implementation later
|
2020-12-01 22:53:22 +00:00
|
|
|
|
request, err := fositestorage.ValidateAndExtractAuthorizeRequest(requester)
|
2020-11-19 04:30:05 +00:00
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-01 19:01:23 +00:00
|
|
|
|
// Note, in case it is helpful, that Hydra stores specific fields from the requester:
|
2020-11-19 04:30:05 +00:00
|
|
|
|
// request ID
|
|
|
|
|
// requestedAt
|
|
|
|
|
// OAuth client ID
|
|
|
|
|
// requested scopes, granted scopes
|
|
|
|
|
// requested audience, granted audience
|
|
|
|
|
// url encoded request form
|
|
|
|
|
// session as JSON bytes with (optional) encryption
|
|
|
|
|
// session subject
|
|
|
|
|
// consent challenge from session which is the identifier ("authorization challenge")
|
|
|
|
|
// of the consent authorization request. It is used to identify the session.
|
|
|
|
|
// signature for lookup in the DB
|
|
|
|
|
|
2022-08-26 17:57:45 +00:00
|
|
|
|
_, err = a.storage.Create(ctx, signature, &Session{Active: true, Request: request, Version: authorizeCodeStorageVersion}, nil, nil)
|
2020-11-19 04:30:05 +00:00
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *authorizeCodeStorage) GetAuthorizeCodeSession(ctx context.Context, signature string, _ fosite.Session) (fosite.Requester, error) {
|
2020-12-01 19:01:23 +00:00
|
|
|
|
// Note, in case it is helpful, that Hydra:
|
|
|
|
|
// - uses the incoming fosite.Session to provide the type needed to json.Unmarshal their session bytes
|
|
|
|
|
// - gets the client from its DB as a concrete type via client ID, the hydra memory client just validates that the
|
|
|
|
|
// client ID exists
|
|
|
|
|
// - hydra uses the sha512.Sum384 hash of signature when using JWT as access token to reduce length
|
2020-11-19 04:30:05 +00:00
|
|
|
|
|
|
|
|
|
session, _, err := a.getSession(ctx, signature)
|
|
|
|
|
|
|
|
|
|
// we need to always pass both the request and error back
|
|
|
|
|
if session == nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return session.Request, err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (a *authorizeCodeStorage) InvalidateAuthorizeCodeSession(ctx context.Context, signature string) error {
|
|
|
|
|
session, rv, err := a.getSession(ctx, signature)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
session.Active = false
|
|
|
|
|
if _, err := a.storage.Update(ctx, signature, rv, session); err != nil {
|
|
|
|
|
if errors.IsConflict(err) {
|
|
|
|
|
return &errSerializationFailureWithCause{cause: err}
|
|
|
|
|
}
|
|
|
|
|
return err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-10 23:34:19 +00:00
|
|
|
|
func (a *authorizeCodeStorage) getSession(ctx context.Context, signature string) (*Session, string, error) {
|
2020-11-19 04:30:05 +00:00
|
|
|
|
session := NewValidEmptyAuthorizeCodeSession()
|
|
|
|
|
rv, err := a.storage.Get(ctx, signature, session)
|
|
|
|
|
|
|
|
|
|
if errors.IsNotFound(err) {
|
2020-12-17 20:09:19 +00:00
|
|
|
|
return nil, "", fosite.ErrNotFound.WithWrap(err).WithDebug(err.Error())
|
2020-11-19 04:30:05 +00:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, "", fmt.Errorf("failed to get authorization code session for %s: %w", signature, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if version := session.Version; version != authorizeCodeStorageVersion {
|
|
|
|
|
return nil, "", fmt.Errorf("%w: authorization code session for %s has version %s instead of %s",
|
|
|
|
|
ErrInvalidAuthorizeRequestVersion, signature, version, authorizeCodeStorageVersion)
|
|
|
|
|
}
|
|
|
|
|
|
2020-12-02 01:18:32 +00:00
|
|
|
|
if session.Request.ID == "" {
|
2020-11-19 04:30:05 +00:00
|
|
|
|
return nil, "", fmt.Errorf("malformed authorization code session for %s: %w", signature, ErrInvalidAuthorizeRequestData)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// we must return the session in this case to allow fosite to revoke the associated tokens
|
|
|
|
|
if !session.Active {
|
|
|
|
|
return session, rv, fmt.Errorf("authorization code session for %s has already been used: %w", signature, fosite.ErrInvalidatedAuthorizeCode)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return session, rv, nil
|
|
|
|
|
}
|
|
|
|
|
|
2021-11-10 23:34:19 +00:00
|
|
|
|
func NewValidEmptyAuthorizeCodeSession() *Session {
|
|
|
|
|
return &Session{
|
2020-12-01 19:01:23 +00:00
|
|
|
|
Request: &fosite.Request{
|
2021-06-15 16:27:30 +00:00
|
|
|
|
Client: &clientregistry.Client{},
|
2021-10-06 22:28:13 +00:00
|
|
|
|
Session: &psession.PinnipedSession{},
|
2020-11-19 04:30:05 +00:00
|
|
|
|
},
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var _ interface {
|
|
|
|
|
Is(error) bool
|
|
|
|
|
Unwrap() error
|
|
|
|
|
error
|
|
|
|
|
} = &errSerializationFailureWithCause{}
|
|
|
|
|
|
|
|
|
|
type errSerializationFailureWithCause struct {
|
|
|
|
|
cause error
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (e *errSerializationFailureWithCause) Is(err error) bool {
|
|
|
|
|
return stderrors.Is(fosite.ErrSerializationFailure, err)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (e *errSerializationFailureWithCause) Unwrap() error {
|
|
|
|
|
return e.cause
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
func (e *errSerializationFailureWithCause) Error() string {
|
|
|
|
|
return fmt.Sprintf("%s: %s", fosite.ErrSerializationFailure, e.cause)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// ExpectedAuthorizeCodeSessionJSONFromFuzzing is used for round tripping tests.
|
|
|
|
|
// It is exported to allow integration tests to use it.
|
|
|
|
|
const ExpectedAuthorizeCodeSessionJSONFromFuzzing = `{
|
2021-10-06 22:28:13 +00:00
|
|
|
|
"active": true,
|
|
|
|
|
"request": {
|
2020-12-17 21:14:20 +00:00
|
|
|
|
"id": "曑x螠Gæ鄋楨",
|
2020-12-01 19:01:23 +00:00
|
|
|
|
"requestedAt": "2082-11-10T18:36:11.627253638Z",
|
2020-11-19 04:30:05 +00:00
|
|
|
|
"client": {
|
2021-10-06 22:28:13 +00:00
|
|
|
|
"id": ":NJ¸Ɣ8(黋馛ÄRɴJa¶z",
|
|
|
|
|
"client_secret": "UQ==",
|
2021-12-10 22:22:36 +00:00
|
|
|
|
"rotated_secrets": [
|
|
|
|
|
"Bno=",
|
|
|
|
|
"0j8=",
|
|
|
|
|
"1c4="
|
|
|
|
|
],
|
2021-10-06 22:28:13 +00:00
|
|
|
|
"redirect_uris": [
|
2021-12-10 22:22:36 +00:00
|
|
|
|
"ʊXĝ",
|
|
|
|
|
"Ƿ"
|
|
|
|
|
],
|
|
|
|
|
"grant_types": [
|
|
|
|
|
"祩d",
|
2021-10-06 22:28:13 +00:00
|
|
|
|
"zŇZ",
|
|
|
|
|
"優蒼ĊɌț訫DŽǽeʀO2ƚ\u0026N"
|
|
|
|
|
],
|
2021-12-10 22:22:36 +00:00
|
|
|
|
"response_types": [
|
2021-10-06 22:28:13 +00:00
|
|
|
|
"唐W6ɻ橩斚薛ɑƐ"
|
|
|
|
|
],
|
2021-12-10 22:22:36 +00:00
|
|
|
|
"scopes": [
|
2021-10-06 22:28:13 +00:00
|
|
|
|
"w",
|
|
|
|
|
"ǔŭe[u@阽羂ŷ-Ĵ½輢OÅ濲喾H"
|
|
|
|
|
],
|
|
|
|
|
"audience": [
|
2021-12-10 22:22:36 +00:00
|
|
|
|
"G螩歐湡ƙı唡ɸğƎ\u0026胢輢Ƈĵƚ"
|
2021-10-06 22:28:13 +00:00
|
|
|
|
],
|
|
|
|
|
"public": false,
|
2021-12-10 22:22:36 +00:00
|
|
|
|
"jwks_uri": "潌țjA9;焋Ēƕ",
|
2021-10-06 22:28:13 +00:00
|
|
|
|
"jwks": {
|
|
|
|
|
"keys": [
|
|
|
|
|
{
|
|
|
|
|
"kty": "OKP",
|
|
|
|
|
"crv": "Ed25519",
|
2021-12-10 22:22:36 +00:00
|
|
|
|
"x": "LHMZ29A64WecPQSLotS8hfZ2mae0SR17CtPdnMDP7ZI",
|
|
|
|
|
"x5u": {
|
|
|
|
|
"Scheme": "",
|
|
|
|
|
"Opaque": "",
|
|
|
|
|
"User": null,
|
|
|
|
|
"Host": "",
|
|
|
|
|
"Path": "",
|
|
|
|
|
"RawPath": "",
|
2022-08-24 21:45:55 +00:00
|
|
|
|
"OmitHost": false,
|
2021-12-10 22:22:36 +00:00
|
|
|
|
"ForceQuery": false,
|
|
|
|
|
"RawQuery": "",
|
|
|
|
|
"Fragment": "",
|
|
|
|
|
"RawFragment": ""
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"kty": "OKP",
|
|
|
|
|
"crv": "Ed25519",
|
|
|
|
|
"x": "1PwKrC4qDe8cabzGTdA0NjuMJhAZAw7Bu7Tj9z2Y4pE",
|
2021-10-06 22:28:13 +00:00
|
|
|
|
"x5u": {
|
|
|
|
|
"Scheme": "",
|
|
|
|
|
"Opaque": "",
|
|
|
|
|
"User": null,
|
|
|
|
|
"Host": "",
|
|
|
|
|
"Path": "",
|
|
|
|
|
"RawPath": "",
|
2022-08-24 21:45:55 +00:00
|
|
|
|
"OmitHost": false,
|
2021-10-06 22:28:13 +00:00
|
|
|
|
"ForceQuery": false,
|
|
|
|
|
"RawQuery": "",
|
|
|
|
|
"Fragment": "",
|
|
|
|
|
"RawFragment": ""
|
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
{
|
|
|
|
|
"kty": "OKP",
|
|
|
|
|
"crv": "Ed25519",
|
2021-12-10 22:22:36 +00:00
|
|
|
|
"x": "j4b-Vld5buh_2KIpjjaDRJ8OY7l7d6XAumvDtVTT9BI",
|
2021-10-06 22:28:13 +00:00
|
|
|
|
"x5u": {
|
|
|
|
|
"Scheme": "",
|
|
|
|
|
"Opaque": "",
|
|
|
|
|
"User": null,
|
|
|
|
|
"Host": "",
|
|
|
|
|
"Path": "",
|
|
|
|
|
"RawPath": "",
|
2022-08-24 21:45:55 +00:00
|
|
|
|
"OmitHost": false,
|
2021-10-06 22:28:13 +00:00
|
|
|
|
"ForceQuery": false,
|
|
|
|
|
"RawQuery": "",
|
|
|
|
|
"Fragment": "",
|
|
|
|
|
"RawFragment": ""
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
]
|
|
|
|
|
},
|
2021-12-10 22:22:36 +00:00
|
|
|
|
"token_endpoint_auth_method": "趀Ȁ;hYGe天蹗ĽǙ澅j翕q骽",
|
2021-10-06 22:28:13 +00:00
|
|
|
|
"request_uris": [
|
2021-12-10 22:22:36 +00:00
|
|
|
|
"Ǐ蛓ȿ,JwwƐ\u003c涵ØƉKĵ",
|
|
|
|
|
"Ȟú",
|
|
|
|
|
"Q7钎漡臧n栀,i"
|
2021-10-06 22:28:13 +00:00
|
|
|
|
],
|
2021-12-10 22:22:36 +00:00
|
|
|
|
"request_object_signing_alg": "廜+v,淬Ʋ4Dʧ呩锏緍场脋",
|
|
|
|
|
"token_endpoint_auth_signing_alg": "ưƓǴ罷ǹ~]ea胠Ĺĩv絹b垇I"
|
2020-11-19 04:30:05 +00:00
|
|
|
|
},
|
|
|
|
|
"scopes": [
|
2021-12-10 22:22:36 +00:00
|
|
|
|
"ĩǀŻQ'k頂箨J-a",
|
|
|
|
|
"ɓ啶#昏Q遐*\\髎bŸ1慂U"
|
2020-11-19 04:30:05 +00:00
|
|
|
|
],
|
|
|
|
|
"grantedScopes": [
|
2021-12-10 22:22:36 +00:00
|
|
|
|
"ƼĮǡ鑻Z¥篚h°ʣ£ǖ%\"砬ʍ"
|
2020-11-19 04:30:05 +00:00
|
|
|
|
],
|
|
|
|
|
"form": {
|
2021-12-10 22:22:36 +00:00
|
|
|
|
"¡": [
|
|
|
|
|
"Ła卦牟懧¥ɂĵ",
|
|
|
|
|
"ɎǛƍdÚ慂+槰蚪i齥篗裢?霃谥vƘ:",
|
|
|
|
|
"/濔Aʉ\u003cS獾蔀OƭUǦ"
|
|
|
|
|
],
|
|
|
|
|
"民撲ʓeŘ嬀j¤囡莒汗狲N\u003cCq": [
|
|
|
|
|
"5ȏ樛ȧ.mĔ櫓Ǩ療騃Ǐ}ɟ",
|
|
|
|
|
"潠[ĝU噤'",
|
|
|
|
|
"ŁȗɉY妶ǵ!ȁ"
|
2021-10-06 22:28:13 +00:00
|
|
|
|
],
|
2021-12-10 22:22:36 +00:00
|
|
|
|
"褰ʎɰ癟VĎĢ婄磫绒u妔隤ʑƍš駎竪": [
|
|
|
|
|
"鱙翑ȲŻ麤ã桒嘞\\摗Ǘū稖咾鎅ǸÖ"
|
2021-10-06 22:28:13 +00:00
|
|
|
|
]
|
2020-11-19 04:30:05 +00:00
|
|
|
|
},
|
|
|
|
|
"session": {
|
2021-10-06 22:28:13 +00:00
|
|
|
|
"fosite": {
|
2022-12-14 00:18:51 +00:00
|
|
|
|
"id_token_claims": {
|
|
|
|
|
"jti": "褗6巽ēđų蓼tùZ蛆鬣a\"ÙǞ0觢",
|
|
|
|
|
"iss": "j¦鲶H股ƲLŋZ-{",
|
|
|
|
|
"sub": "ehpƧ蓟",
|
|
|
|
|
"aud": [
|
2021-12-10 22:22:36 +00:00
|
|
|
|
"驜Ŗ~ů崧軒q腟u尿宲!"
|
2021-10-06 22:28:13 +00:00
|
|
|
|
],
|
2022-12-14 00:18:51 +00:00
|
|
|
|
"nonce": "ǎ^嫯R忑隯ƗƋ*L\u0026",
|
|
|
|
|
"exp": "1989-06-02T14:40:29.613836765Z",
|
|
|
|
|
"iat": "2052-03-26T02:39:27.882495556Z",
|
|
|
|
|
"rat": "2038-04-06T10:46:24.698586972Z",
|
|
|
|
|
"auth_time": "2003-01-05T11:30:18.206004879Z",
|
|
|
|
|
"at_hash": "ğǫ\\aȊ4ț髄Al",
|
|
|
|
|
"acr": "曓蓳n匟鯘磹*金爃鶴滱ůĮǐ_c3#",
|
|
|
|
|
"amr": [
|
2021-12-10 22:22:36 +00:00
|
|
|
|
"装ƹýĸŴB岺Ð嫹Sx镯荫őł疂ư墫"
|
|
|
|
|
],
|
2022-12-14 00:18:51 +00:00
|
|
|
|
"c_hash": "\u0026鶡",
|
|
|
|
|
"ext": {
|
2021-12-10 22:22:36 +00:00
|
|
|
|
"rǓ\\BRë_g\"ʎ啴SƇMǃļū": {
|
|
|
|
|
"4撎胬龯,t猟i\u0026\u0026Q@ǤǟǗ": [
|
|
|
|
|
1239190737
|
|
|
|
|
],
|
|
|
|
|
"飘ȱF?Ƈ畋": {
|
|
|
|
|
"劰û橸ɽ銐ƭ?}HƟ玈鳚": null,
|
|
|
|
|
"骲v0H晦XŘO溪V蔓Ȍ+~ē埅Ȝ": {
|
|
|
|
|
"4Ǟ": false
|
2021-10-06 22:28:13 +00:00
|
|
|
|
}
|
2021-12-10 22:22:36 +00:00
|
|
|
|
}
|
|
|
|
|
},
|
|
|
|
|
"鑳绪": 2738428764
|
2021-10-06 22:28:13 +00:00
|
|
|
|
}
|
2020-12-17 21:14:20 +00:00
|
|
|
|
},
|
2022-12-14 00:18:51 +00:00
|
|
|
|
"headers": {
|
|
|
|
|
"extra": {
|
2021-12-10 22:22:36 +00:00
|
|
|
|
"d謺錳4帳ŅǃĊ": 663773398,
|
|
|
|
|
"Ř鸨EJ": {
|
|
|
|
|
"Ǽǟ迍阊v\"豑觳翢砜": [
|
|
|
|
|
995342744
|
|
|
|
|
],
|
|
|
|
|
"ȏl鐉诳DT=3骜Ǹ": {
|
|
|
|
|
"厷ɁOƪ穋嶿鳈恱va|载ǰɱ汶C]ɲ": null,
|
|
|
|
|
"荤Ý呐ʣ®DžȪǣǎǔ爣縗ɦü": {
|
|
|
|
|
"H :靥湤庤毩fɤȆʪ融ƆuŤn": true
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
}
|
2021-10-06 22:28:13 +00:00
|
|
|
|
}
|
|
|
|
|
},
|
2022-12-14 00:18:51 +00:00
|
|
|
|
"expires_at": {
|
2021-12-10 22:22:36 +00:00
|
|
|
|
"韁臯氃妪婝rȤ\"h丬鎒ơ娻}ɼƟ": "1970-04-27T04:31:30.902468229Z"
|
2021-10-06 22:28:13 +00:00
|
|
|
|
},
|
2022-12-14 00:18:51 +00:00
|
|
|
|
"username": "髉龳ǽÙ",
|
|
|
|
|
"subject": "\u0026¥潝邎Ȗ莅ŝǔ盕戙鵮碡ʯiŬŽ"
|
2021-10-06 22:28:13 +00:00
|
|
|
|
},
|
|
|
|
|
"custom": {
|
Create username scope, required for clients to get username in ID token
- For backwards compatibility with older Pinniped CLIs, the pinniped-cli
client does not need to request the username or groups scopes for them
to be granted. For dynamic clients, the usual OAuth2 rules apply:
the client must be allowed to request the scopes according to its
configuration, and the client must actually request the scopes in the
authorization request.
- If the username scope was not granted, then there will be no username
in the ID token, and the cluster-scoped token exchange will fail since
there would be no username in the resulting cluster-scoped ID token.
- The OIDC well-known discovery endpoint lists the username and groups
scopes in the scopes_supported list, and lists the username and groups
claims in the claims_supported list.
- Add username and groups scopes to the default list of scopes
put into kubeconfig files by "pinniped get kubeconfig" CLI command,
and the default list of scopes used by "pinniped login oidc" when
no list of scopes is specified in the kubeconfig file
- The warning header about group memberships changing during upstream
refresh will only be sent to the pinniped-cli client, since it is
only intended for kubectl and it could leak the username to the
client (which may not have the username scope granted) through the
warning message text.
- Add the user's username to the session storage as a new field, so that
during upstream refresh we can compare the original username from the
initial authorization to the refreshed username, even in the case when
the username scope was not granted (and therefore the username is not
stored in the ID token claims of the session storage)
- Bump the Supervisor session storage format version from 2 to 3
due to the username field being added to the session struct
- Extract commonly used string constants related to OIDC flows to api
package.
- Change some import names to make them consistent:
- Always import github.com/coreos/go-oidc/v3/oidc as "coreosoidc"
- Always import go.pinniped.dev/generated/latest/apis/supervisor/oidc
as "oidcapi"
- Always import go.pinniped.dev/internal/oidc as "oidc"
2022-08-08 23:29:22 +00:00
|
|
|
|
"username": "Ĝ眧Ĭ",
|
2023-06-05 21:40:39 +00:00
|
|
|
|
"upstreamUsername": "ʼn2ƋŢ觛ǂ焺nŐǛ",
|
|
|
|
|
"upstreamGroups": [
|
|
|
|
|
"闣ʬ橳(ý綃ʃʚƟ覣k眐4Ĉt",
|
|
|
|
|
"ʃƸ澺淗a紽ǒ|鰽ŋ猊Ia瓕巈環_ɑ"
|
|
|
|
|
],
|
|
|
|
|
"providerUID": "ƴŤȱʀļÂ?墖",
|
|
|
|
|
"providerName": "7就伒犘c钡",
|
|
|
|
|
"providerType": "k|鬌R蜚蠣麹概÷驣7Ʀ澉1æɽ誮",
|
2022-01-18 23:34:19 +00:00
|
|
|
|
"warnings": [
|
2023-06-05 21:40:39 +00:00
|
|
|
|
"鷞aŚB碠k9帴ʘ赱",
|
|
|
|
|
"ď逳鞪?3)藵睋邔\u0026Ű惫蜀Ģ¡圔"
|
2022-01-18 23:34:19 +00:00
|
|
|
|
],
|
2021-10-06 22:28:13 +00:00
|
|
|
|
"oidc": {
|
2023-06-05 21:40:39 +00:00
|
|
|
|
"upstreamRefreshToken": "墀jMʥ",
|
|
|
|
|
"upstreamAccessToken": "+î艔垎0",
|
|
|
|
|
"upstreamSubject": "ĝ",
|
|
|
|
|
"upstreamIssuer": "ǢIȽ"
|
2021-10-22 20:57:30 +00:00
|
|
|
|
},
|
|
|
|
|
"ldap": {
|
2023-06-05 21:40:39 +00:00
|
|
|
|
"userDN": "士b",
|
2021-12-08 23:03:57 +00:00
|
|
|
|
"extraRefreshAttributes": {
|
2023-06-05 21:40:39 +00:00
|
|
|
|
"O灞浛a齙\\蹼偦歛ơ 皦pSǬŝ": "Džķ?吭匞饫Ƽĝ\"zvư",
|
|
|
|
|
"f跞@)¿,ɭS隑ip偶宾儮猷": "面@yȝƋ鬯犦獢9c5¤"
|
2021-12-08 23:03:57 +00:00
|
|
|
|
}
|
2021-10-22 20:57:30 +00:00
|
|
|
|
},
|
|
|
|
|
"activedirectory": {
|
2023-06-05 21:40:39 +00:00
|
|
|
|
"userDN": "置b",
|
2021-12-08 23:03:57 +00:00
|
|
|
|
"extraRefreshAttributes": {
|
2023-06-05 21:40:39 +00:00
|
|
|
|
"MN\u0026錝D肁Ŷɽ蔒PR}Ųʓl{鼐": "$+溪ŸȢŒų崓ļ憽",
|
|
|
|
|
"ĩŦʀ宍D挟": "q萮左/篣AÚƄŕ~čfVLPC諡}",
|
|
|
|
|
"姧骦:駝重EȫʆɵʮGɃ": "囤1+,Ȳ齠@ɍB鳛Nč乿ƔǴę鏶"
|
2021-12-08 23:03:57 +00:00
|
|
|
|
}
|
2021-10-06 22:28:13 +00:00
|
|
|
|
}
|
2020-12-17 21:14:20 +00:00
|
|
|
|
}
|
2020-11-19 04:30:05 +00:00
|
|
|
|
},
|
|
|
|
|
"requestedAudience": [
|
2023-06-05 21:40:39 +00:00
|
|
|
|
"ň"
|
2020-11-19 04:30:05 +00:00
|
|
|
|
],
|
|
|
|
|
"grantedAudience": [
|
2023-06-05 21:40:39 +00:00
|
|
|
|
"â融貵捠ʼn",
|
|
|
|
|
"d鞕ȸ腿tʏƲ%}ſ¯Ɣ 籌Tǘ乚Ȥ2"
|
2020-11-19 04:30:05 +00:00
|
|
|
|
]
|
2021-10-06 22:28:13 +00:00
|
|
|
|
},
|
2023-06-05 21:40:39 +00:00
|
|
|
|
"version": "5"
|
2021-10-06 22:28:13 +00:00
|
|
|
|
}`
|