Replies: 3 comments
-
|
I wanted to follow up my original post with some insights gleaned from an initial implementation of OIDC. Let me start by saying that I am strongly motivated to keep token management simple. If we get this right, most of the Aerie team should be able to explain exactly what guarantees the OIDC system provides and how it works. Since my initial post, I have realized that storing tokens in-memory subtly works against this goal. It adds inherent complexity without providing any additional real safety against a motivated attacker. Last week, I discovered an edge cases where server-side initiated refresh of tokens is needed. There are ways to make a server-side request with stale access or id tokens and a valid refresh token. For example, you can have multiple tabs open, access Aerie UI in one, close that Aerie tab, wait a few minutes for your tokens to expire and return to Aerie in a new tab. The expected behavior of the system is dependent on enough other factors to amount to "undefined." The solution should offer these guarantees:
The first two guarantees rely on a server side hook that performs these steps: 1. Remove any invalid access or identity tokens. Invalid means that a token has expired, malformed, does not match the expected signature, issuer, or audience. This step simplifies the next step. 2. Refresh tokens if (and only if a refresh token is present). This step doesn't need to check if an access or identity is token is expired, because it would be removed during the first step if it is. New tokens are validated and then set as httpOnly: false cookies. Aside: A third step could also be added to this hook to improve developer experience w.r.t. defining access related rules for routes. It would set server side local values to contain the decoded access + identity token and a list of roles extracted from a specific claim in the access token. The third guarantee requires client-side functionality. I have experimented with a solution that uses a combination of the CookieStore, setTimeout, and clearTimeout APIs to enable refresh of tokens with rescheduling. The refresh behavior relies on server-side code to obtain new refresh tokens as before, but no longer stores a JSON response in-memory on the client side. Because this approach relies strictly on cookies, fetch requests to Gateway and Hasura are expected to read access token values from cookies. I'm eager to hear what others think about this approach. |
Beta Was this translation helpful? Give feedback.
-
|
Overall this looks really good. A few clarification questions below:
That's all I can think of for now. |
Beta Was this translation helpful? Give feedback.
-
|
We have had some synchronous conversations about how to align the existing authentication related behavior in Aerie Gateway and the proposed design introduced by PR-1746. This comment captures some background and rationale for evolving the current authorization-related endpoints in Aerie Gateway toward a set of endpoints that are compliant with OpenID Connect (OIDC). BackgroundAerie Gateway currently provides a set of authentication-related endpoints that abstract away specific identity mechanisms through an adapter pattern. Two adapters are currently supported: JPL CAM – an integration with JPL’s enterprise identity system. No-Auth Mode – a development mode that bypasses authentication for ease of use when an identity system is unavailable or unnecessary. These endpoints are used to issue, validate, and revoke JWT-encoded access tokens, which are then consumed by Aerie UI and Aerie CLI tools. When we began adding OIDC support to Aerie, we found it challenging to use the existing abstractions to implement a full OIDC Authorization Code Flow with Proof Key for Code Exchange (PKCE). For example, the current design does not natively support refresh tokens. Because we wanted to deeply understand OIDC before making architectural changes, and because OIDC best practices often recommend implementing the Authorization Code + PKCE flow in the user application (e.g., the web client or mobile or desktop app), we initially handled OIDC authentication directly within the Aerie UI, using server-side SvelteKit endpoints. Key InsightAs our understanding of OIDC matured, we realized that Aerie Gateway itself could provide a set of OIDC-conforming endpoints that delegate to CAM (or another IdP), effectively replacing the existing authorization endpoints. This would allow the Aerie UI to use a consistent, standards-based authentication flow regardless of which identity system a mission chooses. However, migrating to an OIDC-compliant interface would significantly affect how the Aerie CLI obtains and refreshes tokens. Aerie CLICLI tools typically obtain tokens through one of two approaches: Device Authorization Flow – The CLI presents the user with a link and a short verification code. The user opens the link in a browser, authenticates with the IdP, and the CLI polls until it receives the token set. Headless/Service Account Flow – For automated scenarios, the CLI may use client credentials or a pre-provisioned refresh token to obtain access tokens non-interactively. Both approaches need to handle token refresh seamlessly (e.g., storing and using refresh tokens securely, or re-prompting the user if the refresh token expires). Next Steps
|
Beta Was this translation helpful? Give feedback.
Uh oh!
There was an error while loading. Please reload this page.
-
Background
This proposal is about adding OIDC to Aerie in order to remove a substantial technical barrier to adoption. SAML 2.0 is another standard that may be worth supporting in the future. However, this proposal focuses solely on OIDC.
Priority of Constituencies
Inspired by the Priority of Constituencies principle from the W3C Design Principles, this proposal assumes the following constituencies (collectively referred to as "users") for Aerie:
When trade-offs need to be made, the interests of these different constituents need to be prudently balanced against eachother. This proposal will reference any such priorities, but does not seek to establish them here.
Use Cases
There is one essential use case:
This proposal considers two additional use cases. However, we should discuss if this is reasonable to include as in-scope for our first iteration.
Operators should be able to configure external systems to obtain service account credentials that can be used to interact with Aerie's external machine interfaces (e.g., GraphQL, Gateway).
Operators should be able to configure Aerie to obtain service account credentials so that Aerie can interact with external systems.
Technical Considerations
What authentication flow should be used?
There are several different authorization flows. OIDC Authorization Code Flow with PKCE is the best choice because it’s secure, works seamlessly with SPAs, avoids leaking tokens in URLs, and integrates well with Hasura and Swagger-based services. Implicit flow is obsolete and insecure. Client Credentials flow isn’t for user-based login. Device Code flow is for devices without browsers.
The need for service account support is orthogonal to the authorization flow. External systems will use Client Credentials Flow to get the same type of OIDC (access) tokens from an identity provider and pass them to Aerie machine interfaces. Aerie, will consume service account and user provided tokens the same way.
What additional libraries are required to facilitate authentication?
We want to ensure we use well-maintained libraries instead of implementing our own. Ideally, libraries are released under the same license as Aerie. Any proposed libraries licensed under different terms should be discussed.
What configuration is needed?
A URL to an OIDC provider's well known configuration. The configuration contains URLs to resources for initiating authorization, requesting, and refreshing tokens.
What verification should be performed by the client application?
How are routes protected?
There are two options: a centralized route protecion using
server.hooksor distributed across layouts and pages. Aerie currently uses a hooks based approach. Although it's best to make minimal changes, if there is a time to reconsider this decision, now would be it!What tokens are used?
What values are needed and how are they made available?
The access token is must be available to client side javascript so it can be added in the authorization header for requests to backing services (i.e., Hasura and Gateway). Access tokens should be set to expire within to mitigate the risk of a compromised token. However, this is up to the discretion of the Aerie operator to the extent they control the identity provider.
How are tokens refreshed?
Tokens are refreshed using a hidden component mounted in all pages. The component calculates the remaining shelf-life of a token and uses setTimeout to schedule refreshing tokens (if a refresh token is available). This component watches a reactive access token value to determine when it should schedule a refresh.
This component does not need the refresh token value. It invokes a backend service to perform the refresh token.
How does a user end a session?
Notional Structure for Aerie UI
We should discuss how to arrange modules so that the current authz mechanisms can co-exist. This structure is notional, it should not be construed as a suggestion to either remove the current mechanism or to commingle the two modules.
Libraries
The server module provides reusable functions for creating an Arctic OIDC client (using environment variables), and decoding base64 encode tokens in cookie into JwtPayloads.
No reusable client-side module functions have been identified as of yet.
The refresh component manages access token refresh using setTimeout. It works in tandem with the
/auth/refreshroute and sets client state to newly obtained tokens. This component calculates the time until an access token will expire and refresh a few seconds before scheduled expiration. The current implementation provides a countdown clock and a button to manually invoke the refresh workflow. It is not intended to be visible in normal circumstances.The auth store makes access and identity tokens available in-memory. This approach avoids the need to process cookies client-side and allows us consistently rely on the default cookie setting
httpOnly: false.Routes
The essential routes are:
A fifth route for displaying token details is also implemented, but does not need to exist in a real-world implementation.
Login
Login is implemented as a page because browsers should navigate to a page to initiate login instead of initiating login using
fetch.OIDC login requires a browser redirect to the identity provider. However, you can’t perform a login redirect from the result of a fetch because fetch requests don’t follow redirects that change the browser’s top-level URL.
Login will...
backquery parameter was supplied.All values stored in cookies are short-lived and cleared when the identity provider redirects back to the callback route.
Callback
Callback is also implemented as a page. It will:
/auth/loginand stored in a cookie).OIDC_WELL_KNOWN_URL).OIDC_AUDIENCE)./auth/login(retaining the original location in a 'back' cookie).Refresh
Refresh should be invoked by
fetchrequests from theAuthcomponent a few seconds before the access token will expire. The fetch request automatically sends cookies to this handler. The handler obtains a new set of tokens from the identity provider. It returns a JSON object that contains the access and identity tokens so theAuthcomponent can update in memory values of the same. If this request fails, the server sends a 401 to the client; the client should initiate login but it may display an error instead.Logout
Currently, logout should be invoked by sending a
POSTviaformsubmit orfetchrequests from a navigation component. The/auth/logoutroute will delete the token related cookies and redirect to the home page.For fetch requests, the client application can decide what to do next.
Hasura
We have successfully demonstrated that Hasura will work with an external identity provider and can successfully validate access tokens.
Aerie Gateway
Additional time is required to assess the impact to Aerie Gateway.
Next Steps
Beta Was this translation helpful? Give feedback.
All reactions