fusionauth / fusionauth-react-sdk Goto Github PK
View Code? Open in Web Editor NEWAn SDK for using FusionAuth with React
Home Page: https://fusionauth.io/docs/quickstarts/#single-page-app
License: Apache License 2.0
An SDK for using FusionAuth with React
Home Page: https://fusionauth.io/docs/quickstarts/#single-page-app
License: Apache License 2.0
This repo is not complete without the server side node code. Should example code be contained in this repo?
The main provider component -- FusionAuthProvider
-- doesn't check if the response from /me
is good. If someone had a server implementation that returned an object representing an error, the SDK would set it as the user
.
Probably just need to check for an ok http status from call to /me
, and take the correct action from there.
All versions.
Using the example-react-sdk:
/me
route handler and make it do something like:res.status(500).send({
error: 'something went wrong'
})
Then save and boot up the server and client.
2. Go to the browser and log in. If the browser doesn't make the network call to (/me
), then clear the user
cookie with dev tools and refresh.
3. Observe that the SDK is showing you the /account
page, but there is no user info to display because your user is set to { error: 'something went wrong' })
The SDK should know that it is in a non-authenticated state and set the user to {}
Any web browser.
All issues filed in this repository must abide by the FusionAuth community guidelines.
Option to customize the cookie name used to check isAuthenticated
.
We're using a different backend implementation, our cookie content and name is different, so isAuthenticated
will never work for us.
Have an option to change the cookie name from app.at_exp
to something else, so the SDK could still be used as is.
Don't use the SDK and implement our own SDK which is also OK.
package.json
should specify a node
and npm
version
This is important for compatibility because mismatched versions can create unexpected issues.
We are converting the SDK polyrepo setup into a monorepo
Once we are ready to start publishing @fusionauth/react-sdk
from our monorepo, this repo should be archived.
done: Let's add an archival note to the README here. Here's an example of an archived project: https://github.com/FusionAuth/fusionauth-example-react-native-0-71 (don't use the exact links, but you can grab the wording).
Using the React SDK requires hosting server endpoints. The only compatible solution right now is in https://github.com/FusionAuth/fusionauth-example-react-sdk
When FusionAuth adds functionality to host these endpoints, the react sdk should work with this out of the box.
Use the example-react-sdk / server
FusionAuth/fusionauth-issues#1943
All issues filed in this repository must abide by the FusionAuth community guidelines.
Please give us a thumbs up or thumbs down as a reaction to help us prioritize this feature. Feel free to comment if you have a particular need or comment on how this feature should work.
NOTE: this issue tracks the discussion around the decision to even do this. This is not the implementation issue.
The FusionAuth React SDK has a FusionAuthProvider
component that is responsible for much of the functionality. It's basically the valuable intellectual property of the SDK.
The FusionAuthProvider
source code is a bit unwieldy and difficult to digest for developers. This is turn can make it difficult to debug or just generally understand what "state" the running code is in. Extending or adding new functionality can also be a precarious operation.
Should we refactor the FusionAuthProvider
to improve the readability and maintainability of the code?
Yes. And let's use XState to model the business logic and state of the FusionAuthProvider
.
XState is a state management and orchestration solution for JavaScript and TypeScript apps. It lets us create state machines which offer predictability via guarantees about state and functionality.
Here's an example we can look at.
The example demonstrates a few nifty features:
In in the world of state machines, the term "state" refers to a "state of being" (as opposed to "data" – this is a common conflation of terms). State machines can only be in one state at a time. In the case of lampMachine
, the defined states are TurnedOn
and TurnedOff
. So the lampMachine
can either be TurnedOn
or TurnedOff
. There's no way to be in both or neither. This is great because it exactly models the binary state lamps actually have.
There are two super important states to consider within the FusionAuth ReactSDK: 1) "user is authenticated" and 2) "user is not authenticated". If a user is authenticated they cannot also be unauthenticated (and vice versa). Boolean values can represent this simple idea easy enough. But things get hazy once we introduce related states.
Take this code from the React SDK for example:
const [isLoading, setIsLoading] = useState(false);
const isAuthenticated = useMemo(() => Object.keys(user).length > 0, [user]);
So it turns out there's some more "states" we can be in. How do they interact? What's the relationship? Do they affect one another? Does isLoading
have something to do with isAuthenticated
?
Further, it's possible for isLoading
and isAuthenticated
to both be true
. Is that ok? It's really hard to tell, we need a lot more context which requires digging into the code.
This demonstrates one of the big benefits of state machines: I don't need any more context than reading the defined states to understand what's even possible. Intention is clearly encoded when using state machines.
State machines have the ability to scope events to particular states. In the lampMachine
, if it's in the TurnedOn
state, sending an ON
event won't do anything because we didn't define that event for that state. This matches our mental model of how lamps work: if a lamp is turned on, I can't turn it on again. I could try but I couldn't actually do anything since there is nothing to do.
When a user is a particular state like Authenticated
, we can make sure events that don't contextually mean anything (like AUTHENTICATE
) actually do anything at all. We'll be guaranteed to not try and authenticate again.
While there is a small integration API to deal with when consuming state machines, the actual business logic is framework agnostic so can be used in arbitrary JavaScript projects.
This means we can build out this robust model of business logic and immediately bring it over to the other SDKs and it'll work exactly the same. This helps maintainability and coherence among the SDKs – a fix for one fixes the others, extending functionality for one, extends that functionality for the others.
The current business logic isn't very explicit so we will need to be extra careful about interpreting the business logic, extracting it, and translating it into a state machine. We already have built up a good mental model about what's going on but refactors like this have the potential to uncover ambiguity in the current implementation. We'll need to eliminate these ambiguities by making decisions/uncovering what the actual product requirments are. This could involve some back-and-forth or even a meeting.
Estimate: 1 developer 2-3 days
Feedback from a community user:
I won’t be able to use it yet, because my auth flow is traditional i.e. Saving the tokens to localstorage
The source code published to NPM is compressed and no source maps are shipped. This makes it really difficult to debug.
FWIW; A lot of libraries these days don't even do compression since it will be handled by the users setup ( webpack ) already.
The warning is being triggered by some tests calling testing-library's render
method with await
as well as some instances from within act
. This is an indication that react state of components being tested may be out of sync with assertions made in tests, which could cause unexpected behavior.
Relevant documentation
All issues filed in this repository must abide by the FusionAuth community guidelines.
We've noticed that some repos contain both yarn.lock
and package-lock.json
files -- a result of using both yarn
and npm
package managers. This item is to make sure repositories have just one lock file. Document this information in any relevant README
files, so that future contributors will know which package manager to use.
/me fetch failure causing infinite loop
After login when no user
cookie is present then the user info endpoint is being invoked via a fetch request and if that fetch fails for a reason (CORS, or anything else), and a re-render is triggered, then it will cause an infinite loop (can be tested with the example app, comment out the alert dialog to see it).
Latest
Steps to reproduce the behavior:
Have some backoff strategy to stop making the fetch calls and getting information about userinfo failure specifically (because it is a backchannel invocation failure, not a redirect failure) or the ability to pass in a function which can do the userinfo invocation, so the consumers of the SDK has a better control over what to do.
N/A
It looks like a normal button, rather than being styled like the other buttons.
See https://github.com/FusionAuth/fusionauth-react-sdk/blob/main/src/styles/button.module.scss#L20
Would be better if it were styled like logout and registration buttons.
All issues filed in this repository must abide by the FusionAuth community guidelines.
Add any other context about the problem here.
From a feedback request:
You will want to include “react” and “react-dom” as peerDependencies, if that is the package that is being released.
Include these in the package.json so that if you try to install the react sdk without react, npm complains.
All issues filed in this repository must abide by the FusionAuth community guidelines.
We may want to display user information in the react app. The proper way to do that is to use an id_token.
We should send the id token down to the react app as a non http only cookie.
The user object may have that data. See #22 as well.
Add any other context or screenshots about the feature request here.
All issues filed in this repository must abide by the FusionAuth community guidelines.
Please give us a thumbs up or thumbs down as a reaction to help us prioritize this feature. Feel free to comment if you have a particular need or comment on how this feature should work.
Currently when making a POST
to the /jwt-refresh
endpoint the request gets a content-type
set to application/json
(see: https://github.com/FusionAuth/fusionauth-react-sdk/blob/main/src/providers/FusionAuthProvider.tsx#L141-L149). This seems odd since there is no BODY on the request. This can cause problems in some backends as they might choose to valid a body based on the the content-type
. For example the default JSON parser in fastify (a node backend) throws if there is no body (see: https://github.com/fastify/fastify/blob/a4b21f0a2e7e753e9a8772dbac6b255b7dcbdc6b/test/helper.js#L316-L335).
I see to possible solutions to this.
content-type
Please let me know your thoughts.
Should doc that you can customize the server side code to send down additional data, that is by pulling from the user API.
NOTE: this issue tracks the discussion around the decision to even do this. This is not the implementation issue.
The FusionAuth React SDK is a React npm package that offers both UI components with preconfigured functionality and non-UI components that offer non-visible functionality. There is currently no way to view the visible UI components.
It is difficult to work on the visual styles of the UI components because there is no way to view them.
How can we view the UI components in order to get visual feedback on style changes?
Let's use Storybook. It is the industry standard for viewing UI components in isolation and as such is well-supported with lots of documentation and resources.
Rendering components in isolation is particularly necessary for UI libraries like the FusionAuth React SDK because we don't have a host app to run them in. Components being isolated also forces developers to make sure they don't have any hidden dependencies to work correctly.
Aside from giving developers a place to develop UI components, Storybook also acts as interactive documentation for these components. Developers (and others) can interactively change props and components update to reflect their changes.
FusionAuth could choose to host the Storybook publicly so developers can view the components without even pulling down and running the project themselves. Services like Chromatic make hosting Storbook instances extremely easy and quick.
We can also "compose" Storybooks enabling us to have one place to view the components for every FusionAuth SDK. This would be a future enhancement past this one however.
Integrating Storybook is fairly simple. And because we don't have a ton of components, it shouldn't take very long to make a "Story" for each component.
Estimate: 1 developer 1 day
Need to get that documented
The SDK comes with out-of-the-box support for FusionAuth hosted endpoints, however, the README currently says “…requires you to set up a server that will be used to perform the OAuth token exchange. This server must have the following endpoints…”
We should keep the documentation up to date by mentioning (alongside the example server code) that the SDK supports the core endpoints by default, and that hosting your own server is not a requirement. Link to the quickstart example app
All issues filed in this repository must abide by the FusionAuth community guidelines.
Changing a password is part of the login flow. We should add a button/function to do this. It will redirect the user over to FusionAuth and then, on successful change, send them back to the app.
Remove the "Server code requirements" section from README. This section is duped across the SDK READMEs, so it's getting consolidated to the README of fusionauth-javascript-sdk-express
async
from non-async function definitionsThe login
, logout
, and register
methods provided by IFusionAuthContext
are incorrectly typed as asynchronous. They are defined with async
, even though they do not contain any asynchronous code. It would be helpful if tests calling these functions with await
and waitFor(() =>... assertion)
were also be updated.
All issues filed in this repository must abide by the FusionAuth community guidelines.
When using the Hosted Backend, the endpoint URL for token refresh is /app/refresh/{clientId}
but the React SDK is sending the refresh attempt to /app/refresh
.
When the provider is set with a tokenRefreshPath
which includes the clientId, the refresh works as expected.
Not working:
<FusionAuthProvider
clientID="4dff639c-d360-427a-b742-d108a2229002"
serverUrl="http://localhost:9011"
redirectUri="http://localhost:4000"
>
Working:
<FusionAuthProvider
clientID="4dff639c-d360-427a-b742-d108a2229002"
serverUrl="http://localhost:9011"
redirectUri="http://localhost:4000"
tokenRefreshPath="/app/refresh/4dff639c-d360-427a-b742-d108a2229002"
>
"@fusionauth/react-sdk": "^0.25.0"
Fusion Auth w/ Hosted Backend API @ 1.47.1
I expect the clientId to be included in the refresh URL without having to set the tokenRefreshPath
Cannot import anything, as there's no code listed under node_modeles/@fusionauth/react-sdk
List the version you are running, and any other versions you have attempted a recreated.
Package should contain code
(Please complete the following information)
All issues filed in this repository must abide by the FusionAuth community guidelines.
Workaround is to install v1.0.0
Currently, if I am making requests of an external API who is using the access token retrieved from the OAuth server as a credential, I have to catch a failure/access denied request, and then manually call the refresh token endpoint, then repeat my call.
It would be great if the SDK automatically renewed the access token without any work on my part.
One option would be to have the server side SDK code send down a cookie that expired just before the access token did. This cookie could contain no secrets, so it could be readable by JS. We could set a timer to check for this cookie regularly. When the cookie is gone/expired, the SDK js could call out to the refresh token endpoint and get a new access token.
There may be other approaches that would work.
n/a
FusionAuth/fusionauth-issues#1674 outlines this functionality as well.
All issues filed in this repository must abide by the FusionAuth community guidelines.
Please give us a thumbs up or thumbs down as a reaction to help us prioritize this feature. Feel free to comment if you have a particular need or comment on how this feature should work.
NOTE: this issue tracks the discussion around the decision to even do this. This is not the implementation issue.
The FusionAuth React SDK currently extensively uses function mocking in order to test functions that access the network.
While this ensures that a function by a particular name is called, it isn't a very robust way to check that a function does what it's supposed to do.
How can we test functions that access the network in a more robust way that gives us more confidence in the results of our tests?
Let's use Mock Service Worker (MSW). MSW is the industry standard API mocking library that allows you to write client-agnostic mocks and reuse them across any frameworks, tools, and environments. It's well supported and has plenty of resources and documention.
Basically, we can utilize it to setup Service Workers that intercept network requests and respond with what we want to test. I.e. a function that accesses GET /some-endpoint
won't need to be mocked. It will make it's request normally and the Service Workers will intercept the call and respond accordingly.
We're able to easily test multiple response types as well, so we can make sure any network failures are handled in an appropriate way as well.
This will let the code run that should run and the surface area of our tests will be increased and so will the accuracy of our tests.
MSW is also framework agnostic which means we could reuse the work we do for the other FusionAuth SDKs, increasing our confidence in those tests as well.
The FusionAuth React SDK doesn't hit very many endpoints (about 6), so even accounting for variations, we're not looking at too much setup time.
After setup, we just need to update the tests to remove the mocked calls. This also won't be very time consuming as there aren't many functions to worry about.
Estimate: 1 developer 1 day
The component tests (buttons, and HOC tests) use waitFor
to render components. This can cause unexpected behavior.
This is the same pattern as issue #62.
Relevant docs... https://testing-library.com/docs/dom-testing-library/api-async#waitfor
All issues filed in this repository must abide by the FusionAuth community guidelines.
Related: #22
The access and refresh token cookies are set by the example server side code. Since it is set by a server side component, it is not accessible to the react SDK to delete on logout.
When you click logout, you are sent to FusionAuth's logout link: https://github.com/FusionAuth/fusionauth-react-sdk/blob/main/src/providers/FusionAuthProvider.tsx#L100
This kills the FusionAuth managed cookies. We also need to remove any server side non FusionAuth set cookies, such as the access_token
.
After the FusionAuth logout URL is processed, it calls the logout url. (Set here by default; https://github.com/FusionAuth/fusionauth-example-react-sdk/blob/main/kickstart/kickstart.json#L60 ). That'd the proper place to nuke the access token and refresh token cookies.
So I think the best path is to create another server side route which receives the logout request, nukes the cookies, and then redirect to the first, unauthenticated page of the react app.
yarn.lock
and package-lock.json
The repo contains both yarn.lock
and package-lock.json
-- the result of using both yarn
and npm
package managers. Only one package manager is needed for this project.
All issues filed in this repository must abide by the FusionAuth community guidelines.
We have an AuthRequired
component, but it is common to want to display things (the login button) only to unauthenticated users.
Add an inverse component that only executes when a user is not authenticated.
use the isAuthenticated
function.
Add any other context or screenshots about the feature request here.
All issues filed in this repository must abide by the FusionAuth community guidelines.
Please give us a thumbs up or thumbs down as a reaction to help us prioritize this feature. Feel free to comment if you have a particular need or comment on how this feature should work.
There is no error message to the developer if they configure this SDK with the wrong client id, just a blank screen.
0.25.0
using fusionauth 1.45.1 hosted backend.
Steps to reproduce the behavior:
createRoot
call.I don't know, but at least a message in the JS console.
All issues filed in this repository must abide by the FusionAuth community guidelines.
You can grab this sample app if you want a super simple testbed: https://github.com/FusionAuth/fusionauth-example-react-guide
The issue I am seeing is that the user object from the sdk is just an empty object. The “isAuthenticated” property is being correctly populated though.
const { user, isAuthenticated, } = useFusionAuth();
This results in user
being set to an empty object: {}
and isAuthenticated
being true
.
I am able to get the user by just reading the cookie myself, but it seems like user
should be set.
It looks like setUser
is configured here: https://github.com/FusionAuth/fusionauth-react-sdk/blob/main/src/providers/FusionAuthProvider.tsx#L56
and it seems like useEffect
should be setting the user object here: https://github.com/FusionAuth/fusionauth-react-sdk/blob/main/src/providers/FusionAuthProvider.tsx#L127 but the useFusionAuth
method doesn't return a valid user.
There is no inline documentation for components and methods provided by the SDK
Use JSDoc to include inline docs so that developers get useful descriptions in tooltips, like the screenshot below (pulled in from our Angular SDK).
We can align requirements/documentation across the react/angular/vue SDKs using our tech spec -- cc: @david-chalk
All issues filed in this repository must abide by the FusionAuth community guidelines.
Please give us a thumbs up or thumbs down as a reaction to help us prioritize this feature. Feel free to comment if you have a particular need or comment on how this feature should work.
I am trying to recreate the example using a Vite/React app with a Serverless/Lambda REST API. I have the POST /token-exchange
endpoint doing exactly as it does in the example server, and every time I try to login without an access_token cookie, it is calling the token-exchange endpoint twice. This causes it to error out since it's a race to whoever gets to claim the code first. Sometimes one call will succeed and the other will fail, other times both will fail. But never will both succeed (as they shouldn't). Not sure how much it helps as it's a very local setup, but here are the logs from my Docker container (note the first call succeeds and gives me a token and gets the user info, but the second call fails with an auth_code_not_found
error):
fusionauth | 2022-12-16 08:43:27.606 PM WARN org.elasticsearch.client.RestClient - request [PUT http://search:9200/fusionauth_user/_doc/cf82193b-56b6-4740-a75b-0bc1d8ee38fd] returned 1 warnings: [299 Elasticsearch-7.17.0-bee86328705acaa9a6daede7140defd4d9ec56bd "Elasticsearch built-in security features are not enabled. Without authentication, your cluster could be accessible to anyone. See https://www.elastic.co/guide/en/elasticsearch/reference/7.17/security-minimal-setup.html to enable security."]
forms-services |
forms-services |
forms-services | POST /token-exchange (λ: tokenExchange)
forms-services |
forms-services |
forms-services | POST /token-exchange (λ: tokenExchange)
forms-localstack | 2022-12-16T20:43:32.845 INFO --- [ asgi_gw_0] localstack.request.aws : AWS secretsmanager.GetSecretValue => 200
forms-services | user info ClientResponse {
forms-services | statusCode: 200,
forms-services | response: {
forms-services | applicationId: <redacted>,
forms-services | email: <redacted>,
forms-services | email_verified: true,
forms-services | family_name: 'Dura',
forms-services | given_name: 'Josh',
forms-services | roles: [],
forms-services | scope: 'openid offline_access',
forms-services | sid: <redacted>,
forms-services | sub: <redacted>,
forms-services | tid: <redacted>
forms-services | }
forms-services | }
forms-services | (λ: tokenExchange) RequestId: 6f693574-2c1c-4879-b922-a1d1cf658d73 Duration: 5184.44 ms Billed Duration: 5185 ms
forms-localstack | 2022-12-16T20:43:33.032 INFO --- [ asgi_gw_2] localstack.request.aws : AWS secretsmanager.GetSecretValue => 200
forms-services | error with oauth2/token endpoint call {"statusCode":400,"exception":{"error":"invalid_request","error_description":"Invalid Authorization Code","error_reason":"auth_code_not_found"}}
forms-services | (λ: tokenExchange) RequestId: 00c5005e-59f9-4a1e-a758-7a8c6b71945b Duration: 5250.92 ms Billed Duration: 5251 ms
forms-services | error Error: [object Object]
forms-services | at /source/.build/utils/fusionauth.js:87:27
forms-services | at step (/source/.build/utils/fusionauth.js:33:23)
forms-services | at Object.throw (/source/.build/utils/fusionauth.js:14:53)
forms-services | at rejected (/source/.build/utils/fusionauth.js:6:65)
forms-services | at processTicksAndRejections (node:internal/process/task_queues:96:5)
A developer might want to make a page available to multiple roles. RequireAuth.withRole
only takes a single value.
Allow withRole to take a single value or array of values. It should be typed, anyways. So something like withRole: String | String[]
FusionAuth role configuration can be set to add hybrid roles that are the combined set of the multiple roles. But this is cumbersome and might not be possible for all users.
Requested by Sonderformat.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. 📊📈🎉
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google ❤️ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.