Giter Site home page Giter Site logo

soofstad / react-oauth2-pkce Goto Github PK

View Code? Open in Web Editor NEW
112.0 6.0 46.0 217 KB

Provider agnostic OAuth2 Authorization Code flow with PKCE for React

License: MIT License

TypeScript 86.28% HTML 0.81% JavaScript 12.91%
github jwt oauth2 react authentication azure azuread microsoft oauth fusionauth keycloak amazon cognito openid-connect wso2

react-oauth2-pkce's Issues

Feature: Add extra auth params for the authorization endpoint

Amazon Cognito allows for two custom parameters at the time of sending the request to the authorization endpoint to specify the identity provider that will be used. These parameters are identity_provider and idp_identifier as described in their documentation https://docs.aws.amazon.com/cognito/latest/developerguide/authorization-endpoint.html.

As of now the package allows for extraAuthParams at the time of getting the tokens but not at the time of getting the login url when using the /authorize endpoint.

Is there any reason this is not supported or is it possible to enable extraAuthorizationParams that can be passed to the /authorize endpoint?

Bug: Bug: Bad authorization state console error even when autoLogin set to false

Steps To Reproduce

Setup component as below:

const authConfig: TAuthConfig = {
  autoLogin: false,
...
};

function AuthenticationWrapper({ children }) {
  return <AuthProvider authConfig={authConfig}>{children}</AuthProvider>;
}

Load app.

The current behavior

Console error:

  console.error
    Bad authorization state. Refreshing the page might solve the issue.

      at node_modules/react-oauth2-code-pkce/dist/AuthContext.js:133:25

The expected behavior

Should not produce error until we are expecting to be logged in. Or perhaps it should just be a warning.

Bug: redirect uri and Logout

2 things.

1.Document mentions that redirect uri is supposed to be the location where auth should take user after user logs out. But after we log in, we are redirected to the "redirectUri"
2. I logout user using the logOut() function while having the logoutRedirect to some other path, but it still takes me to the "redirectUri"
3. Note: my xyz.com/logout which is my logout endpoint, it is not completely operational, but What I expect at the moment is that after clicking out, I should be redirected to xyz.com/logout.

Any thoughts about this

Steps To Reproduce

Here is my authconfig

const authConfig: TAuthConfig = {
  clientId: process.env.REACT_APP_CLIENT_ID || '',
  authorizationEndpoint:
    process.env.REACT_APP_OAUTH_AUTHORIZATION_ENDPOINT || '',
  tokenEndpoint: process.env.REACT_APP_TOKEN_ENDPOINT || '',
  redirectUri: 'http://localhost:3000/oauth/callback',
  scope: process.env.REACT_APP_SCOPE,
  autoLogin:false,
  postLogin: () => {
    window.location.replace('http://localhost:3000');
  },
  logoutEndpoint:'https://xyz.com/logout',
  logoutRedirect:'https://xyz.com/logout'
};

Cheers
Thanks.

No unsafe functions or expressions

Accepting any as function parameter can lead to unsafe member access, misused promises, unsafe assignment, its a source to many bugs.. It accepts class declarations etc. which will throw at runtime as they will not be called with new.

No restricted globals

location is considered a confusing browser global and should use the window prefix

typescript error

typescript complains about the initialized value of authcontext.

i think this can be fixed by initializing AuthContext on line 14 in AuthContext.tsx

change: export const AuthContext = createContext({})
to
export const AuthContext = createContext({token: "NONE"}) for example

image

Refresh Token returned as 401

I'm using ping federate as provider, when ever making a request with grant type as refresh token getting 401, reason Ping Federate is expecting client secret which I have configured as extra params, is it possible to pass the extra parameters in the refresh token request, like original request ?

Feature: Broader assumptions on failed refresh requests

Failed refresh requests are not always identified to be caused by an expired refresh_token.

The way this is handled today is too brittle, and requires manually maintaining a list of known error responses from all possible authentication servers.

Suggestion:
Make a broader assumption like if the refresh request fails with a "400" (40x?) status code, that it's caused by an expired refresh_token.

Bug: Logout does not log out on auth provider

Steps To Reproduce

  1. setup logoutEndpoint
  2. use logOut()
  3. watch storage being cleared
  4. login()
  5. Doesn't redirect to authprovider/login and auto logs in

The current behavior

Currently doesn't "log out", only clears client side info but doesn't redirect to logout endpoint.

The expected behavior

Should log out on server and client.

Bug: When developing with <React.StrictMode> in React 18, will get an error because the same auth code is used twice

