Skip to content
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

Concerns Regarding client_secret Exposure in Client-Side Applications #1188

Open
swiiny opened this issue Apr 7, 2024 · 17 comments
Open

Concerns Regarding client_secret Exposure in Client-Side Applications #1188

swiiny opened this issue Apr 7, 2024 · 17 comments
Labels
question Further information is requested

Comments

@swiiny
Copy link

swiiny commented Apr 7, 2024

description

I have identified a potential security concern related to the handling of the client_secret within a client-side application. Currently, the client_secret is included in the client-side code, making it accessible from the user's browser. This practice may expose us to security risks, including unauthorized access and potential misuse of the client_secret. I aim to assess the associated risks and explore more secure alternatives if necessary.

The client_secret is a sensitive piece of information intended to authenticate the client application to the server, ensuring that requests made to the server are from a trusted source. In traditional server-side applications, the client_secret is securely stored on the server and is not exposed to the public. However, in our case, the client_secret is included in the JavaScript bundle sent to the client, making it accessible to anyone who inspects the browser's resources.

Potential Risks

  1. Exposure to malicious users: Malicious users can easily extract the client_secret from the browser, potentially using it to impersonate the client application.
  2. Security compliance issues: Storing the client_secret client-side may violate security compliance requirements or best practices, potentially leading to legal or reputational repercussions.

Questions and Next Steps

  1. Risk Assessment: What is the level of risk associated with exposing the client_secret in the client-side code? Are there any documented incidents or breaches attributed to this practice?
  2. Technical Solutions: Can we implement any technical solutions that allow us to customize the https://<provider>/token endpoint to call our backend instead of doing it client side?
@pamapa
Copy link
Member

pamapa commented Apr 8, 2024

Very true, in case you can not protect a client_secret, e.g. when running on a client on customer side like browser. Do not use client_secret. The preferred way to use is using the PKCE code flow....

@swiiny
Copy link
Author

swiiny commented Apr 8, 2024

Thanks for your answer @pamapa!

I initially questioned the necessity of keeping certain secrets private, given the library is solely for browser use, suggesting perhaps those secrets might not require stringent protection. However, a significant challenge has arisen: the authentication provider in question does not support PKCE code flow. This absence necessitates an alternative method to securely authenticate users, hence my proposal to override react-oidc-context library's default <provider>/token endpoint call.

To address this, I propose the implementation of a function capable of:

  • Waiting for a custom function's execution: This custom function should perform the necessary actions to acquire an access_token from the API.
  • Returning the access_token: Once obtained, this token would then be used to ensure secure interaction with the frontend web application.

This approach is particularly vital for the application I'm developing, aimed at a marketplace. It requires that users authenticate with the original application to obtain an access token. This token, in turn, enables my application to securely interact with the parent application's API, ensuring a secure and seamless integration.

@pamapa
Copy link
Member

pamapa commented Apr 9, 2024

the authentication provider in question does not support PKCE code flow.

This library and the underlying library has a strong focus on PKCE code flow, respectively on OAuth2.0. I do not intent to support anything else.

BTW: even if you acquire an access_token from the API within the browser context you are exposing it to a potential attacker.... even if it is only part of the memory for a very short time.

@zach-betz-hln
Copy link

React apps are usually assigned a "public" client which does not require a client_secret, and instead relies on PKCE code flow mentioned like @pamapa said.

Now... @swiiny I ran into a similar situation years ago on a project where the identity provider did not support PKCE code flow. Our workaround then was to add a "pass through" api to one of our backend services. It essentially intercepted the request from the React app, then added the client_secret to the request and forwarded it along to the identity provider.

@bitbythecron
Copy link

bitbythecron commented Apr 16, 2024

@pamapa (or @zach-betz-hln ) I was just about to ask a similar question but saw this one, so I figure I'd jump in: I'm looking to integrate my React app (using this library) with Keycloak using Authorization Code with PKCE. I was able to get a basic integration flow with Keycloak working using the code examples in your READE (so thank you!). Does this library use PKCE by default (meaning do the code examples in your README essentially create an Authorization Code w/ PKCE flow by default) or are there other configurations/code I'd need to implement to leverage PKCE? Thanks again!

@pamapa
Copy link
Member

pamapa commented Apr 16, 2024

Does this library use PKCE by default (meaning do the code examples in your README essentially create an Authorization Code w/ PKCE flow by default) or are there other configurations/code I'd need to implement to leverage PKCE?

Yes, best practice (Authorization Code w/ PKCE) is the default.

@bitbythecron
Copy link

bitbythecron commented Apr 16, 2024

Yes, best practice (Authorization Code w/ PKCE) is the default.

