{"id":19842028,"url":"https://github.com/soofstad/react-oauth2-pkce","last_synced_at":"2025-05-15T15:04:54.076Z","repository":{"id":40653717,"uuid":"402752550","full_name":"soofstad/react-oauth2-pkce","owner":"soofstad","description":"Provider agnostic OAuth2 Authorization Code flow with PKCE for React","archived":false,"fork":false,"pushed_at":"2025-04-07T05:08:47.000Z","size":216,"stargazers_count":144,"open_issues_count":3,"forks_count":57,"subscribers_count":5,"default_branch":"main","last_synced_at":"2025-05-06T14:13:17.817Z","etag":null,"topics":["amazon","authentication","azure","azuread","cognito","fusionauth","github","jwt","keycloak","microsoft","oauth","oauth2","openid-connect","react","wso2"],"latest_commit_sha":null,"homepage":"","language":"TypeScript","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/soofstad.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":".github/CODEOWNERS","security":"SECURITY.md","support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null}},"created_at":"2021-09-03T11:51:24.000Z","updated_at":"2025-05-05T00:26:54.000Z","dependencies_parsed_at":"2022-07-14T04:50:32.070Z","dependency_job_id":"4868bdee-0854-4a9b-b20c-7a5d93f7660d","html_url":"https://github.com/soofstad/react-oauth2-pkce","commit_stats":{"total_commits":194,"total_committers":17,"mean_commits":"11.411764705882353","dds":"0.22164948453608246","last_synced_commit":"1a5b45e531df9ed2bd800f4a6e9fc7abdbfd241e"},"previous_names":[],"tags_count":74,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soofstad%2Freact-oauth2-pkce","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soofstad%2Freact-oauth2-pkce/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soofstad%2Freact-oauth2-pkce/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/soofstad%2Freact-oauth2-pkce/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/soofstad","download_url":"https://codeload.github.com/soofstad/react-oauth2-pkce/tar.gz/refs/heads/main","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":254364270,"owners_count":22058878,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["amazon","authentication","azure","azuread","cognito","fusionauth","github","jwt","keycloak","microsoft","oauth","oauth2","openid-connect","react","wso2"],"created_at":"2024-11-12T12:33:33.274Z","updated_at":"2025-05-15T15:04:54.055Z","avatar_url":"https://github.com/soofstad.png","language":"TypeScript","funding_links":[],"categories":[],"sub_categories":[],"readme":"# react-oauth2-code-pkce\n[![GitHub license](https://img.shields.io/badge/license-MIT-blue.svg)](https://github.com/soofstad/react-oauth2-pkce/blob/main/LICENSE) ![NPM Version](https://img.shields.io/npm/v/react-oauth2-code-pkce?logo=npm\u0026label=version) ![NPM Downloads](https://img.shields.io/npm/d18m/react-oauth2-code-pkce?logo=npm) ![npm bundle size](https://img.shields.io/bundlephobia/minzip/react-oauth2-code-pkce?label=size) ![CI](https://github.com/soofstad/react-oauth2-pkce/actions/workflows/tests.yaml/badge.svg)\n\nReact package for OAuth2 Authorization Code flow with PKCE\n\nAdhering to the RFCs recommendations, cryptographically sound, and with __zero__ dependencies!  \n\n## What is OAuth2 Authorization Code Flow with Proof Key for Code Exchange?\n\nShort version;  \nThe modern and secure way to do authentication for mobile and web applications!\n\nLong version;  \n\u003chttps://www.rfc-editor.org/rfc/rfc6749.html\u003e  \n\u003chttps://datatracker.ietf.org/doc/html/rfc7636\u003e  \n\u003chttps://oauth.net/2/pkce/\u003e\n\n## Features\n\n- Authorization provider-agnostic. Works equally well with all OAuth2 authentication servers following the OAuth2 spec\n- Supports OpenID Connect (idTokens)\n- Pre- and Post-login callbacks\n- Session expired callback\n- Silently refreshes short-lived access tokens in the background\n- Decodes JWT's\n- A total of ~440 lines of code, easy for anyone to audit and understand\n\n## Example\n\n```tsx\nimport { AuthContext, AuthProvider, TAuthConfig, TRefreshTokenExpiredEvent } from \"react-oauth2-code-pkce\"\n\nconst authConfig: TAuthConfig = {\n  clientId: 'myClientID',\n  authorizationEndpoint: 'https://myAuthProvider.com/auth',\n  tokenEndpoint: 'https://myAuthProvider.com/token',\n  redirectUri: 'http://localhost:3000/',\n  scope: 'someScope openid',\n  onRefreshTokenExpire: (event: TRefreshTokenExpiredEvent) =\u003e event.logIn(undefined, undefined, \"popup\"),\n}\n\nconst UserInfo = (): JSX.Element =\u003e {\n    const {token, tokenData} = useContext\u003cIAuthContext\u003e(AuthContext)\n\n    return \u003c\u003e\n        \u003ch4\u003eAccess Token\u003c/h4\u003e\n        \u003cpre\u003e{token}\u003c/pre\u003e\n        \u003ch4\u003eUser Information from JWT\u003c/h4\u003e\n        \u003cpre\u003e{JSON.stringify(tokenData, null, 2)}\u003c/pre\u003e\n    \u003c/\u003e\n}\n\nReactDOM.render(\u003cAuthProvider authConfig={authConfig}\u003e\n        \u003cUserInfo/\u003e\n    \u003c/AuthProvider\u003e\n    , document.getElementById('root'),\n)\n```\n\nFor more advanced examples, see `./examples/`.  \n\n## Install\n\nThe package is available on npmjs.com here; https://www.npmjs.com/package/react-oauth2-code-pkce\n\n```bash\nnpm install react-oauth2-code-pkce\n```\n\n## API\n\n### IAuthContext values\n\nThe object that's returned by `useContext(AuthContext)` provides these values;\n\n```typescript\ninterface IAuthContext {\n  // The access token. This is what you will use for authentication against protected Web API's\n  token: string\n  // An object with all the properties encoded in the token (username, email, etc.), if the token is a JWT \n  tokenData?: TTokenData\n  // Function to trigger login. \n  // If you want to use 'state', you might want to set 'clearURL' configuration parameter to 'false'.\n  // Note that most browsers block popups by default. The library will print a warning and fallback to redirect if the popup is blocked\n  logIn: (state?: string, additionalParameters?: { [key: string]: string | boolean | number }, method: TLoginMethod = 'redirect') =\u003e void\n  // Function to trigger logout from authentication provider. You may provide optional 'state', and 'logout_hint' values.\n  // See https://openid.net/specs/openid-connect-rpinitiated-1_0.html#RPLogout for details.\n  logOut: (state?: string, logoutHint?: string, additionalParameters?: { [key: string]: string | boolean | number }) =\u003e void\n  // Keeps any errors that occured during login, token fetching/refreshing, decoding, etc.. \n  error: string | null\n  // The idToken, if it was returned along with the access token\n  idToken?: string\n  // An object with all the properties encoded in the ID-token (username, groups, etc.)\n  idTokenData?: TTokenData\n  // If the \u003cAuthProvider\u003e is done fetching tokens or not. Usefull for controlling page rendering\n  loginInProgress: boolean\n}\n```\n\n### Configuration parameters\n\n__react-oauth2-code-pkce__'s goal is to \"just work\" with any authentication provider that either\nsupports the [OAuth2](https://datatracker.ietf.org/doc/html/rfc7636) or [OpenID Connect](https://openid.net/developers/specs/) (OIDC) standards.  \nHowever, many authentication providers are not following these standards, or have extended them. \nWith this in mind, if you are experiencing any problems, a good place to start is to see if the provider expects some custom parameters.\nIf they do, these can be injected into the different calls with these configuration options;\n\n- `extraAuthParameters`\n- `extraTokenParameters`\n- `extraLogoutParameters`\n\nThe `\u003cAuthProvider\u003e` takes a `config` object that supports these parameters;\n\n```typescript\ntype TAuthConfig = {\n  // ID of your app at the authentication provider\n  clientId: string  // Required\n  // URL for the authentication endpoint at the authentication provider\n  authorizationEndpoint: string  // Required\n  // URL for the token endpoint at the authentication provider\n  tokenEndpoint: string  // Required\n  // Which URL the auth provider should redirect the user to after successful authentication/login\n  redirectUri: string  // Required\n  // Which scopes to request for the auth token\n  scope?: string  // default: ''\n  // Optional state value. Will often make more sense to provide the state in a call to the 'logIn()' function\n  state?: string // default: null\n  // Which URL to call for logging out of the auth provider\n  logoutEndpoint?: string  // default: null\n  // Which URL the auth provider should redirect the user to after logout\n  logoutRedirect?: string  // default: null\n  // Optionally provide a callback function to run _before_ the\n  // user is redirected to the auth server for login\n  preLogin?: () =\u003e void  // default: () =\u003e null\n  // Optionally provide a callback function to run _after_ the\n  // user has been redirected back from the auth server\n  postLogin?: () =\u003e void  // default: () =\u003e null\n  // Which method to use for login. Can be 'redirect', 'replace', or 'popup'\n  // Note that most browsers block popups by default. The library will print a warning and fallback to redirect if the popup is blocked\n  loginMethod: 'redirect' | 'replace' | 'popup'  // default: 'redirect'\n  // Optional callback function for the 'refreshTokenExpired' event.\n  // You likely want to display a message saying the user need to log in again. A page refresh is enough.\n  onRefreshTokenExpire?: (event: TRefreshTokenExpiredEvent) =\u003e void  // default: undefined\n  // 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))\n  // If `false`, 'tokenData' will be 'undefined' from the \u003cAuthContext\u003e\n  decodeToken?: boolean  // default: true\n  // By default, the package will automatically redirect the user to the login server if not already logged in.\n  // If set to false, you need to call the \"logIn()\" function to log in (e.g. with a \"Log in\" button)\n  autoLogin?: boolean  // default: true\n  // Store login state in 'localStorage' or 'sessionStorage'\n  // If set to 'session', no login state is persisted by 'react-oauth2-code-pkce` when the browser closes.\n  // NOTE: Many authentication servers will keep the client logged in by cookies. You should therefore use \n  // the logOut() function to properly log out the client. Or configure your server not to issue cookies.\n  storage?: 'local' | 'session'  // default: 'local'\n  // Sets the prefix used when storing login state\n  storageKeyPrefix?: string // default: 'ROCP_'\n  // Set to false if you need to access the urlParameters sent back from the login server.\n  clearURL?: boolean  // default: true\n  // Can be used to provide any non-standard parameters to the authentication request\n  extraAuthParameters?: { [key: string]: string | boolean | number }  // default: null\n  // Can be used to provide any non-standard parameters to the token request\n  extraTokenParameters?: { [key: string]: string | boolean | number } // default: null\n  // Can be used to provide any non-standard parameters to the logout request\n  extraLogoutParameters?: { [key: string]: string | boolean | number } // default: null\n  // Superseded by 'extraTokenParameters' options. Will be deprecated in 2.0\n  extraAuthParams?: { [key: string]: string | boolean | number }  // default: null\n  // Can be used if auth provider doesn't return access token expiration time in token response\n  tokenExpiresIn?: number // default: null\n  // Can be used if auth provider doesn't return refresh token expiration time in token response\n  refreshTokenExpiresIn?: number // default: null\n  // Defines the expiration strategy for the refresh token.\n  // - 'renewable': The refresh token's expiration time is renewed each time it is used, getting a new validity period.\n  // - '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.\n  refreshTokenExpiryStrategy?: 'renewable' | 'absolute' // default: renewable\n  // Whether or not to post 'scope' when refreshing the access token\n  refreshWithScope?: boolean // default: true\n  // Controls whether browser credentials (cookies, TLS client certificates, or authentication headers containing a username and password) are sent when requesting tokens.\n  // Warning: Including browser credentials deviates from the standard protocol and can introduce unforeseen security issues. Only set this to 'include' if you know what \n  // you are doing and CSRF protection is present. Setting this to 'include' is required when the token endpoint requires client certificate authentication, but likely is\n  // not needed in any other case. Use with caution.\n  tokenRequestCredentials?: 'same-origin' | 'include' | 'omit' // default: 'same-origin'\n}\n\n```\n\n## Common issues\n\n### Sessions expire too quickly\n\nA session expire happens when the `refresh_token` is no longer valid and can't be used to fetch a new valid `access_token`.\nThis is governed by the `expires_in`, and `refresh_expires_in | refresh_token_expires_in`, in the token response.\nIf the response does not contain these values, the library assumes a quite conservative value. \nYou should configure your IDP (Identity Provider) to send these, but if that is not possible, you can set them explicitly\nwith the config parameters `tokenExpiresIn` and `refreshTokenExpiresIn`.\n\n### Fails to compile with Next.js\nThe library's main componet `AuthProvider` is _client side only_. Meaning it must be rendered in a web browser, and can not be pre-rendered server-side (which is default in newer versions of NextJS and similar frameworks). \n\nThis can be solved by marking the module with `use client` and importing the component in the client only (`\"ssr\": false`).\n\n```tsx\n'use client'\nimport {useContext} from \"react\";\nimport dynamic from 'next/dynamic'\nimport {TAuthConfig,TRefreshTokenExpiredEvent, AuthContext} from 'react-oauth2-code-pkce'\n\nconst AuthProvider = dynamic(\n    ()=\u003e import(\"react-oauth2-code-pkce\")\n        .then((mod) =\u003e mod.AuthProvider),\n    {ssr: false}\n)\n\nconst authConfig: TAuthConfig = {...for you to fill inn}\n\nexport default function Authenticated() {\n    (\u003cAuthProvider authConfig={authConfig}\u003e\n        \u003cLoginInfo/\u003e\n    \u003c/AuthProvider\u003e)\n}\n```\n\n### Error `Bad authorization state...`\n\nThis is most likely to happen if the authentication at the identity provider got aborted in some way.\nYou 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.\n\nFirst of all, you should handle any errors the library throws. Usually, hinting at the user reload the page is enough.\n\nSome known causes for this is that instead of logging in at the auth provider, the user \"Registers\" or \"Reset password\" or \nsomething similar instead. Any such functions should be handled outside of this library, with separate buttons/links than the \"Log in\" button.\n\n### After redirect back from auth provider with `?code`, no token request is made\n\nIf you are using libraries that intercept any `fetch()`-requests made. For example `@tanstack/react-query`. That can cause\nissues for the _AuthProviders_ token fetching. This can be solved by _not_ wrapping the `\u003cAuthProvider\u003e` in any such library.\n\nThis could also happen if some routes in your app are not wrapped by the `\u003cAuthProvider\u003e`.\n\n### The page randomly refreshes in the middle of a session\n\nThis will happen if you haven't provided a callback-function for the `onRefreshTokenExpire` config parameter, and the refresh token expires.\nYou 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.\nEither by refreshing the page, or clicking a \"Log in\" button.\n\n## Develop\n\n1. Update the 'authConfig' object in `src/index.js` with config from your authorization server and application\n2. Install node_modules -\u003e `$ yarn install`\n3. Run -\u003e `$ yarn start`\n\n## Contribute\n\nYou are most welcome to create issues and pull requests :)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoofstad%2Freact-oauth2-pkce","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fsoofstad%2Freact-oauth2-pkce","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fsoofstad%2Freact-oauth2-pkce/lists"}