Giter VIP home page Giter VIP logo

react-oauth2-pkce's Introduction

react-oauth2-code-pkce

GitHub license NPM Version NPM Downloads npm bundle size CI

React package for OAuth2 Authorization Code flow with PKCE

Adhering to the RFCs recommendations, cryptographically sound, and with zero dependencies!

What is OAuth2 Authorization Code Flow with Proof Key for Code Exchange?

Short version;
The modern and secure way to do authentication for mobile and web applications!

Long version;
https://www.rfc-editor.org/rfc/rfc6749.html
https://datatracker.ietf.org/doc/html/rfc7636
https://oauth.net/2/pkce/

Features

  • Authorization provider-agnostic. Works equally well with all OAuth2 authentication servers following the OAuth2 spec
  • Supports OpenID Connect (idTokens)
  • Pre- and Post-login callbacks
  • Session expired callback
  • Silently refreshes short-lived access tokens in the background
  • Decodes JWT's
  • A total of ~440 lines of code, easy for anyone to audit and understand

Example

import { AuthContext, AuthProvider, TAuthConfig, TRefreshTokenExpiredEvent } from "react-oauth2-code-pkce"

const authConfig: TAuthConfig = {
  clientId: 'myClientID',
  authorizationEndpoint: 'https://myAuthProvider.com/auth',
  tokenEndpoint: 'https://myAuthProvider.com/token',
  redirectUri: 'http://localhost:3000/',
  scope: 'someScope openid',
  onRefreshTokenExpire: (event: TRefreshTokenExpiredEvent) => event.logIn(undefined, undefined, "popup"),
}

const UserInfo = (): JSX.Element => {
    const {token, tokenData} = useContext<IAuthContext>(AuthContext)

    return <>
        <h4>Access Token</h4>
        <pre>{token}</pre>
        <h4>User Information from JWT</h4>
        <pre>{JSON.stringify(tokenData, null, 2)}</pre>
    </>
}

ReactDOM.render(<AuthProvider authConfig={authConfig}>
        <UserInfo/>
    </AuthProvider>
    , document.getElementById('root'),
)

For more advanced examples, see ./examples/.

Install

The package is available on npmjs.com here; https://www.npmjs.com/package/react-oauth2-code-pkce

npm install react-oauth2-code-pkce

API

IAuthContext values

The object that's returned by useContext(AuthContext) provides these values;

interface IAuthContext {
  // The access token. This is what you will use for authentication against protected Web API's
  token: string
  // An object with all the properties encoded in the token (username, email, etc.), if the token is a JWT 
  tokenData?: TTokenData
  // Function to trigger login. 
  // If you want to use 'state', you might want to set 'clearURL' configuration parameter to 'false'.
  logIn: (state?: string, additionalParameters?: { [key: string]: string | boolean | number }, method: 'redirect' | 'popup' = 'redirect') => void
  // Function to trigger logout from authentication provider. You may provide optional 'state', and 'logout_hint' values.
  // See https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout for details.
  logOut: (state?: string, logoutHint?: string, additionalParameters?: { [key: string]: string | boolean | number }) => void
  // Keeps any errors that occured during login, token fetching/refreshing, decoding, etc.. 
  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
}

Configuration parameters

react-oauth2-code-pkce's goal is to "just work" with any authentication provider that either supports the OAuth2 or OpenID Connect (OIDC) standards.
However, many authentication providers are not following these standards, or have extended them. With this in mind, if you are experiencing any problems, a good place to start is to see if the provider expects some custom parameters. If they do, these can be injected into the different calls with these configuration options;

  • extraAuthParameters
  • extraTokenParameters
  • extraLogoutParameters

The <AuthProvider> takes a config object that supports these parameters;

