Skip to content

Commit 7baefe8

Browse files
committed
OpenIDConnect Policy RFC
Signed-off-by: Alex Snaps <[email protected]>
1 parent 3a1a435 commit 7baefe8

File tree

1 file changed

+181
-0
lines changed

1 file changed

+181
-0
lines changed

rfcs/0000-oidc-policy.md

+181
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,181 @@
1+
# `OpenIDConnectPolicy` - a metapolicy
2+
3+
- Feature Name: oidc-policy
4+
- Start Date: 2025-02-05
5+
- RFC PR: [Kuadrant/architecture#0114](https://github.com/Kuadrant/architecture/pull/114)
6+
- Issue tracking: [Kuadrant/architecture#0000](https://github.com/Kuadrant/architecture/issues/0000)
7+
8+
# Summary
9+
[summary]: #summary
10+
11+
The `OpenIDConnectPolicy` aims at helping users to easily & quickly get started with the Kuadrant stack to setup an
12+
[OpenID Connect flow](https://openid.net/developers/how-connect-works/) without needing to initially understand how to
13+
use Kuadrant's `AuthPolicy` to achieve their goals.
14+
15+
# Motivation
16+
[motivation]: #motivation
17+
18+
There are a few goals this implementation hopes to achieve and set a precedent for introducing more such APIs in
19+
Kuadrant:
20+
21+
- users looking at an example policy would be able to make mostly sense of it without needing to lookup documentation;
22+
- the policy would apply sensible defaults in most cases and only require the very minimal input from the user;
23+
- the idea is to implement it as _metapolicy_, which would only be consumed by a controller to produce a `AuthPolicy`
24+
that would then enforce the flow;
25+
- it is _not_ the goal to expose all the power of the `AuthPolicy` to the user through the new policy, but rather to let
26+
users achieve most of their goals by slowly exploring defaults of the policy and eventually dig down the stack, into
27+
the `AuthPolicy` to expand their use cases when they need it;
28+
- explore how to, overtime, grow the initial minimal API of the policy into a larger one to cover more use cases than
29+
the one currently set as the goal for this RFC
30+
31+
# Guide-level explanation
32+
[guide-level-explanation]: #guide-level-explanation
33+
34+
The `OpenIDConnectPolicy` is a direct policy attachment that attaches to one or multiple
35+
[`HttpRoute`](https://gateway-api.sigs.k8s.io/api-types/httproute/) objects. Targeted routes will require requests to
36+
carry an access token to be authorized through. Should the token be missing or be invalid, the requesting entity gets
37+
redirected to the `provider`'s `authorizationEndpoint` specified in the policy for them to identify.
38+
39+
It is important to note here that the access token is to be negotiated stored by the client. While this may change in
40+
the future, currently the token is to either be provided by the appropriate HTTP header (`Authorization: Bearer`, which
41+
is the default source), or by a Cookie.
42+
43+
## Minimal example
44+
45+
Using a very minimal example, we'll use
46+
[gitlab.com](https://docs.gitlab.com/ee/integration/openid_connect_provider.html) as our provider for an OpenID Connect
47+
flow to protect all requests routed to our application by the `HttpRoute`.
48+
49+
```yaml
50+
apiVersion: kuadrant.io/v1alpha1
51+
kind: OpenIDConnectPolicy
52+
metadata:
53+
name: gitlab
54+
spec:
55+
targetRef:
56+
name: toystore
57+
group: gateway.networking.k8s.io
58+
kind: HttpRoute
59+
provider:
60+
host: gitlab.com
61+
credentials:
62+
clientID: 35001bfef37042bf2fb125e9e8f99f0c719231632ab62a18cbf5220c3d1f8f10
63+
```
64+
65+
First we need to provide the `host` of our `provider`, in this case `gitlab.com`. Kuadrant will default to using
66+
`https` to [query the provider for the additional metadata](https://datatracker.ietf.org/doc/html/rfc8414) it requires
67+
from [gitlab's endpoing](https://gitlab.com/.well-known/openid-configuration), initially the `authorizationEndpoint` to which
68+
to redirect unauthorized requests to.
69+
70+
If the request for the additional metadata fails, all access to the protected resources will be `Unauthorized`. All the
71+
information from the endpoint can be manually specified under `provider` to mitigate these failures. These values would
72+
also override any values successfully obtained from querying the provider's endpoint.
73+
74+
Next, we'll have to identify our request with the provider using the `clientID` that were
75+
provided to us by, in this example, gitlab. You'd probably rather store these in a `credentialsRef` instead as we did
76+
here. A `credentialsRef` would point to a Kubernetes'
77+
[`Secret`](https://kubernetes.io/docs/concepts/configuration/secret/) with the required information. And that's all you
78+
should need to get started with to use an OpenID Connect provider to authorize access to your application.
79+
80+
## The complete `OpenIDConnectPolicy`
81+
82+
Here is an example of the complete proposal for configuring an authorization flow using OpenID Connect:
83+
84+
```yaml
85+
apiVersion: kuadrant.io/v1alpha1
86+
kind: OpenIDConnectPolicy
87+
metadata:
88+
name: gitlab
89+
spec:
90+
targetRef:
91+
name: toystore
92+
group: gateway.networking.k8s.io
93+
kind: HttpRoute
94+
tokenSource: authorizationHeader
95+
provider:
96+
host: gitlab.com
97+
discoveryEndpoint: https://gitlab.com/.well-known/openid-configuration
98+
authorizationEndpoint: https://gitlab.com/oauth/authorize
99+
authorizationEndpointPayload:
100+
query:
101+
client_id: provider.credentials.clientID
102+
redirect_uri: >- route.gateway.listeners[0].hostname + "/oauth/callback"
103+
scope: >- "openid"
104+
response_type: >- "code"
105+
tokenEndpoint: https://gitlab.com/oauth/token
106+
introspectionEndpoint: https://gitlab.com/oauth/introspect
107+
jwksUri: https://gitlab.com/oauth/discovery/keys
108+
credentials:
109+
clientID: 35001bfef37042bf2fb125e9e8f99f0c719231632ab62a18cbf5220c3d1f8f10
110+
clientSecret: gloas-35001bfef37042bf2fb125e9e8f99f0c719231632ab62a18cbf5220c3d1f8f10
111+
redirectUri:
112+
host: example.org
113+
path: /oauth/callback
114+
```
115+
116+
Some of these were inferred in the simple example above, while others are implied and some not even necessary for the
117+
basic case. `tokenEndpoint` and `introspectionEndpoint` are both unnecessary for this flow to succeed. That's because we
118+
use the `authorizationHeader` as the source for our access token. In this flow, we let the client deal with getting the
119+
token from the identity provider, i.e. gitlab, as well as storing it and sending it along each request to our
120+
application.
121+
122+
While we do need the `authorizationEndpoint` to redirect unauthorized requests to the identity provider, as well as the
123+
`jwksUri` to validate the token when one is present, these can be inferred from the `host` by querying the
124+
`discoveryEndpoint` from the provider, which itself is inferred from the `host`.
125+
126+
Kuadrant will also automatically append a query string to the `authorizationEndpoint` when forwarding a user. That will
127+
contain data inferred from the configuration itself, as well as some defaults:
128+
129+
- `client_id`: from the `provider`'s `credential` section
130+
- `redirect_uri`: composed of `host`, inferred if there is a single listener for the `Gateway` the targeted `HttpRoute`
131+
is pointed to; and the `path`, which defaults to `/oauth/callback`
132+
- `scope`: defaults to `openid`
133+
- `response_type`: defaulting to `code`
134+
135+
All of which can be overridden by explicitly configuring the `authorizationEndpointPayload` explicitly. Any entry can
136+
be added, the value needs to be a valid CEL expression and will be encoded depending on the how the payload is to be
137+
sent. Here `query`, each key/value pair will be URL encoded and appended to the `authorizationEndpoint` when
138+
redirecting.
139+
140+
# Reference-level explanation
141+
[reference-level-explanation]: #reference-level-explanation
142+
143+
### WIP
144+
145+
Some of this I think I have sorted out in a proof of concept... But I'm not completely done yet.
146+
I'm trying to see if I can get away with:
147+
- Having the client completely deal with `401 Unauthorized` and have it initiate the flow;
148+
- Having the client intercept the redirect when being redirected from the Identity Provider, rather than needing to
149+
have the `AuthConfig` deal with it so that the flow is completely handled by the client.
150+
151+
I'm doing this by using the information provided here and using it inside the `AuthPolicy` directly for now. All that
152+
then remains is writing the mapper controller for "translating" the `OpenIDConnectPolicy` into an `AuthPolicy`.
153+
154+
# Drawbacks
155+
[drawbacks]: #drawbacks
156+
157+
158+
# Rationale and alternatives
159+
[rationale-and-alternatives]: #rationale-and-alternatives
160+
161+
- This iteration is fairly low risk. It tries to make this all work with multiple OIDC providers, starting with Gitlab.
162+
- Today, no changes are required to `AuthorizationPolicy` or `Authorino` nor any other component in Kuadrant, this only
163+
does some to the heavy lifting for the users.
164+
- The API leaves some space to grow it more complex use cases as we evolve this to support possibly more advanced
165+
use cases.
166+
167+
# Prior art
168+
[prior-art]: #prior-art
169+
170+
- I'm looking into some OIDC client & server side libraries to see how to best shape the API.
171+
172+
# Unresolved questions
173+
[unresolved-questions]: #unresolved-questions
174+
175+
176+
# Future possibilities
177+
[future-possibilities]: #future-possibilities
178+
179+
- Add support for letting the upstream deal with the token exchange?
180+
- Add support for letting Kuadrant (i.e. Authorino?) exchange the token with the Identity Provider and store it?
181+

0 commit comments

Comments
 (0)