react-oauth2-code-pkce
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 {
token: string
tokenData?: TTokenData
logIn: (state?: string, additionalParameters?: { [key: string]: string | boolean | number }, method: 'redirect' | 'popup' = 'redirect') => void
logOut: (state?: string, logoutHint?: string, additionalParameters?: { [key: string]: string | boolean | number }) => void
error: string | null
idToken?: string
idTokenData?: TTokenData
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 = {
clientId: string
authorizationEndpoint: string
tokenEndpoint: string
redirectUri: string
scope?: string
state?: string
logoutEndpoint?: string
logoutRedirect?: string
preLogin?: () => void
postLogin?: () => void
onRefreshTokenExpire?: (event: TRefreshTokenExpiredEvent) => void
decodeToken?: boolean
autoLogin?: boolean
storage?: 'local' | 'session'
storageKeyPrefix?: string
clearURL?: boolean
extraAuthParameters?: { [key: string]: string | boolean | number }
extraTokenParameters?: { [key: string]: string | boolean | number }
extraLogoutParameters?: { [key: string]: string | boolean | number }
extraAuthParams?: { [key: string]: string | boolean | number }
tokenExpiresIn?: number
refreshTokenExpiresIn?: number
refreshTokenExpiryStrategy?: 'renewable' | 'absolute'
refreshWithScope?: boolean
}
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: https://github.com/soofstad/react-oauth2-pkce/discussions/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
- Update the 'authConfig' object in
src/index.js
with config from your authorization server and application - Install node_modules ->
$ yarn install
- Run ->
$ yarn start
Contribute
You are most welcome to create issues and pull requests :)