ra-auth-google
An auth provider for react-admin that handles authentication using the Google Identity Services (GIS).
It allows to easily enable users to sign to your app in using their Google account, either personal, or professional via Google Workspaces.
This package provides:
- An
authProvider
to use with your react-admin app - A
<LoginButton>
component to render the Sign in with Google button - A
<OneTapButton>
component to enable the One Tap feature on your website - An
httpClient
to make authenticated requests to your API - A helper method called
initGoogleAuthProvider
, allowing to get all the above from a single configuration object
Supported Features
- Sign In with Google for Web, including all supported (client-side) features
- One Tap
- Automatic sign-in
- Sign in with Google button (popup mode - see limitations)
- Revoking user consent (at logout)
The following features are not (yet) included in this demo, but should also be supported:
- Authorizing for Web
- Using the Google Identity Service JavaScript library with the token model to call a Google API
- Using the Google Identity Service JavaScript library with the token model, along with the Google API Client Library for JavaScript, to call a Google API
- Using the Admin SDK API to retrieve all groups/roles of a user, to compute permissions
Limitations
Since they would require a back-end, the following features are not supported by this package:
- Automatic token refresh
- Sign in with Google button in redirect mode
It's up to you to implement these features in your back-end if you need them.
Installation
yarn add ra-auth-google
# or
npm install --save ra-auth-google
Basic Usage
The following example implements Sign in with Google button in popup mode, using the client id configured in the .env
file.
# in .env
VITE_GOOGLE_CLIENT_ID="my-application-client-id.apps.googleusercontent.com"
<!-- in index.html -->
<!DOCTYPE html>
<html lang="en">
<head>
<!-- Add the following line to load the Google Identity Services library -->
<script async defer src="https://accounts.google.com/gsi/client"></script>
</head>
<body>
<!-- ... -->
</body>
</html>
// in src/App.tsx
import React from "react";
import { Admin, Resource, Login } from "react-admin";
import { initGoogleAuthProvider } from "ra-auth-google";
import dataProvider from "./dataProvider";
import posts from "./posts";
const App = () => {
const { authProvider, LoginButton } = initGoogleAuthProvider();
const LoginPage = () => (
<Login>
<LoginButton theme="filled_black" />
</Login>
);
return (
<Admin
authProvider={authProvider}
dataProvider={dataProvider}
title="Example Admin"
loginPage={LoginPage}
>
<Resource name="posts" {...posts} />
</Admin>
);
};
export default App;
Tip: This example uses the <Login>
component provided by react-admin to create the login page. You can also include the <LoginButton>
component in your own login page if you prefer.
Providing Client ID Via Props
You can also provide the client id via a prop instead of using the .env
file.
// in src/App.tsx
import React from 'react';
import { Admin, Resource, Login } from 'react-admin';
import { initGoogleAuthProvider } from "ra-auth-google";
import dataProvider from './dataProvider';
import posts from './posts';
const App = () => {
const { authProvider, LoginButton } = initGoogleAuthProvider({
client_id: "my-application-client-id.apps.googleusercontent.com",
});
const LoginPage = () => (
<Login>
<LoginButton />
</Login>
);
return (
<Admin
authProvider={authProvider}
dataProvider={dataProvider}
title="Example Admin"
loginPage={LoginPage}
>
<Resource name="posts" {...posts} />
</Admin>
);
};
export default App;
One Tap Button
The <OneTapButton>
component can be used to enable the One Tap button on one or more pages.
It can be used as a standalone component, or to wrap a page.
import { initGoogleAuthProvider } from "ra-auth-google";
const Standalone = () => {
const { OneTapButton } = initGoogleAuthProvider();
return (
<div>
<OneTapButton />
<h1>My Page</h1>
</div>
)
};
const Wrapper = () => {
const { OneTapButton } = initGoogleAuthProvider();
return (
<OneTapButton>
<div>
<h1>My Page</h1>
</div>
</OneTapButton>
)
};
Here is a full example enabling the One Tap button on a custom route:
// in src/App.tsx
import React from "react";
import { Admin, Resource, Login, CustomRoutes } from "react-admin";
import { Route } from "react-router-dom";
import { initGoogleAuthProvider } from "ra-auth-google";
import dataProvider from "./dataProvider";
import posts from "./posts";
const App = () => {
const { authProvider, LoginButton, OneTapButton } = initGoogleAuthProvider();
const LoginPage = () => (
<Login>
<LoginButton />
</Login>
);
return (
<Admin
authProvider={authProvider}
dataProvider={dataProvider}
title="Example Admin"
loginPage={LoginPage}
>
<Resource name="posts" {...posts} />
<CustomRoutes>
<Route
path="/custom"
element={
<div>
<OneTapButton />
<h1>My Page</h1>
</div>
}
/>
</CustomRoutes>
</Admin>
);
};
export default App;
Customizing The Sign In With Google Button
Google does not support using your own button to sign in. However, you can customize the button, using the provided props, to better integrate it with your app.
You can also use the code generator to try out the different options.
For instance, here is how to use a black-filled button theme instead of the default one:
// in src/App.tsx
import React from 'react';
import { Admin, Resource, Login } from 'react-admin';
import { initGoogleAuthProvider } from "ra-auth-google";
import dataProvider from './dataProvider';
import posts from './posts';
const App = () => {
const {
authProvider,
LoginButton,
} = initGoogleAuthProvider();
const LoginPage = () => (
<Login>
<LoginButton theme="filled_black" />
</Login>
);
return (
<Admin
authProvider={authProvider}
dataProvider={dataProvider}
title="Example Admin"
loginPage={LoginPage}
>
<Resource name="posts" {...posts} />
</Admin>
);
};
export default App;
Automatic Sign-In
You can enable Automatic sign-in by setting the auto_select
param to true
:
// in src/App.tsx
import React from "react";
import { Admin, Resource, Login, CustomRoutes } from "react-admin";
import { Route } from "react-router-dom";
import { initGoogleAuthProvider } from "ra-auth-google";
import dataProvider from "./dataProvider";
import posts from "./posts";
const App = () => {
const { authProvider, LoginButton, OneTapButton } = initGoogleAuthProvider({
auto_select: true,
});
const LoginPage = () => (
<Login>
<LoginButton />
</Login>
);
return (
<Admin
authProvider={authProvider}
dataProvider={dataProvider}
title="Example Admin"
loginPage={LoginPage}
>
<Resource name="posts" {...posts} />
<CustomRoutes>
<Route
path="/custom"
element={
<div>
<OneTapButton />
<h1>My Page</h1>
</div>
}
/>
</CustomRoutes>
</Admin>
);
};
export default App;
Configuring The Google Identity Services Library
initGoogleAuthProvider
accepts all the parameters supported by the GIS library.
For example, to change the text of the title and messages in the One Tap prompt, use the context
parameter:
// in src/App.tsx
import React from "react";
import { Admin, Resource, Login, CustomRoutes } from "react-admin";
import { Route } from "react-router-dom";
import { initGoogleAuthProvider } from "ra-auth-google";
import dataProvider from "./dataProvider";
import posts from "./posts";
const App = () => {
const { authProvider, LoginButton, OneTapButton } = initGoogleAuthProvider({
context: "use",
});
const LoginPage = () => (
<Login>
<LoginButton />
</Login>
);
return (
<Admin
authProvider={authProvider}
dataProvider={dataProvider}
title="Example Admin"
loginPage={LoginPage}
>
<Resource name="posts" {...posts} />
<CustomRoutes>
<Route
path="/custom"
element={
<div>
<OneTapButton />
<h1>My Page</h1>
</div>
}
/>
</CustomRoutes>
</Admin>
);
};
export default App;
Making Authenticated Requests To The API
ra-auth-google
includes an httpClient
that can be used to make authenticated requests to your API. This helper automatically adds the credentials token
to the request headers.
Here is an example with ra-data-json-server
:
// in src/App.tsx
import { initGoogleAuthProvider } from "ra-auth-google";
import jsonServerProvider from "ra-data-json-server";
import React from "react";
import { Admin, Login, Resource } from "react-admin";
import posts from "./posts";
const App = () => {
const { authProvider, LoginButton, httpClient } = initGoogleAuthProvider();
const dataProvider = jsonServerProvider(
"https://jsonplaceholder.typicode.com",
httpClient
);
const LoginPage = () => (
<Login>
<LoginButton />
</Login>
);
return (
<Admin
authProvider={authProvider}
dataProvider={dataProvider}
title="Example Admin"
loginPage={LoginPage}
>
<Resource name="posts" {...posts} />
</Admin>
);
};
export default App;
Choosing The Token Storage Strategy
By default, ra-auth-google
will store the received token in localStorage, under the "token"
key.
If you want to store it somewhere else, or need to use a different key, you can provide your own tokenStore
:
// in src/myTokenStore.tsx
import { TokenStore } from "ra-auth-google";
export const myTokenStore: TokenStore = {
getToken: () => localStorage.getItem("my_token"),
setToken: (token) => localStorage.setItem("my_token", token),
removeToken: () => localStorage.removeItem("my_token"),
};
// in src/App.tsx
import { initGoogleAuthProvider } from "ra-auth-google";
import jsonServerProvider from "ra-data-json-server";
import React from "react";
import { Admin, Login, Resource } from "react-admin";
import posts from "./posts";
import { myTokenStore } from "./myTokenStore";
const App = () => {
const { authProvider, LoginButton, httpClient } = initGoogleAuthProvider({
tokenStore: myTokenStore,
});
const dataProvider = jsonServerProvider(
"https://jsonplaceholder.typicode.com",
httpClient
);
const LoginPage = () => (
<Login>
<LoginButton />
</Login>
);
return (
<Admin
authProvider={authProvider}
dataProvider={dataProvider}
title="Example Admin"
loginPage={LoginPage}
>
<Resource name="posts" {...posts} />
</Admin>
);
};
export default App;
API
initGoogleAuthProvider
Use initGoogleAuthProvider
to create an authProvider, a login button, a One Tap button and an httpClient
from a single configuration object.
const { authProvider, LoginButton, OneTapButton, httpClient } = initGoogleAuthProvider();
It accepts the following parameters:
-
client_id
: Optional - The Google API client ID of your application. Tries to use theGOOGLE_CLIENT_ID
environment variable if not provided. -
tokenStore
: Optional - The token store to use to store the token. Defaults tolocalStorageTokenStore
. - Other parameters: Optional - All the other parameters are passed to the Google Identity Services library. See the documentation for the full list of supported parameters.
const { authProvider, LoginButton, OneTapButton, httpClient } = initGoogleAuthProvider({
client_id: "my-application-client-id.apps.googleusercontent.com",
context: "use",
tokenStore: myTokenStore,
});
googleAuthProvider
Returns an authProvider that can be used with react-admin.
It accepts the following parameters:
-
gsiParams
: Required - Parameters for the Google Identity Services library. See the documentation for the full list of supported parameters. -
tokenStore
: Optional - The token store to use to store the token. Defaults tolocalStorageTokenStore
.
const authProvider = googleAuthProvider({
gsiParams: {
client_id: "my-application-client-id.apps.googleusercontent.com",
ux_mode: "popup",
},
tokenStore: myTokenStore,
});
<GoogleLoginButton>
Returns a component that can be used to render the Sign in with Google button.
It accepts the following props:
-
gsiParams
: Required - Parameters for the Google Identity Services library. See the documentation for the full list of supported parameters. -
sx
: Optional - Allows to customize the MUI<Box>
inside which the button is rendered. See MUIsx
prop for more information. - Other parameters: Optional - All the other parameters are passed to the Sign in with Google button, and allow for customization. See the documentation for the full list of supported parameters.
const LoginButton = () => (
<GoogleLoginButton
gsiParams={{
client_id: "my-application-client-id.apps.googleusercontent.com"
}}
theme="filled_black"
/>
);
<GoogleOneTapButton>
The <GoogleOneTapButton>
can be used either as a standalone component, or as a wrapper.
The component itself doesn't render anything, but it triggers the Google API to display the One Tap prompt if the user is not yet signed in.
Use it in the pages that you want to enable the One Tap feature on.
const Standalone = () => (
<div>
<GoogleOneTapButton />
<h1>My Page</h1>
</div>
);
const Wrapper = () => (
<GoogleOneTapButton>
<div>
<h1>My Page</h1>
</div>
</GoogleOneTapButton>
);
It supports the following props:
-
children
: Optional - The children to render. If provided, the component will be used as a wrapper. - Other parameters: Required - All the other parameters are passed to the Google Identity Services library. See the documentation for the full list of supported parameters.
const MyPage = () => (
<div>
<GoogleOneTapButton
client_id="my-application-client-id.apps.googleusercontent.com"
/>
<h1>My Page</h1>
</div>
);
googleHttpClient
Returns an httpClient
that can be used to make authenticated requests to your API.
It accepts the following parameters:
-
tokenStore
: Optional - The token store to use to store the token. Defaults tolocalStorageTokenStore
.
const httpClient = googleHttpClient({ tokenStore: myTokenStore });
Demo
You can find a working demo, along with the source code, in this project's repository: https://github.com/marmelab/ra-auth-google
License
This auth provider is licensed under the MIT License and sponsored by marmelab.