React package for OAuth2 Authorization Code flow with PKCE
Adhering to the RFCs recommendations, cryptographically sound, and with zero dependencies!
Short version;
The modern and secure way to do authentication for mobile and web applications!
Long version;
https://oauth.net/2/pkce/
https://datatracker.ietf.org/doc/html/rfc7636
- 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
import { AuthContext, AuthProvider, TAuthConfig } 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',
}
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/
.
For instance, it's recommended to add a "Session expired"-callback like so:
onRefreshTokenExpire: (event) => window.confirm('Session expired. Refresh page to continue using the site?') && event.login(),
.
The package is available on npmjs.com here; https://www.npmjs.com/package/react-oauth2-code-pkce
npm install react-oauth2-code-pkce
The IAuthContext
interface that the AuthContext
returns when called with useContext()
provides these values;
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
// If the <AuthProvider> is done fetching tokens or not. Usefull for controlling page rendering
loginInProgress: boolean
}
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 after loging out
redirectUri: string // Required
// Which scopes to request for the auth token
scope?: string // default: ''
// Which URL to call for logging out of the auth provider
logoutEndpoint?: string // default: null
// Should be used by the auth provider to decide which URL to 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 login 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 login (e.g. with a "Login" button)
autoLogin?: 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
// Superseded by 'extraTokenParameters' options. Will be deprecated in 2.0
extraAuthParams?: { [key: string]: string | boolean | number } // default: null
}
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 login again.
Either by refreshing the page, or clicking a "Login-button".
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.
- Update the 'authConfig' object in
src/index.js
with config from your authorization server and application - Install node_modules ->
$ yarn install
- Run ->
$ yarn start
You are welcome to create issues and pull requests :)