React SDK
The Kinde React SDK allows developers to quickly and securely integrate a new or an existing React application to the Kinde platform.
You can also view the React package and React starter kit in GitHub.
This new SDK (v5) is optmized to work with React version 18+.
If you are currently using v4, refer to this migration information to update to v5.
Register for Kinde
Link to this sectionIf you haven’t already got a Kinde account, register for free here (no credit card required). Registering gives you a Kinde domain, which you need to get started, e.g. yourapp.kinde.com
.
Configure React
Link to this sectionInstallation
Link to this sectionnpm i @kinde-oss/kinde-auth-react
yarn add @kinde-oss/kinde-auth-react
pnpm add @kinde-oss/kinde-auth-react
Integrate with your app
Link to this sectionKinde uses a React Context Provider to maintain its internal state in your application.
Import the Kinde Provider component and wrap your application in it.
import { KindeProvider } from "@kinde-oss/kinde-auth-react";const App = () => ( <KindeProvider clientId="<your_kinde_client_id>" domain="<your_kinde_domain>" logoutUri={window.location.origin} redirectUri={window.location.origin} > <Routes /> </KindeProvider>);
Set callback and logout URLs
Link to this sectionSet the URLs in Kinde so that after your user signs up, signs in, or signs out, they will be redirected back to your application.
- In Kinde, go to Settings > Applications > [your app] > View details.
- Replace the
your_kinde_client_id
andyour_kinde_domain
placeholders in the code block above with the the values from the App keys section. - Add your callback URLs in the relevant fields. For example:
- Allowed callback URLs (also known as redirect URIs) - for example
https://localhost:3000/home/callback
- Allowed logout redirect URLs - for example
https://localhost:3000
- Allowed callback URLs (also known as redirect URIs) - for example
- Select Save.
Tip: Make sure there are no hidden spaces and remove any ‘/’ forward slashes from the end of URLs.
Environments
Link to this sectionIf you would like to use different Environments as part of your development process, you will need to add them within your Kinde business first. You will also need to add the Environment subdomain to the code block above.
Sign in and sign up
Link to this sectionKinde provides a useKindeAuth
hook with the methods login
and register
method to start the flow and also LoginLink
and RegisterLink
components
Use the button examples below to redirect your users to Kinde, where they authenticate before being redirected back to your site.
import { LoginLink, RegisterLink } from '@kinde-oss/kinde-auth-react/components';
<LoginLink>Sign in</LoginLink><RegisterLink>Sign up</RegisterLink>
import { useKindeAuth } from '@kinde-oss/kinde-auth-react';
const { login, register } = useKindeAuth();
<button onClick={() => register(/* params here */)} type="button">Sign up</button><button onClick={() => login(/* params here */)} type="button">Sign In</button>
Callback Events
Link to this sectionTo handle the result of auth there are three callback events.
onSuccess
- On Successful authentication, this includes the user authenticated along with the passed state and context to the Kinde hookonError
- When an error occors during authentication, this includes the error along with the passed state and context to the Kinde hook
<KindeProvider callbacks={ { onSuccess: (user, state, context) => console.log("onSuccess", user, state, context), onError: (error, state, context) => console.log("onError", error, state, context), } }> <Routes /></KindeProvider>
Passing additional params to the auth url
Link to this sectionBoth the login
and register
methods accept all the extra authentication params that can be passed to Kinde as part of the auth flow. All the params are also accepted as attributes to the LoginLink
and RegisterLink
components.
Some things you may wish to pass are:
loginHint
this allows you to ask Kinde to prepopulate a users email address on the sign-up and sign-in screens.lang
if you offer multi-language support Kinde will automatically figure out the best language for your user based on their browser. However, if you want to force a language and override the users preference, you can do so by passing this attribute.
login({ loginHint: "jenny@example.com", lang: "ru" })
Signing out
Link to this sectionKinde provides a useKindeAuth
hook with the method logout
and component LogoutLink
which can be used to end the current session.
const { logout } = useKindeAuth();
<button onClick={() => logout(/* params here */)} type="button">Sign out</button>
import { LogoutLink } from '@kinde-oss/kinde-auth-react/components';
<LogoutLink>Sign out</LogoutLink>
Test sign up
Link to this sectionRegister your first user by signing up yourself. You’ll see your newly registered user on the Users page in Kinde.
View user profile
Link to this sectionYou can get an authorized user’s profile from any component using the Kinde React hook.
import { useKindeAuth } from "@kinde-oss/kinde-auth-react";const SayHello = () => { const { user } = useKindeAuth(); return <p>Hi {user.first_name}!</p>;};
To be on the safe side we have also provided isAuthenticated
and isLoading
state to prevent rendering errors.
import { useKindeAuth } from "@kinde-oss/kinde-auth-react";
const UserProfile = () => { const { user, isAuthenticated, isLoading } = useKindeAuth();
if (isLoading) { return <p>Loading</p>; }
return ({ isAuthenticated ? <div> <h2>{user.firstName}</h2> <p>{user.preferredEmail}</p> </div> : <p>Please sign in or register!</p> });};
Call your API
Link to this sectionThe getToken
method lets you to securely call your API and pass the bearer token to validate that your user is authenticated.
const { getAccessToken } = useKindeAuth();
const fetchData = async () => { try { const accessToken = await getAccessToken(); const res = await fetch(`<your-api>`, { headers: { Authorization: `Bearer ${accessToken}` } }); const {data} = await res.json(); console.log({data}); } catch (err) { console.log(err); }};
We have a range of backend SDKs available to assist you in securing your back end application using this token, alternatively you can use any JWT validator and decoder to assit you here.
Audience
Link to this sectionAn audience
is the intended recipient of an access token - for example the API for your application. The audience
argument can be passed to the Kinde client to request an audience be added to the provided token.
The audience of a token is the intended recipient of the token.
<KindeProvider audience="<your_api>">
To request multiple audiences, pass them separated by white space.
<KindeProvider audience="<your_api1> <your_api2>">
For details on how to connect, see Register an API.
Organizations
Link to this sectionCreate an organization
Link to this sectionTo create a new organization with the user registration, you can use the createOrg
function to start the registration process:
<LoginLink isCreateOrg={true}>Sign in</LoginLink><button onClick={() => login({isCreateOrg: true})} type="button">Sign in</button>
Sign up/sign in users to organizations
Link to this sectionTo sign up a user to a particular organization, you must pass the orgCode
from your Kinde account as the user is created. You can find the orgCode
on the Details page of each organization in Kinde.
<LoginLink orgCode="org_1234">Sign in</LoginLink><button onClick={() => login({orgCode: "org_1234"})} type="button">Sign in</button>
Following authentication Kinde provides a json web token (jwt) to your application. Along with the standard information we also include the org_code
and the permissions
for that organization (this is important as a user can belong to multiple organizations and have different permissions for each). Example of a returned token:
{ "aud": ["https://your_subdomain.kinde.com"], "exp": 1658475930, "iat": 1658472329, "iss": "https://your_subdomain.kinde.com", "jti": "123457890", "org_code": "org_1234", "permissions": ["read:todos", "create:todos"], "scp": ["openid", "offline"], "sub": "kp:123457890"}
For more information about how organizations work in Kinde, see Kinde organizations for developers.
User Permissions
Link to this sectionOnce a user has been verified as signed in, your project/application will be returned in the JWT token with an array of permissions for that user. You need to configure your project to read permissions and unlock the respective functions.
Configure permissions in Kinde first. Here is an example set of permissions.
"permissions":[ "create:todos", "update:todos", "read:todos", "delete:todos", "create:tasks", "update:tasks", "read:tasks", "delete:tasks",]
We provide helper functions to more easily access permissions:
const {getPermission, getPermissions} = useKindeAuth();
await getPermission("create:todos");// {permissionKey: "create:todos", orgCode: "org_1234", isGranted: true}
await getPermissions();// {orgCode: "org_1234", permissions: ["create:todos", "update:todos", "read:todos"]}
A practical example in code might look something like:
{ (await getPermission("create:todos")).isGranted ? <button>Create todo</button> : null;}
Feature flags
Link to this sectionWhen a user signs in, the access token your project/application receives contains a custom claim called feature_flags
which is an object detailing the feature flags for that user.
You can set feature flags in your Kinde account. Here’s an example.
feature_flags: { theme: { "t": "s", "v": "pink" }, is_dark_mode: { "t": "b", "v": true }, competitions_limit: { "t": "i", "v": 5 }}
In order to minimize the payload in the token we have used single letter keys / values where possible. The single letters represent the following:
t
= type
v
= value
s
= string
b
= boolean
i
= integer
We provide helper functions to more easily access feature flags:
const { getFlag } = useKindeAuth();
/* Example usage */await getFlag('theme'); // string valueawait getFlag<boolean>('theme'); // boolean valueawait getFlag<number>('theme'); // numeric value
/*{// "code": "theme",// "type": "string",// "value": "pink",// "is_default": false // whether the fallback value had to be used*/}
getFlag('create_competition', {defaultValue: false});/*{ "code": "create_competition", "value": false, "is_default": true // because fallback value had to be used}*/
A practical example in code might look something like:
const { getFlag } = useKindeAuth();
{ (await getFlag<boolean>('create_competition')) ? <button>Create competition</button> : null;}
A practical example in code might look something like:
const {getFlag} = useKindeAuth();
{ getFlag<boolean>("create_competition") ? ( <button className={`theme-${getFlag("theme")}`}>Create competition</button> ) : null;}
Overriding scope
Link to this sectionBy default the JavaScript SDK requests the following scopes:
profile
email
offline
openid
You can override this by passing scope
into the <KindeProvider>
.
<KindeProvider scope="openid">
Getting claims
Link to this sectionWe have provided a helper to grab any claim from your ID or access tokens. The helper defaults to access tokens:
const { getClaim } = useKindeAuth();
getClaim("aud");// {name: "aud", "value": ["api.yourapp.com"]}
getClaim("given_name", "idToken");// {name: "given_name", "value": "David"}
Persisting authentication state on page refresh or new tab
Link to this sectionYou will find that when you refresh the browser using a front-end based SDK that the authentication state is lost. This is because there is no secure way to persist this in the front-end.
There are two ways to work around this.
- (Recommended) use our Custom Domains feature which then allows us to set a secure, httpOnly first party cookie on your domain.
- (Non-production solution only) If you’re not yet ready to add your custom domain, or for local development, we offer an escape hatch
<KindeProvider>
useInsecureForRefreshToken
. This will use local storage to store the refresh token. DO NOT use this in production.
Once you implement one of the above, you don’t need to do anything else.
Persisting application state
Link to this sectionThe options argument passed into the login
and register
methods accepts an state
key where you can pass in the current application state prior to redirecting to Kinde. This is then returned to you in the second argument of the onSuccess
callback as seen above.
A common use case is to allow redirects to the page the user was trying to access prior to authentication. This could be achieved as follows:
Login handler:
<button onClick={() => login({ state: { redirectTo: location.state ? location.state?.from?.pathname : null } }) }/>
Redirect handler:
<KindeProvider callbacks={ { onSuccess: (user, state, context) => { window.location = state?.redirectTo }, } }>
Token storage in the authentication state
Link to this sectionBy default the JWTs provided by Kinde are stored in memory. This protects you from both CSRF attacks (possible if stored as a client side cookie) and XSS attacks (possible if persisted in local storage).
The trade off with this approach however is that if a page is refreshed or a new tab is opened then the token is wiped from memory, and the sign in button would need to be clicked to re-authenticate. There are two ways to prevent this behaviour:
- Use the Kinde custom domain feature. We can then set a secure, httpOnly cookie against your domain containing only the refresh token which is not vulnerable to CSRF attacks.
- There is an escape hatch which can be used for local development:
useInsecureForRefreshToken
. This SHOULD NOT be used in production. We recommend you use a custom domain. This will store only the refresh token in local storage and is used to silently re-authenticate.
<KindeProvider useInsecureForRefreshToken={process.env.NODE_ENV === 'development'} ...>
Migration from v4 to v5
Link to this sectionlogin and register method params
Link to this sectionNo longer need to use authUrlParams parameter, all url params are now passed at top level and are now camel case instead of snake case.
e.g.
login({ authUrlParams: { login_hint: "jenny@example.com", lang: "ru" }})
becomes
login({ loginHint: "jenny@example.com", lang: "ru"})
Callbacks
Link to this sectiononRedirectCallback
has been removed in favor of a richer event system.
This has been replaced by callbacks > onSuccess
Create organization
Link to this sectioncreateOrg
method has been removed and replaced by using isCreateOrg
on the login or register methods or Components
Accessing access token
Link to this sectiongetToken
has been replaced by await getAccessToken
Token helpers
Link to this sectionall token helpers are now async methods
getClaims
Link to this sectiontoken attribute changed to camelCase.
e.g.
getClaim("given_name", "id_token");
becomes
await getClaim("given_name", "idtoken");
API References - KindeProvider
Link to this sectionaudience
Link to this sectionThe audience claim for the JWT.
Type: string
Required: No
clientId
Link to this sectionThe ID of your application as it appears in Kinde.
Type: string
Required: Yes
domain
Link to this sectionEither your Kinde instance url or your custom domain. e.g https://yourapp.kinde.com
Type: string
Required: Yes
logoutUri
Link to this sectionWhere your user will be redirected when they log out.
Type: string
Required: No
useInsecureForRefreshToken
Link to this sectionAn escape hatch for storing the refresh in local storage for local development.
Type: boolean
Required: No
Default: false
redirectUri
Link to this sectionThe URL that the user will be returned to after authentication.
Type: string
Required: Yes
scope
Link to this sectionThe scopes to be requested from Kinde.
Type: string
Required: No
Default: openid profile email offline
API References- useKindeAuth hook
Link to this sectioncreateOrg
Link to this sectionConstructs redirect url and sends user to Kinde to sign up and create a new org for your business.
Usage:
Arguments:
org_name?: string;app_state?: object;authUrlParams?: object;
createOrg();
Sample:
redirect;
getClaim
Link to this sectionGets a claim from an access or ID token.
Arguments:
claim: string, tokenKey?: string
Usage:
getClaim("given_name", "id_token");
Sample:
"David";
getOrganization
Link to this sectionGet details for the organization your user is signed into.
Usage:
getOrganization();
Sample:
{ orgCode: "org_1234";}
getPermission
Link to this sectionReturns the state of a given permission.
Arguments:
key: string;
Usage:
getPermission("read:todos");
Sample:
{ orgCode: "org_1234", isGranted: true}
getPermissions
Link to this sectionReturns all permissions for the current user for the organization they are signed into.
Usage:
getPermissions();
Sample:
{ orgCode: "org_1234", permissions: [ "create:todos", "update:todos", "read:todos" ]}
getToken
Link to this sectionReturns the raw Access token from memory.
Usage:
getToken();
Sample:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c;
getUser
Link to this sectionReturns the profile for the current user.
Usage:
getUser();
Sample:
{ given_name: "Dave"; id: "abcdef"; family_name: "Smith"; email: "dave@smith.com";}
getUserOrganizations
Link to this sectionGets an array of all organizations the user has access to.
Usage:
getUserOrganizations();
Sample:
{ orgCodes: ["org_1234", "org_5678"];}
login
Link to this sectionConstructs redirect url and sends user to Kinde to sign in.
Arguments:
org_code?: string;app_state?: object;authUrlParams?: object;
Usage:
login();
Sample:
redirect;
logout
Link to this sectionLogs the user out of Kinde.
Argument:
org_code?: string
Usage:
logout();
Sample:
redirect;
register
Link to this sectionConstructs redirect url and sends user to Kinde to sign up.
Arguments:
org_code?: string;app_state?: object;authUrlParams?: object;
Usage:
register();
Sample:
redirect;
API References- login
Link to this sectiongetClaim
Link to this sectionGets a claim from an access or ID token.
Arguments:
claim: string, tokenKey?: string
Usage:
getClaim("given_name", "id_token");
Sample:
"David";
If you need any assistance with getting Kinde connected reach out to us at support@kinde.com.