-
Notifications
You must be signed in to change notification settings - Fork 2
Authentication
Authentication is straightforward with React Server. React Server provides an authenticate
util to check the headers for a valid jwt token.
All you need to do to authenticate a user is calling authenticate
in your serverside component and if the user doesn't have a valid Bearer token in the headers authorization, rendering will throw an error.
backend/src/components/Session
import { authenticate, isClientContext } from '@state-less/react-server';
import { ServerSideProps } from './ServerSideProps';
import { JWT_SECRET } from '../config';
export const Session = (props, { context = { headers: {} } }) => {
const { headers } = context;
if (!isClientContext(context)) {
return <ServerSideProps session={null} />;
}
const session = authenticate(headers, JWT_SECRET);
return <ServerSideProps session={session} />;
};
React-client comes with an AuthenticationProvider
which you can use to simplify the authentication process.
To get started, wrap your App in an AuthenticationProvider
. This provider exposes an authenticate
function that needs to be called with a strategy and a data option.
The data required depends on the strategy used to authenticate the current user.
For instance, with a Google OAuth login, you need to pass AuthStrategy.Google
as the strategy, and data expects an object with {accessToken, idToken}
.
Both can be obtained by performing a client-side OAuth login with Google.
To implement a Google OAuth login, you can use the library react-google-login, which provides a <GoogleLogin />
component that you can use to set up your client-side OAuth flow.
Here's an example of how to log in and obtain a valid session from React Server using react-google-login and authenticate from @state-less/react-client
:
import { Avatar, Button } from '@mui/material';
import { authContext } from '@state-less/react-client';
import { useContext } from 'react';
import GoogleLogin from 'react-google-login';
import GoogleIcon from '@mui/icons-material/Google';
const logError = (response) => {
console.log(response);
};
export const LoggedInGoogleButton = (props) => {
const { session, authenticate } = useContext(authContext);
if (session.strategy !== 'google') {
return null;
}
const decoded = session.strategies.google.decoded;
return (
<Button color="secondary">
<Avatar
src={decoded.picture}
sx={{ width: 24, height: 24, mr: 1 }}
></Avatar>
{decoded.name}
</Button>
);
};
export const GoogleLoginButton = () => {
const { session, authenticate } = useContext(authContext);
return session?.strategy === 'google' ? (
<LoggedInGoogleButton />
) : (
<GoogleLogin
clientId="534678949355-odq15l4236372p864f63ci14g794sfqf.apps.googleusercontent.com"
buttonText="Login"
onSuccess={({ accessToken, tokenId }) => {
console.log('Authenticating with google');
authenticate({
strategy: 'google',
data: { accessToken, idToken: tokenId },
});
}}
render={(props) => {
return (
<Button color="secondary" {...props}>
<GoogleIcon sx={{ mr: 1 }} />
Login
</Button>
);
}}
onFailure={logError}
cookiePolicy={'single_host_origin'}
/>
);
};
As demonstrated, it only requires a single call to authenticate
to log in to React Server, offering a versatile approach for logging in with various providers.
To consume the backend session from your client, you can use useComponent
.
frontend/src/server-components/Session.tsx
import { Alert } from '@mui/material';
import { useComponent } from '@state-less/react-client';
export const ServerSession = () => {
const [component, { error, loading }] = useComponent('session', {});
if (loading) return <Alert severity="info">Loading...</Alert>;
if (error) return <Alert severity="error">{error.message}</Alert>;
return (
<Alert severity="success">{`Currently logged in on the server as ${sessionInfo(
component.props.session
)}`}</Alert>
);
};
export const googleInfo = (session) => {
return `${session.strategies.google.decoded.name} (${session.strategies.google.decoded.email})`;
};
const handler = {
google: googleInfo,
};
export const sessionInfo = (session) => {
return handler[session.strategy](session);
};
In order for the authenticate
call to succeed, you need to wrap your App in an AuthProvder
. The provider exposes information about the current session and brokers authentication with the server.
The AuthenticationProvider
needs to be wrapped in an ApolloProvider
or be given an apollo client instance.
import { AuthProvider } from '@state-less/react-client';
const App = () => {
<ApolloProvider client={localClient}>
<AuthProvider>
<Main />
</AuthProvider>
</ApolloProvider>
}
This is it, you can now enjoy authenticating with the server.