diff --git a/site/content/docs/howto/configure-auth-for-webapps.md b/site/content/docs/howto/configure-auth-for-webapps.md new file mode 100644 index 00000000..1eaa13f0 --- /dev/null +++ b/site/content/docs/howto/configure-auth-for-webapps.md @@ -0,0 +1,347 @@ +--- +title: Using the Pinniped Supervisor to provide authentication for web applications +description: Allow your Kubernetes cluster users to authenticate into web apps using the same identities. +cascade: + layout: docs +menu: + docs: + name: Web Application Authentication + weight: 800 + parent: howtos +--- +The Pinniped Supervisor is an [OpenID Connect (OIDC)](https://openid.net/connect/) issuer that can be used to bring +your user identities from an external identity provider into your Kubernetes clusters for all your `kubectl` users. +It can also be used to bring those same identities to web applications that are intended for use by the same users. +For example, a Kubernetes dashboard web application for cluster developers could use the Supervisor as its OIDC +identity provider. + +This guide explains how to use the Supervisor to provide authentication services for a web application. + +## Prerequisites + +This guide assumes that you have installed and configured the Pinniped Supervisor, and configured it with an +external identity provider, as described in the other guides. + +This guide also assumes that you have a web application which supports configuring an OIDC provider for user +authentication, or that you are developing such a web application. From the point of view of the Supervisor, +your webapp is called a "client" ([as defined in the OAuth 2.0 spec](https://www.rfc-editor.org/rfc/rfc6749#section-1.1)). + +Typically, the web application should use the OIDC client support from its web application development +framework (e.g. Spring, Rails, Django, etc.) to implement authentication. The Supervisor requires that: +- Clients must use the [OIDC authorization code flow](https://openid.net/specs/openid-connect-core-1_0.html#CodeFlowAuth). + Clients must + use `code` as the [response_type](https://openid.net/specs/openid-connect-core-1_0.html#AuthorizationExamples) + at the authorization endpoint. +- Clients must use [PKCE](https://oauth.net/2/pkce/) during the authorization code flow. +- Clients must be confidential clients, meaning that they have a client ID and client secret. + Clients must use [client secret basic auth](https://datatracker.ietf.org/doc/html/rfc6749#section-2.3.1) + for authentication at the token endpoint. +- Clients must use `query` as the + [response_mode](https://openid.net/specs/openid-connect-core-1_0.html#AuthRequest) at the authorization endpoint, + or not specify the `response_mode` param, which defaults to `query`. + +Most web application frameworks offer all these capabilities in their OAuth2/OIDC libraries. + +## Create an OIDCClient + +For each web application, the administrator of the Pinniped Supervisor will create an OIDCClient describing what +that web application is allowed to do: + +```yaml +apiVersion: config.supervisor.pinniped.dev/v1alpha1 +kind: OIDCClient +metadata: + # name must have client.oauth.pinniped.dev- prefix + name: client.oauth.pinniped.dev-my-webapp-client + namespace: supervisor # must be in the same namespace as the Supervisor +spec: + allowedRedirectURIs: + - https://my-webapp.example.com/callback + allowedGrantTypes: + - authorization_code + - refresh_token + - urn:ietf:params:oauth:grant-type:token-exchange + allowedScopes: + - openid + - offline_access + - pinniped:request-audience + - username + - groups +``` + +If you've saved this into a file `my-oidc-client.yaml`, then install it into your cluster using: + +```sh +kubectl apply -f my-oidc-client.yaml +``` + +Do not share OIDCClients between multiple web applications. Each web application should have its own OIDCClient. + +The `name` of the OIDCClient will be the client ID used by the web application in the OIDC flows. + +The `allowedGrantTypes` and `allowedScopes` decides what the web application is allowed to do with respect to +authentication. There are several typical combinations of these settings: + +1. A web application which is allowed to use the Supervisor for authentication, and furthermore is allowed to + authenticate into Kubernetes clusters and perform actions on behalf of the users (using the user's identity): + + ```yaml + allowedGrantTypes: + - authorization_code + - refresh_token + - urn:ietf:params:oauth:grant-type:token-exchange + allowedScopes: + - openid + - offline_access + - pinniped:request-audience + - username + - groups + ``` + +2. A web application which is allowed to use the Supervisor for authentication, but cannot perform actions on + Kubernetes clusters. + + ```yaml + allowedGrantTypes: + - authorization_code + - refresh_token + allowedScopes: + - openid + - offline_access + - username + # "groups" can be excluded from this list when the webapp does + # not need to see the group memberships of the users. + - groups + ``` + +3. A web application which is allowed to use the Supervisor for authentication, but cannot see the username or + group memberships of the authenticated users, and cannot perform actions on Kubernetes clusters. + + ```yaml + allowedGrantTypes: + - authorization_code + - refresh_token + allowedScopes: + - openid + - offline_access + ``` + +## Create a client secret for the OIDCClient + +For each OIDCClient created by the Supervisor administrator, the administrator will also need to generate a client +secret for the client. The client secrets are random strings auto-generated by the Supervisor upon request. +The plaintext secret will only be returned once upon creation. + +```sh +cat <}}) +tutorial, then the next sections will apply. + +### Cluster-scoped ID tokens + +The ID token issued at the end of the authorization code flow contains the user's Kubernetes identity. However, +this ID token is typically not used directly to provide authentication to the Kubernetes clusters' API servers. + +In a typical configuration, the Pinniped Concierge is installed on each workload cluster and is configured with a +JWTAuthenticator resource to validate ID tokens issued by the Pinniped Supervisor. However, typically each workload +cluster's JWTAuthenticator is configured to validate a unique audience value (`aud` claim) of the ID tokens. +This ensures that an ID token which is used to access one workload cluster cannot also be used to access other workload +clusters, to limit the impact of a leaked token. + +In this typical configuration, the client must make an extra API call to the Supervisor after the authorization code +flow before it can access a particular workload cluster, in order to get a cluster-scoped ID token for a specific +workload cluster (technically, for the audience value of that workload cluster).This request is made to the token +endpoint, using parameters described in [RFC 8693](https://datatracker.ietf.org/doc/html/rfc8693). This request +requires that the access token was granted the `username` and `pinniped:request-audience` scopes in the authorization +code flow, and preferably was also granted the `groups` scope. It also requires that the client's OIDCClient +configuration allows it to use the `urn:ietf:params:oauth:grant-type:token-exchange` grant type. + +The client has already called the Supervisor FederationDomain's `/.well-known/openid-configuration` discovery endpoint +at the beginning of the authorization code flow, so the client is already aware of the location of the +FederationDomain's token endpoint. The client makes an HTTPS request to the token endpoint to request a +cluster-scoped ID token. The client sends its client ID and client secret as a basic auth header. It sends the +Supervisor-issued access token as the `subject_token` param to identify the user's active session, along with the +other required parameters. + +``` +POST /federation-domain-path/oauth2/token HTTP/1.1 +Host: my-supervisor.example.com +Content-Type: application/x-www-form-urlencoded +Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW + +grant_type=urn:ietf:params:oauth:grant-type:token-exchange + &subject_token= + &subject_token_type=urn:ietf:params:oauth:token-type:access_token + &requested_token_type=urn:ietf:params:oauth:token-type:jwt + &audience= +``` + +A successful request will result in a `200 OK` response with a JSON body. One of the top-level keys in the returned JSON object +will be `id_token`, and the value at that key will be the cluster-scoped ID token. + +This exchange is typically repeated for each workload cluster, right before the client needs to access the Kubernetes +API of that workload cluster. + +### mTLS client certificates + +Once the client has a cluster-scoped ID token for a particular workload cluster, the next step towards accessing the +Kubernetes API of that workload cluster, in a typical configuration, is to request an mTLS client certificate from +that workload cluster. The client certificate will act as the credential for the Kubernetes API server. + +This is done by making a request to the `/apis/login.concierge.pinniped.dev/v1alpha1/tokencredentialrequests` API of +the Kubernetes API of that cluster. This API is an aggregated API hosted on the Kubernetes API server, but behind the +scenes is actually served by the Pinniped Concierge. It can be accessed just like any other Kubernetes API. It does +not require any authentication on the request. + +The details of the request and response formats are documented in the +[API docs](https://github.com/vmware-tanzu/pinniped/blob/main/generated/{{< latestcodegenversion >}}/README.adoc#tokencredentialrequest). + +Here is a sample YAML representation of a request: + +```yaml +apiVersion: login.concierge.pinniped.dev/v1alpha1 +kind: TokenCredentialRequest +spec: + token: + authenticator: + apiGroup: authentication.concierge.pinniped.dev/v1alpha1 + kind: JWTAuthenticator + name: +``` + +And here is a sample YAML representation of a successful response: + +```yaml +apiVersion: login.concierge.pinniped.dev/v1alpha1 +kind: TokenCredentialRequest +status: + credential: + expirationTimestamp: + clientCertificateData: + clientKeyData: +``` + +The returned mTLS client certificate will contain the user's identity (username and groups) copied from the cluster-scoped +ID token. It may be used to make calls to the Kubernetes API as that user, until it expires. + +These mTLS client certificates are short-lived, typically good for about 5-15 minutes. After it expires, a client which +wishes to make more Kubernetes API calls will need to perform an OIDC refresh request to the Supervisor to get +a new access token, and then repeat the steps described above to get new cluster-scoped ID tokens and mTLS client +certificates. Requiring these steps to be repeated often ensures that the user's session with the external identity +provider is validated often, to ensure any changes to the user's level of access will quickly be reflected in the +Kubernetes clusters.