With Strict Mode starting in React 18, whenever a component mounts in development, React will simulate immediately unmounting and remounting the component.

As a result, the same auth code will be sent to the auth provider twice, and the second time will get an error.

As React document mentions, it requires components to be resilient to effects being mounted and destroyed multiple times.

Steps To Reproduce

root.render( <React.StrictMode> <AuthProvider authConfig={authConfig}> <LoginInfo /> </AuthProvider> </React.StrictMode> );

  1. The error will appear

The current behavior

The useEffect() in AuthProvider is fired twice, and the same auth code is sent to the auth provider twice.

The expected behavior

The same auth code should not be sent to the auth provider twice.

Btw, I am not a front-end developer, so maybe my understanding of the error is not correct.

Feature Request: Ability to override access token expire actions

Ability to override access token expire actions

Steps To Reproduce

1.Login
2. Wait for token to expire
3. It logins automatically

The current behavior

It logins automatically

The expected behavior

It logins automatically, but would be nice to have the possibility to override this behavior and logout, for example.

Bug: Auto login triggered when fetching tokens

Since v1.13.4 we've an issue that the auto login is triggered after returned and login is in progress. This causes an infinity loop authorizing and the fetchTokens call cancelled.

Steps To Reproduce

  1. Set autoConfig: true
  2. Go to application, redirected to OAuth login
  3. Enter details and return to React application
  4. React application is trying to fetch tokens and login is called while fetching tokens

The current behavior

Application loops infinity to authorize endpoint (autoLogin login()) and trying to fetch token

The expected behavior

No concurrent token fetch and auto login trigger

Cover React 18 in the dependencies

Is there a reason why the latest release of react-oauth-pkce (1.4.0) has React dependencies (react and @types/react) that are versioned to not include v18?

Bug: session not ended if browser was closed

logged in first time with ouath2 verification & shut the browser and second time takes me straight through.
no localstorage cleanup option on configuration.

Steps To Reproduce

  1. open page, autologin with oauth2
  2. close browser
  3. open browser, open page, user was already logged

The current behavior

open browser, open page, user was already logged

The expected behavior

open browser, open page, ouath2 login must be started

Silently refresh access tokens in the background

Hello,

First of all, thanks for the library, saved me a lot of implementation time.

I have a question about refresh tokens.
In the features it said "Silently refreshes short-lived access tokens in the background", now, I know the callback "onRefreshTokenExpire" but on that one the only event that can be given is the login (which causes the app to go back and forth through the processes which results in page refresh).

Am I missing something that can be configured to actually silently refresh tokens (so the user does not get a page refresh) or I need to implement a custom function myself to obtain that?

Thanks in advance.

๐Ÿ’ก [REQUEST] - Next.js support out of the box

Summary

Frameworks that relies on any kind of server-side-rendering does not have window.local-/sessionStorage available.
Since these functions are called when loading the <AuthProvider> in this library, a compile error will be raised; localStorage is not defined.

Basic Example

The most basic usage in Next.js will produce this

export default function App({ Component, pageProps }: AppPropsWithLayout) {

  return (
      <AuthProvider authConfig={authConfig(router)}>
            <Layout>
      </AuthProvider>
    )
  );
}

Known workaround is to only render the component after a useEffect has run. The library will then never run on server.

export default function App({ Component, pageProps }: AppPropsWithLayout) {
  const [isClient, setIsClient] = useState(false);
  useEffect(() => setIsClient(true), []);

  return (
    isClient && (
      <AuthProvider authConfig={authConfig(router)}>
            <Layout>
      </AuthProvider>
    )
  );
}

Drawbacks

Depends on the implementation, possibly more complexity.

Unresolved questions

No response

Implementation PR

No response

Reference Issues

#90

Feature: Option to disable "auto login"

For some cases, automatically logging in on page load might not be suitable.
There should be a option to set loginOnPageLoad: false.

The login() function also needs to be exported by the <AuthContext> so manual login is possible.

Bug: Cannot logout of the auth provider

I use AWS Cognito as the auth provider. As required by Cognito, the below screenshot shows the logout GET request format.

Screenshot from 2022-12-22 09-41-28

In the code, it sends token, token_type_hint, client_id and post_logout_redirect_uri as the parameters, which does not match the Cognito format.

Steps To Reproduce

  1. Configure Cognito as the auth provider
  2. Login and logout

The current behavior

Can logout from my app. When log out from Cognito, shows error: Required String parameter 'redirect_uri' is not present.

The expected behavior

Can logout from my app and Cognito.

๐Ÿ’ก [REQUEST] - Allow send cookies on the method postWithXForm