type TAuthConfig = {
  // ID of your app at the authentication provider
  clientId: string  // Required
  // URL for the authentication endpoint at the authentication provider
  authorizationEndpoint: string  // Required
  // URL for the token endpoint at the authentication provider
  tokenEndpoint: string  // Required
  // Which URL the auth provider should redirect the user to after successful authentication/login
  redirectUri: string  // Required
  // Which scopes to request for the auth token
  scope?: string  // default: ''
  // Optional state value. Will often make more sense to provide the state in a call to the 'logIn()' function
  state?: string // default: null
  // Which URL to call for logging out of the auth provider
  logoutEndpoint?: string  // default: null
  // Which URL the auth provider should redirect the user to after logout
  logoutRedirect?: string  // default: null
  // Optionally provide a callback function to run _before_ the
  // user is redirected to the auth server for login
  preLogin?: () => void  // default: () => null
  // Optionally provide a callback function to run _after_ the
  // user has been redirected back from the auth server
  postLogin?: () => void  // default: () => null
  // Optional callback function for the 'refreshTokenExpired' event.
  // You likely want to display a message saying the user need to log in again. A page refresh is enough.
  onRefreshTokenExpire?: (event: TRefreshTokenExpiredEvent) => void  // default: undefined
  // Whether or not to decode the access token (should be set to 'false' if the access token is not a JWT (e.g. from Github))
  // If `false`, 'tokenData' will be 'undefined' from the <AuthContext>
  decodeToken?: boolean  // default: true
  // By default, the package will automatically redirect the user to the login server if not already logged in.
  // If set to false, you need to call the "logIn()" function to log in (e.g. with a "Log in" button)
  autoLogin?: boolean  // default: true
  // Store login state in 'localStorage' or 'sessionStorage'
  // If set to 'session', no login state is persisted by 'react-oauth2-code-pkce` when the browser closes.
  // NOTE: Many authentication servers will keep the client logged in by cookies. You should therefore use 
  // the logOut() function to properly log out the client. Or configure your server not to issue cookies.
  storage?: 'local' | 'session'  // default: 'local'
  // Sets the prefix used when storing login state
  storageKeyPrefix?: string // default: 'ROCP_'
  // Set to false if you need to access the urlParameters sent back from the login server.
  clearURL?: boolean  // default: true
  // Can be used to provide any non-standard parameters to the authentication request
  extraAuthParameters?: { [key: string]: string | boolean | number }  // default: null
  // Can be used to provide any non-standard parameters to the token request
  extraTokenParameters?: { [key: string]: string | boolean | number } // default: null
  // Can be used to provide any non-standard parameters to the logout request
  extraLogoutParameters?: { [key: string]: string | boolean | number } // default: null
  // Superseded by 'extraTokenParameters' options. Will be deprecated in 2.0
  extraAuthParams?: { [key: string]: string | boolean | number }  // default: null
  // Can be used if auth provider doesn't return access token expiration time in token response
  tokenExpiresIn?: number // default: null
  // Can be used if auth provider doesn't return refresh token expiration time in token response
  refreshTokenExpiresIn?: number // default: null
  // Defines the expiration strategy for the refresh token.
  // - 'renewable': The refresh token's expiration time is renewed each time it is used, getting a new validity period.
  // - 'absolute': The refresh token's expiration time is fixed from its initial issuance and does not change, regardless of how many times it is used.
  refreshTokenExpiryStrategy?: 'renewable' | 'absolute' // default: renewable
  // Whether or not to post 'scope' when refreshing the access token
  refreshWithScope?: boolean // default: true
}

Common issues

Sessions expire too quickly

A session expire happens when the refresh_token is no longer valid and can't be used to fetch a new valid access_token. This is governed by the expires_in, and refresh_expires_in | refresh_token_expires_in, in the token response. If the response does not contain these values, the library assumes a quite conservative value. You should configure your IDP (Identity Provider) to send these, but if that is not possible, you can set them explicitly with the config parameters tokenExpiresIn and refreshTokenExpiresIn.

Fails to compile with Next.js

This library expects to have a localStorage (or sessionStorage) available. That is not the case when compiling Next.js projects serverside.
See: #90 for a solution.

Error Bad authorization state...

This is most likely to happen if the authentication at the identity provider got aborted in some way. You might also see the error Expected to find a '?code=' parameter in the URL by now. Did the authentication get aborted or interrupted? in the console.

First of all, you should handle any errors the library throws. Usually, hinting at the user reload the page is enough.

Some known causes for this is that instead of logging in at the auth provider, the user "Registers" or "Reset password" or something similar instead. Any such functions should be handled outside of this library, with separate buttons/links than the "Log in" button.

After redirect back from auth provider with ?code, no token request is made

If you are using libraries that intercept any fetch()-requests made. For example @tanstack/react-query. That can cause issues for the AuthProviders token fetching. This can be solved by not wrapping the <AuthProvider> in any such library.

This could also happen if some routes in your app are not wrapped by the <AuthProvider>.

The page randomly refreshes in the middle of a session

This will happen if you haven't provided a callback-function for the onRefreshTokenExpire config parameter, and the refresh token expires. You probably want to implement some kind of "alert/message/banner", saying that the session has expired and that the user needs to log in again. Either by refreshing the page, or clicking a "Log in" button.

Develop

  1. Update the 'authConfig' object in src/index.js with config from your authorization server and application
  2. Install node_modules -> $ yarn install
  3. Run -> $ yarn start

Contribute

You are most welcome to create issues and pull requests :)

react-oauth2-pkce's People

Contributors

anfreita avatar anthony-vito avatar blackburn29 avatar daannet avatar fpapon avatar getsnoopy avatar jesusthehun avatar kleysonfiretail avatar sebastianvitterso avatar soofstad avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

react-oauth2-pkce's Issues

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?

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.

๐Ÿ’ก [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

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.

๐Ÿ’ก [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

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: 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

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

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?

๐Ÿ’ก [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

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?

๐Ÿ’ก [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

๐Ÿ’ก [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

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.

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.

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 ?

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

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.

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.

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
}

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] - 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

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.

No restricted globals

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

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.

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.

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: 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.

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.

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.

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

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?

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

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.