docs/versioned_docs/version-3.x/authentication.md
@redwoodjs/auth contains both a built-in database-backed authentication system (dbAuth), as well as lightweight wrappers around popular SPA authentication libraries.
We currently support the following third-party authentication providers:
You can also implement your own custom auth client. Check out the Custom docs for more info.
:::info Auth Playground
Check out the Auth Playground for examples of the auth experience with each provider or check out the source code.
:::
You will need to instantiate your authentication client and pass it to the <AuthProvider>. See individual auth docs in the menu for your specific provider.
Once your auth provider is set up you'll get access to the various authentication variables and functions by destructuring them from the useAuth hook:
import { useAuth } from '@redwoodjs/auth'
export const MyComponent = () => {
const { currentUser, isAuthenticated, logIn, logOut } = useAuth()
return (
<ul>
<li>The current user is: {currentUser}</li>
<li>Is the user logged in? {isAuthenticated}</li>
<li>Click to <button type="button" onClick={logIn}>login</button></li>
<li>Click to <button type="button" onClick={logOut}>logout</button></li>
</ul>
)
}
The following variables and functions are available from the useAuth hook:
logIn(options?): Differs based on the client library, with Netlify Identity a pop-up is shown, and with Auth0 the user is redirected. Options are passed to the client.logOut(options?): Log the current user out. Options are passed to the client.signUp(options?): If the provider has a sign up flow we'll show that, otherwise we'll fall back to the logIn flow.currentUser: An object containing information about the current user as set on the api side, or null if the user is not authenticated.userMetadata: An object containing the user's metadata (or profile information) fetched directly from an instance of the auth provider client, or null if the user is not authenticated.reauthenticate(): Refetch the authentication data and populate the state.getToken(): Returns a JWT.client: Access the instance of the client which you passed into AuthProvider.isAuthenticated: Determines if the current user has authenticated.hasRole(['admin']): Determines if the current user is assigned a role like "admin" or assigned to any of the roles in a list such as ['editor', 'author'].loading: The auth state is restored asynchronously when the user visits the site for the first time, use this to determine if you have the correct state.The hasRole() function can be used to implement basic role-based authorization control (RBAC). This assumes that your getCurrentUser() function adds a roles property to the returned object.
export const MyComponent = () => {
const { isAuthenticated, hasRole } = useAuth()
return (
<>
{hasRole('admin') && (
<Link to={routes.admin()}>Admin</Link>
)}
{hasRole(['author', 'editor']) && (
<Link to={routes.posts()}>Admin</Link>
)}
</>
)
}
Routes can require authentication by wrapping them in a <Private> component. An unauthenticated user will be redirected to the page specified in unauthenticated.
import { Router, Route, Private } from '@redwoodjs/router'
const Routes = () => {
return (
<Router>
<Route path="/" page={HomePage} name="home" />
<Route path="/login" page={LoginPage} name="login" />
<Private unauthenticated="login">
<Route path="/admin" page={AdminPage} name="admin" />
<Route path="/secret-page" page={SecretPage} name="secret" />
</Private>
</Router>
)
}
Routes and Sets can also be restricted by role by specifying hasRole="role" or hasRole={['role', 'another_role']}) in the <Private> component. A user not assigned the role will be redirected to the page specified in unauthenticated.
import { Router, Route, Private } from '@redwoodjs/router'
const Routes = () => {
return (
<Router>
<Route path="/" page={HomePage} name="home" />
<Route path="/login" page={LoginPage} name="login" />
<Route path="/forbidden" page={ForbiddenPage} name="forbidden" />
<Private unauthenticated="login">
<Route path="/secret-page" page={SecretPage} name="secret" />
</Private>
<Set private unauthenticated="forbidden" roles="admin">
<Route path="/admin" page={AdminPage} name="admin" />
</Set>
<Private unauthenticated="forbidden" roles={['author', 'editor']}>
<Route path="/posts" page={PostsPage} name="posts" />
</Private>
</Router>
)
}
GraphQL requests automatically receive an Authorization header when a user is authenticated and Redwood will decode and verify the header, making the user available (if they are logged in) in context.currentUser.
context is a global so it's always available on your api side, like in your services.
console.log(context.currentUser)
// {
// sub: '<netlify-id>
// email: '[email protected]',
// [...]
// }
You can map the "raw decoded JWT" into a real user object by passing a getCurrentUser function to createGraphQLHandler
Our recommendation is to create a src/lib/auth.js|ts file that exports a getCurrentUser. (Note: You may already have stub functions.)
import { getCurrentUser } from 'src/lib/auth'
// Example:
// export const getCurrentUser = async (decoded) => {
// return await db.user.findUnique({ where: { decoded.email } })
// }
//
export const handler = createGraphQLHandler({
schema: makeMergedSchema({
schemas,
services: makeServices({ services }),
}),
getCurrentUser,
})
The value returned by getCurrentUser() is available in context.currentUser
Use the requireAuth and skipAuth GraphQL directives to provide protection to individual GraphQL calls.
If you are interested in contributing to the Redwood Auth Package, please start here.