Summary

Hey guys.

I'm not considering it as a bug, because it works fine for some providers. But, sometimes we need pass a credential cookie for custom providers, and by default, the fetch method not sends cookies on HTTP requests.

To resolve it, I just created a request interceptor for the fetch method, adding the property "credentials": "include" in the options object. I'm not sure if it can be added as a parameter on the context configurations, like "allowCookies" or something like that, to be flexible for the dev choose use it or no.

Basic Example

That's the created interceptor:


window.fetch = (...args): any => {
  let [resource, options] = args;
  if (options) {
    options["credentials"] = "include";
    options["mode"] = "cors";
  }
  return originalFetch(resource, options);
};

Drawbacks

Changing the property, you need explicitly the CORS configuration, like the code below.

Unresolved questions

No response

Implementation PR

No response

Reference Issues

No response

Unable to redirect to logout page without a refresh token

I'm having issues with redirecting my app to the auth providers logout page. In short, my auth provider do not issue any refresh tokens (this is apparently the case in a Client Credentials Grant scheme). Hence, I'm blocked from redirecting by this line:

if (config?.logoutEndpoint && refreshToken) redirectToLogout(config, refreshToken, idToken, state, logoutHint)

My auth provider uses OIDC. Per their spec ( https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout ) a token/refresh token shouldn't be neccessary (though an id_token is recommended).

Thoughts?

๐Ÿ’ก [REQUEST] - Implement a check on function availability

Summary

It would be nice to users of the package, if there where some checks on the availability of some of the functions that could be unavailable.
This check could print some more verbose error message that would help in debugging.

Basic Example

Instead of getting;

TypeError: Cannot read properties of undefined (reading 'digest')

when calling window.crypto.subtle.digest('SHA-256', bytes).
We could print Error: The context/environment is not secure, and does not support the 'crypto.subtle' module. See: https://developer.mozilla.org/en-US/docs/Web/API/Crypto/subtle for details

I believe there could be more examples like this:

  • TextEncoder?

Drawbacks

None

Unresolved questions

No response

Implementation PR

No response

Reference Issues

#72