Thanks @pamapa , my understanding was that, with PKCE, when the user authenticates, a hashed Code Challenge is sent to the authorization server, along with the Hashing Method (SHA-256, etc.). And then, on each subsequent request for a token, the client sends over a Code Verifier, which is the plaintext version of the hashed Code Challenge. If the server supports Authorization Code w/ PKCE, it will hash the Code Verifier with the indicated Hashing Method and compare it to the Code Challenge. If they match, the token request is granted, etc.

Where and how do I set these (the Code Challenge and Code Verifier) in react-oidc-context?

@pamapa
Copy link
Member

pamapa commented Apr 17, 2024

Where and how do I set these (the Code Challenge and Code Verifier) in react-oidc-context?

They are part of the underling library. Have a look at oidc-client-ts. e.g. https://github.com/authts/oidc-client-ts/blob/main/src/utils/CryptoUtils.ts

@pamapa pamapa added the question Further information is requested label Apr 17, 2024
@bitbythecron
Copy link

Thanks again @pamapa , so just verifying, it looks like there's nothing I need to explicitly set, this library will generate and send Code Challenges/Verifiers automatically for me using oidc-client-ts?

@pamapa
Copy link
Member

pamapa commented Apr 17, 2024

Thanks again @pamapa , so just verifying, it looks like there's nothing I need to explicitly set, this library will generate and send Code Challenges/Verifiers automatically for me using oidc-client-ts?

Yes! For you to verify:

  • ensure response_type is not set (default is "code") or explicitly set to "code"
  • ensure you are not using client_secret

In case you want to see the process you can enable logging like described here: https://authts.github.io/oidc-client-ts/#md:logging

@swiiny
Copy link
Author

swiiny commented Apr 18, 2024

React apps are usually assigned a "public" client which does not require a client_secret, and instead relies on PKCE code flow mentioned like @pamapa said.

Now... @swiiny I ran into a similar situation years ago on a project where the identity provider did not support PKCE code flow. Our workaround then was to add a "pass through" api to one of our backend services. It essentially intercepted the request from the React app, then added the client_secret to the request and forwarded it along to the identity provider.

Indeed it's a client only library so I was surprised to see the ability to provide the client_secret directly. It could be a good idea to add a warning in case of someone not aware about the risks try to use this package.

I'll check to implement a pass through API as you suggested or will build my own solution based on oidc-client-ts directly so it can be used within Nextjs applications and its server actions.

@zach-betz-hln
Copy link

@swiiny out of curiosity, what identity provider are you using that doesn't support Authorization Code w/ PKCE flow ?

@zach-betz-hln
Copy link

@swiiny in case it helps #1208

@swiiny
Copy link
Author

swiiny commented Apr 22, 2024

@zach-betz-hln it is with this API https://docs.bexio.com/

Thanks for #1208! 👀

@zach-betz-hln
Copy link

I see what you mean...

From their Authentication docs in the Authorization Code Flow section:

  1. The user clicks Login within the regular web application.
  2. The web application redirects the user to the /authorize endpoint of the bexio OpenID Connect service.
  3. The bexio OpenID Connect Service displays the login page.
  4. The user authenticates and sees a consent page listing the permissions bexio will give to the web application.
  5. The user is redirected back to the web application with an Authorization Code.
  6. The web application sends this code to the bexio OpenID Connect service (/token endpoint) along with the application's Client ID and Client Secret.
  7. bexio verifies the code, Client ID, and Client Secret.
  8. An ID Token and Access Token (and optionally, a Refresh Token) is returned to the web application.
  9. The web application uses the Access Token to call the bexio API.
  10. The bexio API responds with requested data.

For this step:

  1. The web application sends this code to the bexio OpenID Connect service (/token endpoint) along with the application's Client ID and Client Secret.

In order to remove the client_secret from your React code, that's where you may need to develop a custom pass-through API to workaround bexio's implementation.

Roughly:

  • Your React app calls your custom /token API
  • This API appends the client_secret to the request, then forwards it to bexio
  • bexio responds, then your API forwards this response to your React app

@swiiny
Copy link
Author

swiiny commented May 6, 2024

Hey @zach-betz-hln, thanks for your answer!

Being able to override the default call to /token and make it pass through my own api is my original request through this PR. This is where the issue starts basically, I want to use this package as it comes with many abstractions and a working well react implementation but while using the signIn function I can't have a custom function call to override what happens in the step 6 you highlighted above ⬆️

@zach-betz-hln
Copy link

Hey @swiiny I should have been more specific.

The workaround I had in mind was to put your identity provider behind a reverse proxy (nginx, apache, etc.). Then when the /token endpoint is called, append the client_secret to the request body.

I realize your original ask was for a way to override the call through this library's api, which I can't speak to.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
question Further information is requested
Projects
None yet
Development

No branches or pull requests

4 participants