Learn how to configure FGA to use your identity provider's ID tokens.
Fine-Grained Authorization (FGA) is commonly used to enforce detailed authorization on your application’s backend. However, it can also be utilized on the frontend to perform access checks directly within your client application. FGA supports the use of ID tokens issued by identity providers, allowing you to make user-specific authorization decisions on the frontend. This not only improves the security of your application but also enables you to present a customized user interface and experience based on the access levels of different users.
To get the most out of this guide, you’ll need:
A JWKS URL is an endpoint that contains the set of public keys used to verify any JSON Web Tokens (JWTs) issued by your provider. Currently, FGA only supports JWTs that are signed using the RS256 signing algorithm.
Common identity provider JWKS URLs:
https://api.workos.com/sso/jwks/{clientId}
https://{yourDomain}/.well-known/jwks.json
https://www.googleapis.com/oauth2/v3/certs
You can set your JWKS URL in the Configuration section of the FGA Dashboard.
Next, let’s create a context for FGA that will allow us to make checks from anywhere in our application.
The FGA context will set and track the user’s session token and expose a check
method that we can access anywhere in our application where we need to make an access check before displaying a UI element or performing an action.
// FGAProvider.jsx import React, { useCallback, useState } from 'react'; export const FGAContext = React.createContext({ clientId: '', sessionToken: '', setSessionToken: () => {}, check: () => {}, }); export const FGAProvider = ({ clientId, children }) => { const [sessionToken, setSessionToken] = useState(''); const check = useCallback( async (checkOptions) => { if (!sessionToken) { throw new Error( 'No session token provided to FGAProvider. You may have forgotten to call setSessionToken with a valid session token.', ); } const checkResponse = await fetch('https://api.workos.com/v1/check', { method: 'POST', headers: { 'Authorization': `Bearer ${sessionToken}`, 'Warrant-Client-Key': `${clientId}`, }, body: JSON.stringify(checks), }); if (!checkResponse.ok) { throw new Error(await checkResponse.json()); } const checkResponseJson = checkResponse.json(); return checkResponseJson.result === 'authorized'; }, [sessionToken], ); return ( <FGAContext.Provider value={{ sessionToken, setSessionToken, check, }} > {children} </FGAContext.Provider> ); };
// App.jsx import React from 'react'; import { FGAProvider } from './providers/FGAProvider'; const App = () => { return ( <FGAProvider clientId={process.env.workosClientId}> {/* Routes, ThemeProviders, etc. */} </FGAProvider> ); }; export default App;
Before we begin making access checks in our application, we need to provide a server-generated session token and set it in our FGA context.
// Login.jsx import React, { useContext } from 'react'; import { FGAContext } from './providers/FGAProvider'; const Login = () => { const { setSessionToken } = useContext(FGAContext); const loginUser = async (event) => { const response = await login(email, password); // NOTE: This session token must be generated // server-side when logging users into your // application and then passed to the client. // Access check calls in this library will fail // if the session token is invalid or not set. setSessionToken(response.sessionToken); // // Redirect user to logged in page // }; return ( <form onSubmit={loginUser}>{/* email & password inputs, etc. */}</form> ); }; export default Login;
Now that we’ve created our FGA context and set the session token, we can start making check requests from our client application.
The main difference here from regular check requests is that we don’t need to provide a subject in our checks because all checks will be scoped to the user specified by the user ID in the session token.
Let’s make a check to see if the user has the viewer
relation on report:7
before displaying the report’s data.
import React, { useEffect } from 'react'; import { FGAContext } from './providers/FGAProvider'; const MyComponent = () => { const { check } = useContext(FGAContext); useEffect(() => { const fetchProtectedInfo = async () => { // Only fetch protected info from server if // user is a "viewer" of "report:7". const userIsAuthorized = await check({ checks: [ { resource: { resourceType: 'report', resourceId: '7', }, relation: 'viewer', }, ], }); if (userIsAuthorized) { // request protected info from server } }; fetchProtectedInfo(); }); return ( <div>{protectedInfo && <ProtectedInfo>{protectedInfo}</ProtectedInfo>}</div> ); }; export default MyComponent;
In this guide, we demonstrated how to perform authorization checks directly in a client application using ID tokens from our identity provider. We created a context to manage the user’s session token upon login, which is then used for subsequent access checks. This approach allows us to deliver a secure and personalized experience to users within our application, leveraging FGA for fine-grained access control.