Bug: Bad authorization state. Refreshing the page might solve the issue. (

Hello,
In my react React I've got an error when I first time enter the page:
image

image
THis may indicates that in my url there is no 'code' query. Indeed there is no 'code' query and after reloading the page it is.

I tried to change settings in my router:
image
Right now default page redirects to /dashboard. But even when I delete it doesn't work.

In TAuthConfig I have autoLogin: true and clearURL: false,

I'am using this verson:
image

My expected behaviour - login wihout refreshing the page. Of course I can force login() method but it does not do the trick for me in this case

Bug: Opening the application multiple times will lead to `onRefreshTokenExpire` in all but one instance

If a user duplicates the tab / has multiple browser windows open with an application running react-oauth2-pkce and the token expires, both tabs will try to refresh the token automatically. However, only one of them will succeed, for the second attempt, the refresh token will already have been consumed and therefore the refreshing doesn't work.

I am perfectly aware, this is likely an edge case, but it still generated quite some confusion among our users.

Steps To Reproduce

  1. Open a tab with a react application using react-oauth2-pkce and authenticate
  2. Duplicate the tab
  3. Wait until the token is close to expiration / until the lib will try to refresh the token
  4. See one tab successfully refresh the token, the other one prompting the user about the expired token (executing onRefreshTokenExpire)

The current behavior

I think this is related to the fact that tokens are stored in local storage which is shared across all tabs on the same domain. It's nice because the duplicated tab does not need to fetch a token on its own and can just use the one that's already in local storage but in the refresh case, it's odd. Basically, this is a race condition: Whichever instance of the application first attempts to refresh the token, will succeed and then cause all other instances to fail / prompt to reload the page.

The expected behavior

From a user perspective, I would just expect the token to be silently refreshed of course - in all tabs. But on a more technical level, I am not sure how to best achieve it. It would either require cross-tabs communication or storing the tokens not in localStorage but some tab specific storage like sessionStorage.

Bug: Session ends within 10 minutes with React 17.

Hey! I am using react 17 on my application and I noticed that after login, the ROCP_tokenExpire variable on localStorage has the right value of 1 hour, but the application will end the session within 10 minutes and navigate back to the authorization endpoint.

Looks like refreshAccessToken method is using the stale values for tokenExpire and refreshTokenExpire, which is the default FALLBACK_EXPIRE_TIME.
Refreshing the page after authentication seems to solve the problem.
The option tokenExpiresIn doesn't work as well.

I was able to fix it locally by setting the dependencies to useEffect, but would like to confirm if this is the best approach.

https://github.com/soofstad/react-oauth2-pkce/blob/main/src/AuthContext.tsx#L182

useEffect(() => {
  interval = setInterval(() => refreshAccessToken(), 10000) // eslint-disable-line
  return () => clearInterval(interval)
}, [token, tokenExpire, refreshToken, refreshTokenExpire, refreshInProgress])

Steps To Reproduce

  1. Copy the sample project
  2. Downgrade both react and react-dom to v17.0.0
  3. Change the index.js render to use the old react dom render method:
ReactDOM.render(
  <>
    <div>
      <h1>Demo using the &apos;react-oauth2-code-pkce&apos; package</h1>
      <p>
        Github:{' '}
        <a href="https://github.com/soofstad/react-oauth2-pkce">https://github.com/soofstad/react-oauth2-pkce</a>
      </p>
      <p>
        NPM:{' '}
        <a href="https://www.npmjs.com/package/react-oauth2-code-pkce">
          https://www.npmjs.com/package/react-oauth2-code-pkce
        </a>
      </p>
    </div>
    <AuthProvider authConfig={softheonConfig}>
      <LoginInfo />
    </AuthProvider>
  </>,
  document.querySelector('#root')
)

The current behavior

The session ends in 10 minutes

The expected behavior

The session must least for 1 hour

Bug: The idTokenData is not set

The idTokenData is not set.

Steps To Reproduce

  1. Get access token and id token

The current behavior

  1. {idToken} has value that can be parsed in JWT format
  2. {idTokenData} shows nothing and {JSON.stringify(idTokenData, null, 2)} shows nothing

The expected behavior

{JSON.stringify(idTokenData, null, 2)} should show the payload of the id token.

Session storage does not work when user is redirected back to the application after resetting password.

As you've already add a configuration to decide which storage to use, is it better to replace the sessionStorage in authentication file with that configuration as well? The reason I'm saying so is that I have one use case:

  1. when the user tries to reset password
  2. the user receives an email
  3. the user clicks the button in the email, and opens a page in a new tab to input their password
  4. then the user is redirected back to the application
  5. but got this error message

image

This is due to the code verifier being saved in session storage. https://github.com/soofstad/react-oauth2-pkce/blob/949676328afb5d7d8287765dab0cb08844e45c41/src/authentication.ts#L69

Please take a consideration, thank you.

๐Ÿ’ก [REQUEST] - Add ability to use the extraTokenParameters into fetchWithRefreshToken

Summary

In my case, i need to pass same extra params into fetchTokens query, and on refetchToken. But with the current implementation of fetchWithRefreshToken function i can`t do it.
It will be great if you will add it.

Basic Example

Code that i write to do it

export const fetchWithRefreshToken = (props: {
  config: TInternalConfig
  refreshToken: string
}): Promise<TTokenResponse> => {
  const { config, refreshToken } = props
  const refreshRequest: TTokenRequestForRefresh = {
    grant_type: 'refresh_token',
    refresh_token: refreshToken,
    scope: config.scope,
    client_id: config.clientId,
    redirect_uri: config.redirectUri,
    ...config.extraTokenParameters,
  }
  return postTokenRequest(config.tokenEndpoint, refreshRequest)
}

Drawbacks

No response

Unresolved questions

No response

Implementation PR

No response

Reference Issues

No response

Bug: Infinite loop using Microsoft Provider

Hi guys.

Using Microsoft as a login provider, after the login we have a infinite loop when the page is redirected, even using the "cleanURL" param as true.

Steps To Reproduce

  1. Set https://login.microsoftonline.com/organizations/oauth2/v2.0/authorize/ as authorizationEndpoint
  2. Set https://login.microsoftonline.com/organizations/oauth2/v2.0/token as tokenEndpoint

Configure your own clientId, and after the login you will see the infinite loop refresh, between microsoft and your application.

React peer dependency erro

Got a peer dependency error. react@"17.0.2" I think it needs to be ^18.1.0? Was trying to install it on CRA, but it failed on resolve.

AutoLogin Needs fixing

Hi,
Just wanted to share my expierence with AutoLogin.

So I was implementing the package as specified in the document where I encountered an issue with the autoLogin Param of this package.
What happens is that when we open our home page (in my case localhost:3000). Autologin which is set to TRUE redirects user to the required authentication point. But if the user after being redirecting, doesnot log in and tries to open home page again(localhost:3000), autoLogin fails to redirect him this time. The interesting thing is Now If the user refreshes his page (meaning that, he is loading the page for the third time), autoLogin will work and user will be redirected to the authentication point.
Summarizing, On Odd number of Page load count, autologin works. On even numbers, it doesnt.

Steps to replicate:
I replicated this using both of the examples given by you guyz on github but I changed the autoLogin to TRUE.

  • Load localhost:3000 -> You will be redirected to authenticator. Now dont log in there. Just go back to localhost: 3000 without logging in. This time you wont be ridrected. Now refresh your page and voila you will be.

๐Ÿ’ก [REQUEST] - Optionally store state in sessionStorage

Summary

Provide the option to store all state used by the package in session storage.

This could be useful if developers want to force a new login on every new browser. For public/shared computers this could make sense.

Basic Example

  1. A user opens a browser and navigates to webpage that uses react-oauth2-code-pkce
  2. Redirected to login server, and checks "Do not remember me", or something similar
  3. Completes task in webpage
  4. Closes browser
  5. Another user on the same desktop session navigates to the same webpage.
  6. Should be forced to login again

Drawbacks

None specifically

Unresolved questions

No response

Implementation PR

No response

Reference Issues

#69

Feature: idTokenData new IAuthContext value

It could be great if we had idTokenData in the useContext.

Today I have to reprocess the idToken decoding inside my react code.
It adds a few complexity when it could be done easily directly in the library as for tokenData.

The decoded idToken can be used for example to display the groups of the logged user.

interface IAuthContext {
  // The access token. This is what you will use for authentication against protected API's
  token: string
  // An object with all the properties encoded in the token (username, email, etc.)
  tokenData?: TTokenData
  // Login the user
  login: () => void  
  // Logout the user from the auth provider
  logOut: () => void
  // Keeps any errors that occured during login or token fetching/refreshing. 
  error: string | null
  // The idToken, if it was returned along with the access token
  idToken?: string
  // An object with all the properties encoded in the ID token (username, groups, etc.)
  idTokenData?: TTokenData
  // If the <AuthProvider> is done fetching tokens or not. Usefull for controlling page rendering
  loginInProgress: boolean
}

Error with Webpack bundler

when using the library in an SharePoint Framework Webpart with React the bundle fails with a Webpack error:
./node_modules/react-oauth2-code-pkce/dist/AuthContext.js 77:19

Module parse failed: Unexpected token (77:19)
You may need an appropriate loader to handle this file type, currently no loaders are configured to process this file. See https://webpack.js.org/concepts#loaders
|     function logOut() {
|         clearStorage();
>         if (config?.logoutEndpoint && refreshToken)
|             (0, authentication_1.redirectToLogout)(config, refreshToken);
|     }
 @ ./node_modules/react-oauth2-code-pkce/dist/index.js 4:20-44

any ideas?

๐Ÿ’ก [REQUEST] - Enable separate decoding steps for token/id_token

Summary

My OAuth provider uses id_token as the JWT instead of token, but react-oauth2-code-pkce's AuthContext tries to decode token first, and fails immediately without getting to id_token:

try {
if (config.decodeToken) setTokenData(decodeJWT(response.access_token))
if (config.decodeToken && response.id_token) setIdTokenData(decodeJWT(response.id_token))
} catch (e) {
setError((e as Error).message)
}

try {
setTokenData(decodeJWT(token))
if (idToken) setIdTokenData(decodeJWT(idToken))
} catch (e) {
setError((e as Error).message)
}
}

If they were decoded separately, I could at least use idTokenData. My current workaround is to disable decodeToken, and parse idToken with my own decoder.

Basic Example

Option 1: Decode the tokens separately

if (decodeToken) {
  try {
    setTokenData(decodeJWT(token))
  } catch (e) {
    setError((e as Error).message)
  }

  try {
    if (idToken) setIdTokenData(decodeJWT(idToken))
  } catch (e) {
    setError((e as Error).message)
  }
}

Option 2: Add a separate config flag to decode idToken

if (decodeToken) {
  try {
    setTokenData(decodeJWT(token))
  } catch (e) {
    setError((e as Error).message)
  }
}
if (decodeIdToken) {
  try {
    if (idToken) setIdTokenData(decodeJWT(idToken))
  } catch (e) {
    setError((e as Error).message)
  }
}

Drawbacks

Option 1's Drawbacks

It potentially performs unnecessary work trying to decode token when only idToken is needed. If token decoding fails, an error is shown in the browser console, which could annoy developers.

Option 2's Drawbacks

It's extra code/config to handle.

Unresolved questions

No response

Implementation PR

No response

Reference Issues

No response

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.