-
Notifications
You must be signed in to change notification settings - Fork 1.3k
New issue
Have a question about this project? # for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “#”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? # to your account
Implementation guidelines for Browser-Based Apps (SPA) #297
Comments
Add a "response_type=jwtCookie" in the authorize endpoind can be helpfull for javascript applications with common domain (currently very used) This cookie can be added using HTTP-only, SameSite and Secure options. |
@jgrandja May I ask if there's a rough timeline for when |
@nickmelis We don't have a timeline yet. See this comment for more info. |
@jgrandja isn't it possible to mitigate the misuse of the rotating "leaked" refresh token, by passing the code_verifier from access token grant flow for the refresh token grant flow? FYI I am not sure how flexible is the OAuth2 architecture on this. |
@rayman245 PKCE is specified for the authorization_code grant only. It's not specified for the refresh_token grant. See Refresh Token Protection for the recommended mitigations. |
@jgrandja is there support (existing or planned) to handle cases when SPA sends multiple concurrent requests with expired access token to BFF, so BFF is required to update token via refresh flow and does this for every request causing all refresh token requests except one to fail OR maybe there was ongoing request propagated downstream at the time new request arrives and causes refresh? |
my spring authorization server works perfectly with my angular but I have only one single issue, On my first login it works correctly, but when I do a logout which is a token revocation, the token is successfully revoked. |
@mahdiraddadi, you may be interested in following progress on gh-266 which I believe is related to your experience. If the context on that issue doesn't clear up your question, please feel free to open a stackoverflow question and post the link here so others can find it, and I can take a look. |
Providing no option for refresh tokens for public clients makes the authorization server a really tough choice compared to other approaches: Taking the user experience into account it is either logging in again every 5 minutes or having long lived access tokens with no way to become aware of misuse compared to token rotation. I would appreciate if this could be re-evaluated. |
@everflux, thanks for the discussion and link.
I believe the summary from that article (see "Using Browser Storage Best Practices Helps Keep Data Secure") comes to the same conclusion as the guidelines recommended by this issue. Specifically:
|
@sjohnr I understand the concern saving a refresh token on a public client, but I don't understand how the BFF approach solves this. Even if the refresh token is stored in the server context, there must be some way for the public client to request a new token when the current token expires. Per the proposal draft:
Further, the draft does not indicate how this is to be implemented:
In this case, the client is still storing a piece of data (e.g. a cookie) that effectively gives it the ability to refresh the access token. I'm still reading the linked info, but at this point, other than the additional control point, I don't understand how this is safer than storing the refresh token directly in the public client. |
I think there are different types of applications to be considered: SPAs in the desktop-replacement class and traditional web sites, possibly augmented with jquery or some other progressive enhancements, possibly even hosted in an environment not considered as an application but a website. For the later I would concur that it is hard to secure correctly. Using a modern framework XSS protection must be turned off forcefully, so the developer should be knowing, what he does. It is really hard to follow the reasoning that a backend or framework developer tries to solve an potential issue (refresh token leakage) for the frontend application by forcing them to use a less secure approach (long lived access token). I don't believe that a technical approach is the right one compared to education, code review etc. I would really appreciate to be able to build desktop-class SPAs with spring in the background using the authorization server. And yes, I might consider a BFF approach, but depending on the requirements it is not always feasible to do so. I would like to see that Spring gives me a choice, as that is - in my opinion - the spirit of Spring. |
Came here from #335 |
@jgrandja thank you so much for all of the discovery you've done here, and for all info you've provided on this subject! while i agree with your takes for the SPA/browser based use case, i have similar questions to @pstorch regrading mobile/native clients that use ACF + PKCE with a public client. These native platforms provide methods for secure storage (e.g. apple's secure enclave system), which i understand are typically used to store sensitive refresh tokens. I'm also aware these native flows do still use the browser during the oauth2 flow, but my understanding there is that the embedded browser apis are sandboxed and more secure. It seems #296 and this issue may be conflating "refresh tokens with public clients" with "secure SPA/browser based apps", and decisions regarding security for browser based applications are inhibiting this refresh token feature for native clients. Are there still vulnerabilities with the native/mobile story for ACF + PKCE with a public client that i'm unaware of? are the concerns with mobile/native still the same as SPA since technically a browser is used during the oauth2 flow? |
Great post, i very much agree on everything written in this issue @jgrandja I have just one question, will the text in this issue ever be added to any official spring documentation, so that it is possible to link to properly. For instance in the authorization server docs, or in spring security docs? |
Great idea @Tandolf. We'll look at adding it to the reference doc. |
@davidmarne-wf thanks for the reply, you wrote:
Im not well versed in the apple eco system when it comes to development and apis, but I fail to see how a sandboxed environment can prevent for instance a XSS attack in a mobile application that stores a tokens that running code can access. |
@Tandolf
Yea I'm no expert on apple APIs either and there isn't a whole lotta info in those docs for ASWebAuthenticationSession. All it says is "the browser is a secure, embedded web view" lol, not exactly sure what all that means. But I agree, that alone surely isn't gonna rule out all XSS attacks for example. The main thing i was trying to call out with my comment is native apps and SPAs are not always susceptible to the same threats/attacks, and have entirely different ecosystems of security tooling and apis to leverage. Take XSS as an example, that is generally browser based attack. Once the oauth2 flow is complete, a native ios application or a jvm based android app is going to be far less susceptible to being tricked into running malicious javascript than a browser based app. For many mobile app use cases the only time javascript is ever executed is to perform the oauth2 flow. So my core question is this: Is the spring teams stance of "no refresh token for public clients" still true for native applications because native apps will still need to use a browser to perform the oauth2 flow? if this is the stance then cool, i get it. or does there need to be another conversation about supporting refresh tokens for public clients, but considering the threat model for native apps rather than browser based apps? |
Thanks all for your inputs! Where can I find a custom implementation for refresh_token for public clients? |
I am developing a command-line utility that leverages the OAuth 2.0 Authorization Code flow with PKCE to obtain an access token, which is used for authenticating with an API server. A notable example of this approach can be seen in the Google Cloud CLI. The gcloud utility temporarily creates a local web server to manage the response from the Authorization Server and facilitates the exchange of the received code for tokens. It's essential to emphasize that these tokens are neither stored nor exposed within the user's browser. While there are valid concerns about securely managing tokens in Single Page Applications (SPAs), it's worth noting that this scenario represents a public client that should not be held to the same restriction of not being granted a refresh token. I could opt for issuing access tokens with extended lifespans (in the span of hours). However, this approach doesn't appear right to me, especially considering that the risk of inadvertently disclosing the refresh token remains consistent with the BFF pattern or other methodologies. I am a big fan of this project but this is definitely a pain point for me. |
Agreed and thanks for providing this use case.
We hear you and it's been a paint point for a few others as well. This is not our intention. Our goal is to make the framework easy to use and configure. And to provide enough flexibility to customize to meet your requirements. Given this, I opened gh-1430 to address this. |
@mberwanger @nickmelis @everflux @mrowley-hundred10 @davidmarne-wf Please take a look at gh-1432, specifically this test and let me know if this enhancement will provide the customization you need. |
@jgrandja That works for me! I really appreciate the quick response and the hard work you and the team have put into this project. |
Excellent @mberwanger ! I'm glad this will solve your use case. We also appreciate the feedback you have given and glad that this project is working well for you 👍 |
@jgrandja , I was able to get refresh tokens for a public client as shown in your test case. I did some further analysis and described a solution at #1430 (comment) , where it fits better. |
Having a spring authorization server, resource server, oauth client. how does react application connect with this OAuth system ? I've read this whole issue. and cannot find one example of this BFF pattern. the two urls in the end are just abstract discussion of a pattern. does anyone here have a link to an article to how an SPA connect to a Spring OAuth backend. I asked this on stackoverflow with no answer so far: question |
@Mehdi-HAFID, please do not cross-post questions on issues, as the team (and community) regularly review Stack Overflow for questions. We prefer to use GitHub issues only for bugs and enhancements. |
Hey just to be sure, although I know it is not recommended but I can assign a "placeholder" client secret for my public client i.e SPA website so that I can force this spring authorization server to generate refresh token, right? |
For everyone struggling with the implementation of a Spring OAuth 2 system with React SPA as the front as I was. I made Nidam just for that. It is open source at https://nidam.derbyware.com. It is a collection of Spring microservices and a React SPA. It includes:
Every single component is well documented on the website above. Also, all is open source. |
That sounds like a terrible work-around, but should indeed "solve" the issue. |
The RFC document describes three structures. I believe this framework should support these three structures to function well. Especially in the third structure (Frontend-only component), it is restrictive to only use access tokens. If the resource server is stateless and cannot use CSRF tokens, access tokens will typically be managed in local storage. This is because cookies are vulnerable to CSRF, and in-memory variables cannot maintain sessions across refreshes or other tabs. However, if there is a refresh token, it can be managed in an http-only cookie, and the access token can be managed in a more secure in-memory variable or web worker, allowing sessions to be maintained across tabs or refreshes. In summary, excluding refresh tokens increases the validity period of access tokens, increasing security threats.I believe that managing refresh tokens in http-only cookies and access tokens in in-memory variables is a more secure method and can maintain UX, compared to managing long-lived access tokens in local storage. |
Hey why don't we just use remember-me cookies from vanilla Spring Security, instead of using refresh token for public client? Basically, the Spring Authorization Server requires the user (resources owner) to be authenticated. However, it does not care which method the user would be authenticated by. On the other hand, the regular Spring Security will automatically authenticate user after validating the remember-me token, forward to Authorization Server's filter chain, issues an access token with no additional authentication is required Moreover, Spring Security has already provided mechanisms to invalidate, persist remember-me cookies or use non-persistent, hash-based token |
The OAuth 2.0 for Browser-Based Apps specification details the security considerations and best practices when developing browser-based applications that use OAuth 2.0.
The purpose of this issue is to:
Overview
The current best practice for browser-based applications is to use the OAuth 2.0 Authorization Code flow with PKCE.
Browser-based applications:
OAuth 2.0 Authorization Servers supporting browser-based applications:
Application Architecture Patterns
There are three primary architectural patterns available when building browser-based applications:
These three architectures have different use cases and considerations.
Common-domain cookies
Backend component
Frontend-only component
Refresh Tokens
With public clients, the risk of a leaked refresh token is greater than leaked access tokens, since an attacker may be able to continue using the stolen refresh token to obtain new access tokens potentially without being detectable by the Authorization Server.
Browser-based applications provide an attacker with several opportunities by which a refresh token can be leaked, just as with access tokens. As such, these applications are considered a higher risk for handling refresh tokens.
Authorization Servers may choose whether or not to issue refresh tokens to browser-based applications. If refresh tokens are issued, Authorization Servers MUST conform to the following:
Refresh Token Rotation
The Authorization Server issues a new refresh token with every access token refresh response. The previous refresh token is invalidated but information about the relationship is retained by the Authorization Server. If a refresh token is compromised and subsequently used by both the attacker and the legitimate client, one of them will present an invalidated refresh token, which will inform the Authorization Server of the breach. The Authorization Server cannot determine which party submitted the invalid refresh token, but it will revoke the active refresh token. This stops the attack at the cost of forcing the legitimate client to obtain a fresh authorization grant.
Sender-Constrained Refresh Tokens
Sender-constrained refresh tokens scope the applicability of a refresh token to a certain sender. This sender is obliged to demonstrate knowledge of a certain secret as prerequisite for the acceptance of the refresh token at the Authorization Server.
A typical flow looks like this:
There exists several proposals to demonstrate the proof of possession in the scope of the OAuth working group:
OAuth 2.0 Mutual-TLS is the most widely implemented and the only standardized sender-constraining method. The use of OAuth 2.0 Mutual-TLS is RECOMMENDED.
Current Feature Support for Browser-Based Apps
Supported
Unsupported
Refresh Tokens for Public Clients
There are no plans to implement refresh tokens for Public Clients, as there are no browser APIs that allow refresh tokens to be stored in a secure way, which would result in an increased attack surface.
Rotating refresh tokens would help reduce the attack surface, however, it cannot eliminate it. For example, a leaked refresh token can be used by an attacker (before the legitimate client) to refresh an access token and ultimately access the protected resources. The exposure would last until the refreshed access token is expired or invalidated.
A sender-constrained refresh token prevents Token Replay and therefore is RECOMMENDED. However, this requires key material (Public-Private key pair) to be configured in the browser agent and used by the browser-based application, resulting in a complicated setup.
Final Recommendation
After a detailed review of OAuth 2.0 for Browser-Based Apps, OAuth 2.0 Security Best Current Practice and OAuth 2.0 Threat Model and Security Considerations, our recommendation when developing browser-based applications is to use the OAuth 2.0 Authorization Code flow with PKCE and a confidential client.
This can be implemented using either of the two architectural patterns:
The text was updated successfully, but these errors were encountered: