Before we get started, just in case you missed them, here are quick links to the announcement post and Changelog:
Although this major is smaller in scope than the last one, auth is a big part of any project. Let’s go over the changes you’ll have to make.
- Decoupled Auth
- Node.js 16 and 18
- Switch from
cross-undici-fetch
to@whatwg-node/fetch
- Extend from
RedwoodError
instead ofRedwoodGraphQLError
-
depthLimitOptions
has changed toarmorConfig
-
matchPath
is no longer exported from@redwoodjs/router
Decoupled Auth
This only affects you if you’re using Redwood’s auth.
Removed providers
We gave the good news first. Now, the bad news. We’ve removed the following providers:
- Ethereum
- GoTrue
- Magic
- Nhost
- Okta
If you want to continue using one of these auth providers, you’ll have to set it up yourself via custom auth. To show you exactly how, we’ve updated the Custom Auth doc with step-by-step instructions, using Nhost as an example. (So if you were using Nhost, your job is that much simpler.)
Even though we removed the providers, it felt like a waste to just delete the code, especially since some of you may be using one of them. So instead, we moved the code to these new repos:
- Ethereum - GitHub - redwoodjs/auth-walletconnect
- GoTrue - GitHub - redwoodjs/auth-gotrue
- Magic - GitHub - redwoodjs/auth-magiclink
- Nhost - GitHub - redwoodjs/auth-nhost
- Okta - GitHub - redwoodjs/auth-okta
We hope that referencing one of these repos help ease your migration to v4. And If you’d like to maintain one of them, reach out and we’re happy to talk.
API changes
We’re going to go step by step here; please follow along regardless of your auth provider and don’t hesitate to respond in this thread if you have any questions.
The rest of the changes are architectural. That is, we didn’t remove any functionality from the useAuth
hook or anything like that, just moved things around. The most tedious change on your end will probably be changing where the useAuth
hook gets imported from. It’s no longer from @redwoodjs/auth
, but from src/auth
. That’ll make more sense in a second, after we go through all the changes.
Let’s use auth0 as an example, but follow along even if you’re not using auth0—the steps here should apply to any auth provider.
First, make sure your git index is clean. Then, upgrade to v4.0.0:
yarn rw upgrade
To make this process less manual, we recommend re-running the original setup command you used to setup auth in the first place:
yarn rw setup auth auth0
# Say yes to overwriting the existing auth file
Ok, let’s look at the web and api changes one at a time. On the web side, you should notice a brand new file: web/src/auth.{ts,js}
. This is where all the action happens. Previously, all the action happened in web/src/App.{tsx,js}
. There, your auth provider’s client SDK was instantiated and passed to the AuthProvider
component, like so:
import { Auth0Client } from '@auth0/auth0-spa-js'
import { AuthProvider } from '@redwoodjs/auth'
// ...
const auth0 = new Auth0Client({ /* ... */ })
const App = () => (
// ...
<AuthProvider client={auth0} type="auth0">
// ...
)
Now things are different. This all happens in web/src/auth.{ts,js}
:
import { Auth0Client } from '@auth0/auth0-spa-js'
import { createAuth } from '@redwoodjs/auth-auth0-web'
const auth0 = new Auth0Client({ /* ... */ })
export const { AuthProvider, useAuth } = createAuth(auth0)
Notice that the AuthProvider
component and the useAuth
hook aren’t things you import from @redwoodjs/auth
anymore, but things returned by a function called createAuth
, imported from a new package (that the setup command installed for you), @redwoodjs/auth-auth0-web
.
This function takes an instance of your auth provider’s client SDK and maps its methods to Redwood’s unified auth interface.
Since we’ve decoupled auth from the framework, the router doesn’t automatically pick this up anymore. You have to tell it about the integration by passing it the useAuth
hook you just created:
+ import { useAuth } from 'src/auth'
const Routes = () => {
return (
+ <Router useAuth={useAuth}>
{/* ... */}
</Router>
)
}
Similarly, in web/src/App.{tsx,js}
, the RedwoodApolloProvider
needs the useAuth
hook to get a token to include in every GraphQL request:
+ import { AuthProvider, useAuth } from 'src/auth'
const App = () => (
<FatalErrorBoundary page={FatalErrorPage}>
<RedwoodProvider titleTemplate="%PageTitle | %AppTitle">
+ <AuthProvider>
- <AuthProvider client={auth0} type="auth0">
+ <RedwoodApolloProvider useAuth={useAuth}>
<Routes />
</RedwoodApolloProvider>
</AuthProvider>
</RedwoodProvider>
</FatalErrorBoundary>
)
There’s a good chance the logic for instantiating your auth provider’s client SDK is still here in web/src/App.{tsx,js}
because you needed to pass it to the old version of the AuthProvider
. We don’t need it in two places—go ahead and remove it.
That’s all for the web side. Well, almost—if you’re really using auth, you’re probably using useAuth
in some of your components. You’ll have to change the import from @redwoodjs/auth
to src/auth
, but that should be easy enough with a simple search and replace.
By now, unless you’re using custom auth, there shouldn’t be any references to @redwoodjs/auth
anymore. Go ahead and remove it from your web side’s package.json
and update your project’s yarn.lock
by yarn installing.
There’s not much to the api side. The only decoupling we had to do here was with the authDecoder
, which, in auth0’s case, decodes the JWT in the Authorization request header. Now you pass it to the GraphQL server instead of it being implied. This gives you control of how the token is decoded:
+ import { authDecoder } from '@redwoodjs/auth-auth0-api'
// ...
export const handler = createGraphQLHandler({
getCurrentUser,
+ authDecoder,
// ...
})
Hopefully that wasn’t too much. Here’s a summary of all the changes we made to your codebase for reference:
-
installed Redwood’s auth integration packages; in this case,
@redwoodjs/auth-auth0-web
and@redwoodjs/auth-auth0-api
-
made a new file,
web/src/auth.{ts,js}
; moved your auth provider’s client SDK instantiation fromweb/src/App.{tsx,js}
to this new file -
still in this new file, imported
createAuth
from@redwoodjs/auth-{yourAuthProvider}-web
. called it with your instantiated client,createAuth(client)
, and exported its returns,AuthProvider
anduseAuth
-
passed the
useAuth
hook to the router inweb/src/Routes.{tsx,js}
-
in
web/src/App.{tsx,js}
, wrapped your app with theAuthProvider
fromweb/src/auth.{ts,js}
, and passed theuseAuth
hook toRedwoodApolloProvider
-
anywhere the
useAuth
hook was used, changed its import tosrc/auth
instead of@redwoodjs/auth
-
unless you’re using custom auth, removed the
@redwoodjs/auth
package from the list of dependencies in the web side’spackage.json
-
on the api side, in
api/src/functions/graphql.{ts,js}
imported theauthDecoder
function from@redwoodjs/auth-{yourAuthProvider}-api
and passed it tocreateGraphQLHandler
Using Clerk?
If you’re using Clerk, there’s one more recommended step for you: update to their new API keys. The Clerk team recently refactored their frontend API keys. They did this refactor in a backwards-compatible way, so huge shoutout to them, but definitely upgrade sooner than later. Check out their blog post about it for more details:
Node.js 16 and 18
This change affects all users.
In your Redwood app’s package.json
, expand the range in the node engines:
"engines": {
- "node": ">=14.19 <=16.x",
+ "node": ">=16.x <=18.x",
"yarn": ">=1.15"
},
Lastly, change the node version in your deploy provider to 16. This may involve updating a NODE_VERSION env var in your deploy provider’s UI, or changing the version in an .nvmrc
file. Look to your deploy provider for more information.
Switch from cross-undici-fetch
to @whatwg-node/fetch
This is just a recommendation.
If you’ve followed along some of our how to’s, like Using a Third Party API, you may be using cross-undici-fetch
on the api side. We recommend switching to @whatwg-node/fetch
.
Extend from RedwoodError
instead of RedwoodGraphQLError
This is just a recommendation.
If you’ve read the Error Masking section in the GraphQL docs, you may be making your own custom errors by extending from RedwoodGraphQLError
. While this’ll still work, we recommending extending from the new RedwoodError
instead.
depthLimitOptions
has changed to armorConfig
This only affects you if you set the
depthLimitOptions
option in thecreateGraphQLHandler
function, and can be applied via a codemod, or by manually updating the function in theapi/src/functions/graphql.{ts,js}
file.
Now that Redwood uses GraphQL Armor, the depthLimitOptions
option in the createGraphQLHandler
, where you set the max query depth, has changed to armorConfig
. There’s a codemod to do this for you, or you can do it manually.
Codemod Available
To implement this step via automated codemod, run:
npx @redwoodjs/codemods@canary use-armor
Here’s how you’d do it manually:
import { createGraphQLHandler } from '@redwoodjs/graphql-server'
// ...
export const handler = createGraphQLHandler({
loggerConfig: { logger, options: {} },
directives,
sdls,
services,
- depthLimitOptions: { maxDepth: 6 },
+ armorConfig: { maxDepth: { n: 6 } },
onException: () => {
// Disconnect from your database with an unhandled exception.
db.$disconnect()
},
})
matchPath
is no longer exported from @redwoodjs/router
This was an undocumented internal function that was exported; the odds this affects you are low.
The <NavLink>
component takes a new prop, matchSubPaths
, that makes it apply the class in the activeClassName
prop to matching sub-paths:
<NavLink activeClassName="activeTest" matchSubPaths to="/users">
Users
</NavLink>
For example, if you have a NavLink
to /users
and set matchSubPaths
, if the user navigates to /users/2
, the activeClassName
still applies. The useMatch
hook takes the same parameter.
The breaking part: the matchPath
function, which is what this component uses under the hood, is no longer exported from @redwoodjs/router
. If you’re using it, we recommend you use the useMatch
hook instead. If useMatch
doesn’t cover what you were using matchPath
for, please let us know.