Background: For dynamic clients, the groups scope is not always allowed
and/or requested by the client, so it will not always be granted by the
Supervisor for an authorization request.
Previously, when the groups scope was not granted, we would skip
searching for upstream groups in some scenarios.
This commit changes the behavior of authorization flows so that even
when the groups scope is not granted we still search for the upstream
group memberships as configured, and we pass the upstream group
memberships into any configured identity transformations. The identity
transformations could potentially reject the user's authentication based
on their upstream group membership.
When the groups scope is not granted, we don't include the groups in
the final Supervisor-issued ID token. This behavior is not changed.
Create an interface to abstract the upstream IDP from the
authorize, IDP discovery, callback, choose IDP, and login
endpoints. This commit does not refactor the token endpoint,
which will be refactored in a similar way in the next commit.
- continued refactoring the auth handler to share more code between
the two supported browserless flows: OIDC and LDAP/AD
- the upstreamldap package should not know about the concept of
OIDC granted scopes, so refactored it to be a skipGroups bool
- Current goboring only allows TLS 1.2.
- The next goboring will allow TLS 1.2 and TLS 1.3. We got a preview
of this when the Go team upgraded goboring in Go 1.21.6, but then
downgraded it again in the next Go releases.
The release of Go 1.21.6 includes the new boring crypto when compiling
with FIPS enabled. See https://go.dev/doc/devel/release#go1.21.0 and
https://github.com/golang/go/issues/64717.
This new version of boring crypto allows the use of TLS v1.3 for the
first time, so we changed the Pinniped code to use TLS v1.3 where
appropriate when compiled with the FIPS compiler. It also changed the
allowed TLS v1.2 ciphers, so we updated those as well.
After this commit, the project must be compiled by at least Go v1.21.6
when compiling in fips mode. The hack/Dockerfile_fips was already
updated to use that version of Go in a previous commit.
Co-authored-by: Benjamin A. Petersen <ben@benjaminapetersen.me>
Made the switch wherever possible, but since fosite still uses the old
gopkg.in/square/go-jose.v2 there was one test where we still need to use
it as a direct dependency.
The unused-parameter linter became stricter, so we adjust it to
allow unused params that start with underscore. It can be nice to keep
unused param names when implementing an interface sometimes, to help
readers understand why it is unused in that particular implementation.
To make the subject of the downstream ID token more unique when
there are multiple IDPs. It is possible to define two IDPs in a
FederationDomain using the same identity provider CR, in which
case the only thing that would make the subject claim different
is adding the IDP display name into the values of the subject claim.
Used this as an opportunity to refactor how some tests were
making assertions about error strings.
New test helpers make it easy for an error string to be expected as an
exact string, as a string built using sprintf, as a regexp, or as a
string built to include the platform-specific x509 error string.
All of these helpers can be used in a single `wantErr` field of a test
table. They can be used for both unit tests and integration tests.
Co-authored-by: Benjamin A. Petersen <ben@benjaminapetersen.me>
- Specify mappings on OIDCIdentityProvider.spec.claims.additionalClaimMappings
- Advertise additionalClaims in the OIDC discovery endpoint under claims_supported
Co-authored-by: Ryan Richard <richardry@vmware.com>
Co-authored-by: Joshua Casey <joshuatcasey@gmail.com>
Also fix some tests that were broken by bumping golang and dependencies
in the previous commits.
Note that in addition to changes made to satisfy the linter which do not
impact the behavior of the code, this commit also adds ReadHeaderTimeout
to all usages of http.Server to satisfy the linter (and because it
seemed like a good suggestion).
When the token exchange grant type is used to get a cluster-scoped
ID token, the returned token has a new audience value. The client ID
of the client which performed the authorization was lost. This didn't
matter before, since the only client was `pinniped-cli`, but now that
dynamic clients can be registered, the information would be lost in the
cluster-scoped ID token. It could be useful for logging, tracing, or
auditing, so preserve the information by putting the client ID into the
`azp` claim in every ID token (authcode exchange, clsuter-scoped, and
refreshed ID tokens).
- 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"