-
Notifications
You must be signed in to change notification settings - Fork 2.8k
WebSocket Next: enable users to update SecurityIdentity before previous bearer access token expires #47675
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
base: main
Are you sure you want to change the base?
Conversation
michalvavrik
commented
May 3, 2025
- closes WebSocket-Next - Refresh OIDC AccessToken without reconnection #43434
Status for workflow
|
🎊 PR Preview bf02409 has been successfully built and deployed to https://quarkus-pr-main-47675-preview.surge.sh/version/main/guides/
|
Status for workflow
|
@@ -1073,6 +1073,51 @@ When you plan to use bearer access tokens during the opening WebSocket handshake | |||
* Use a custom WebSocket ticket system which supplies a random token with the HTML page which hosts the JavaScript WebSockets client which must provide this token during the initial handshake request as a query parameter. | |||
==== | |||
|
|||
Before the bearer access token sent on the initial HTTP request expires, you can send a new bearer access token as part of a message and update current `SecurityIdentity` attached to the WebSocket server connection: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How is this new token can be obtained ? The current one is bound to the connection at the upgrade time.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How did you obtained original token used at the upgrade time? Obtain it the same way. You pass it in a DTO that contains message and optionally metadata. You can pass it into the endpoint or hide that in the decoder (but I prefer the endpoint, hence the example). Tests speak for itself
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Can front end client use refresh token stored in a session storage and obtain access token?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
How did you obtained original token used at the upgrade time?
I'm assuming SPA used the authorization code flow
Obtain it the same way.
I don't think it works the same way for the token refresh.
So the proposal is for SPA to run a background task and check the current access token expiry time and refresh ?
What I don't know if OIDC scripts allow SPA access the access token expiry time - as some of these tokens are binary tokens.
The other thing I don't know, can one do HTTP calls while working with WS API ?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'm assuming SPA used the authorization code flow
I don't know how to make WS work with authorization code flow on the Backend. I have looked into it before and I also raised it in the linked issue comments. Even if authorization code flow started before the WebSocket was opened, you still don't have a have to link session cookie with opened WebSocket. Anyway, I opened this PR for a bearer token authentication only.
Obtain it the same way.
I don't think it works the same way for the token refresh.
[1]
I need more information to understand why not, I can trust you, but then there is no point continue because this PR just won't do. No point wasting time on a review. My thinking was that:
- For client credentials (like server to server websocket communication) you can use client id and secret to obtain token in the background.
- For implicit grant flow (if there is still someone using it) SPA can send in the background and gets a token based on valid browser session
- For code flow on the SPA side, you store session tokens in the browser session store, you keep refreshing token and you have direct access to the access token. I have personal experience with it, because I was maintaining Angular SPA in production for 2 years that communicated with multiple backends and we wrote interceptor to generated front-end clients that always set authorization header for tokens retrieved from the browser storage. we used https://github.com/manfredsteyer/angular-oauth2-oidc, it was hell to get it right, but then it worked
So the proposal is for SPA to run a background task and check the current access token expiry time and refresh ?
proposal is for SPA to get access token itself in a ways described right above, please see [1]
of this comment.
What I don't know if OIDC scripts allow SPA access the access token expiry time - as some of these tokens are binary tokens.
I have no idea about that, but I remember that in our SPA we got tokens from Keycloak and we knew token expiration time (I want to say 30 minutes for access token and 18 hours for the refresh token, but honestly, I am not sure if I am not imaging it). It didn't change for us, maybe they can just check it out before? E.g. they don't exactly need to automate it, just add timed task.
The other thing I don't know, can one do HTTP calls while working with WS API ?
Typescript has multithreading, I think having background tasks like updating feeds on your SPA is a standard (e.g. on the newspage you have breaking news and that can by done by polling and HTTP requests). But I do speak about frameworks like Angular and React, no idea what you do with vanilla JS or node.js etc.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@michalvavrik Sure, SPA can do token refreshes
Can it really work though such that the script that works with the current WS connection can accept a refreshed token and forward this token to Quarkus ?
If we could confirm it then it would be great, IMHO there is no rush with the fix being merged
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'll write an example by the end of the week.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If we could confirm it then it would be great, IMHO there is no rush with the fix being merged
I have more plans for WS Next and while I didn't mention it (no reason to mention it), what I have added to this PR will allow me to easily fix quarkiverse/quarkus-langchain4j#1418 in the follow up. I don't want to keep this PR on hold, but let's wait until I have the example front end application. I'll inform you when I am done.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@michalvavrik Thanks, IMHO it will be worth it because this sample SPA can be used as a base for Step-Up authentication demos.
We can also add some simple example, similar to https://quarkus.io/guides/security-oidc-bearer-token-authentication#single-page-applications, without expecting it to be complete, to give users an idea how a token can be passed over to the existing WS connection
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
We can also add some simple example, similar to https://quarkus.io/guides/security-oidc-bearer-token-authentication#single-page-applications, without expecting it to be complete, to give users an idea how a token can be passed over to the existing WS connection
ok, I'll look into it, but no promises on the https://quarkus.io/guides/security-oidc-bearer-token-authentication#single-page-applications part because it may require complex stuff, I don't know until I do it
If you can see a way to do the work to support Quarkus LangChain4j fix without having to support the token injection, then please consider a dedicated PR - simply because, if that is considered a bug fix, it can be backported to 3.20 LTS, as this PR is unlikely to be backported |
WebSocketSecurity webSocketSecurity; | ||
|
||
@OnTextMessage | ||
String echo(RequestDto request) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think this method will have to match the security level of the HTTP upgrade.
For example, if HTTP Upgrade happened at @RolesAllowed("Admin")
, then only verifying the validity of the token which would only match @Authenticated
level, would raise a risk of decreasing the original security identity strength, for example, the new token which is about to replace an admin
level identity may only have a user
role.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK, that's easy to do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@michalvavrik Sounds good, we do it indirectly in the code flow too when refreshing tokens, since it is done in scope of the request.
In case of this PR, the new token is coming via the front channel so the extra care is needed.
We might end up doing extra checks as well...
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I am not sure if I understand what you mean, but my plan is: there is a check that assures principal name is same (so they can't change alice for bob), but for roles and permissions, we just need to reapply standard security annotations (HTTP policies sounds like out of scope to me), which I think is easy. There is a code that prevents repeated security checks, I'll just disable the code when the identity refresh is enabled.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@michalvavrik I was actually thinking about it too, how do you prevent an identity swap, so having the principal name check is good, but a few more checks may be necessary, such as a sub
claim - sub
is a unique number associated with a given user that did an original login, or even a more thorough identity comparison, but we can think about it later.
IMHO, whatever the security constraints were applied to HTTP upgrade should be reapplied at the token injection time because this is how we do in the code flow refresh. For example, when the ID and access token are refreshed, the refreshed ID token goes through the complete authentication and authorization cycle before the method can be called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the token is binary then we can update OidcIdentityProvider to record sub
value from the introspection response as an identity attribute, something like that, so that you can easily access this sub
My plan is to use the fact that this PR stores security support on the connection. It should fix it inherently, as either LangChain4j uses the same duplicate context where we have the connection, or a new duplicated context created from the original one (there is a hierarchy and this will be parent one). Anyway, I don't think it is important, the user that reported it didn't bother to add reproducer so I can't prove it is actual fix and fixing it after this get in is ok as this could be very edge case. Also, the way I plan to propose it (it may never get it), it could possibly provide further improvements, but not sort of code changes you want to backport. It is just idea, maybe never gets in. |
@michalvavrik Once the sample SPA sending a bearer token to HTTP upgrade is available, you can use it as a reproducer for the Quarkus LangChain4j issue too, so overall, it is worth it, not only to check the PR idea works, but also as a base for StepUp authentication and other experiments |