Add Authenticator for when serving OIDC as a proxy#877
Add Authenticator for when serving OIDC as a proxy#877DiamondJoseph wants to merge 25 commits intomainfrom
Conversation
fd664f0 to
02232db
Compare
| description: Optional[str] = None, | ||
| ): | ||
| self.model: APIKey = APIKey( | ||
| **{"in": APIKeyIn.header}, name=name, description=description |
There was a problem hiding this comment.
I hate this but I see why it is required (in is a reserved keyword but APIKey uses it as an init arg... using _in does not work, as it is a serialisation alias only).
|
why |
e1a752c to
2ee2a42
Compare
| ) | ||
|
|
||
| async def decode_access_token(self, access_token: str) -> dict[str, Any]: | ||
| keys = httpx.get(self.jwks_uri).raise_for_status().json().get("keys", []) |
There was a problem hiding this comment.
As discussed, use a TTL cache to ensure we do not hammer the AuthN provider
There was a problem hiding this comment.
(for the result of httpx.get(self.jwks_uri))
| ) | ||
|
|
||
| @property | ||
| def oauth2_scheme(self) -> Callable[[Request], str]: |
There was a problem hiding this comment.
| def oauth2_scheme(self) -> Callable[[Request], str]: | |
| def oauth2_scheme(self) -> OAuth2: |
There was a problem hiding this comment.
Just some type hinting for my benefit
There was a problem hiding this comment.
Just some type hinting for my benefit
There was a problem hiding this comment.
For consistency with the serialization/deserialization registries.
| annotation=Optional[List[field_type]], | ||
| ) | ||
| parameters.append(injected_parameter) | ||
| route_with_sig.__signature__ = signature.replace(parameters=parameters) |
There was a problem hiding this comment.
I wonder if much of this could be replaced by replacing search/distinct **kwargs with Unpack[QueryParams] where QueryParams is a TypedDict generated by the QueryRegistry?
|
Test Summary as I hand off this PR for a week of leave: 173 failed, 432 passed, 4 skipped, 4 xfailed, 13 warnings in 510.08s (0:08:30) Summary of changes' intents:
|
| self.metadata = metadata | ||
|
|
||
| def with_session_state(self, state): | ||
| def with_session_state(self, state: dict[str, Any]) -> Self: |
There was a problem hiding this comment.
From a design perspective, it's just expected to still be an Adapter (?)
|
We're down to a tractable number of failures with a hilariously tiny change, pushed above. |
- Prevents having to inject the password late into the oauth2_schema, and allows supporting Proxied credentials
- Such that decode_access_token can be overriden when serving behind proxied OIDC - Removes injection of password into security obj - Removes use of dependency_override which is intended for use in tests
79f8497 to
3ca1344
Compare
|
Rebased on |
|
This is where we are post-rebase: |
tiled/server/authentication.py
Outdated
| ): | ||
| "Mark a Session as revoked so it cannot be refreshed again." | ||
| request.state.endpoint = "auth" | ||
| payload = await decode_access_token(refresh_token.refresh_token) |
There was a problem hiding this comment.
Something's crossed here, where we're decoding the refresh token with decode_access_token.
There was a problem hiding this comment.
Yeah, I think it should be named decode_token but otherwise it's solid: there's a "get access token from request" method and a "decode token" that can take an access or request token.
|
I think the next issue is that With fresh eyes, I think that it would make sense to put the database connection pool in We might stash the |
|
This is where things stand as I set this down. The |
So what exactly is the difference between what is loaded by Settings, which extends Pydantic BaseSettings and so picks up any environment (PR to make it slightly clearer 😎 ) and what goes into the merged_settings object? I think one of the easiest ways to get to decoupling the fastapi creation from the server startup is making use of BaseSettings as the CLI entrypoint- we looked at using BaseSettings for blueapi previously but because we made heavy use of |
I've made a mockup of how this would look for some of the simple commands, but it would require another turning the sock inside out |
|
Using a Settings object as the way to initialize an app seems right. I am less sure about building a CLI application around it, but open to exploring. We have quite a lot of CLI code built on Typer (which is in turn built on Click) that would need to be changed. For this PR, given the scope, the small change we can devise that moves in the generally right direction and passes the tests is the thing. |
WIP: adds an Authenticator implementation for use when serving Tiled behind a proxy.
This is intended be used to define an alternative
decode_access_tokenIn order to inject the desired behaviour for decode_access_token has required a fairly weighty refactor, inverting the creation order of a lot of router objects.
Checklist