![Oracle Drags Its Feet in the JavaScript Trademark Dispute](https://cdn.sanity.io/images/cgdhsj6q/production/919c3b22c24f93884c548d60cbb338e819ff2435-1024x1024.webp?w=400&fit=max&auto=format)
Security News
Oracle Drags Its Feet in the JavaScript Trademark Dispute
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
@assaf/react-one-tap
Advanced tools
One-Tap is a super simple UI for signing with your Google account.
In supported browsers, one-tap is a slick user experience. It pops an overlay at the top/right of the page, with your active Google account. It takes a single click to sign in.
(And it supports having multiple Google accounts and let you switch between them)
When you return to the app after your token has expired (~1 hour), it will automatically sign you in again with a new token. There's a short delay which allows you to cancel.
You're not going to be bouncing around through multiple pages, the jerky OAuth flow you get with "Sign in with X" buttons.
So this is the quickest and easiest way I found to add single sign-on.
There are tradeoffs of course.
The user experience is great in supported browsers. That means Chrome, Firefox, and Edge (not Safari). On every operating system except for iOS and iPadOS, because those only have one browser (all other browsers are re-skinned Safari).
The user experience on unsupported browsers is not bad, it's just no better than OAuth.
This only works for users that have a Google account. However, you don't need a GMail or G Suite account. And many people have Google accounts, they use them for YouTube, Google Docs, Android, etc.
This UI doesn't blend well with other methods of authentication. So if you want to give users multiple options — email/password, magic link, Facebook, GitHub, etc — you should be using OAuth instead.
It should go without saying you're helping Google track users across the web. Because cookies are going away, the next best thing is to try and get everyone to sign in with their Google account.
I'm using this for "internal" apps. I know every user has a Google account, so not going to bother with other methods of authentication.
And we're using other Google products, so already signed into the account, so this is the easiest SSO experience.
If you trust Google to authenticate users, then it's a pretty good authentication mechanism. On the back-end verify users by checking email address, or email domain.
yarn add @assaf/react-one-tap
node install @assaf/react-one-tap
import { GoogleOneTap } from "@assaf/react-one-tap";
function LoginRequired({ children }) {
const buttonId = "google-sign-in-one-tap";
return (
<GoogleOneTap
autoSelect={true}
clientId={process.env.GOOGLE_CLIENT_ID}
context="use"
fallback={{ buttonId }}
>
{({ isSignedIn, profile, signOut }) => isSignedIn ? (
<main className="regular-page">
<header>
Welcome back {profile.name}
<button onClick={signOut}>sign out</button>
</header>
{children}
</main>
) : (
<main className="sign-in-page">
<div id={buttonId} />
</main>
)}
</GoogleOneTap>
);
}
If the user is authenticated, then isSignIn
is true. You also get the JWT access token (token
) and their Google profile (profile
).
The profile will include their name, email address, picture, etc. See Profile.
To allow users to sign out, you need to render a button that will call signOut
. It will sign them out of all open tabs, and revoke their access token. It will not sign them out of other browsers/devices.
On Safari and iOS, if the user has not signed in before, then the one-tap overlay doesn't show. If you want these users to sign in, you need to use the fallback
option.
That option expects the ID of a container element. It will render a sign-in button into that element.
With SWR you can do somethig like this:
import { useGoogleOneTap } from "@assaf/react-one-tap";
function MyComponent() {
const { headers, signOut } = useGoogleOneTap();
const { data, error } = useSWR(
token ? "/api" : null,
async (path) => {
const response = await fetch(path, { headers });
if (response.ok) {
return await response.json();
} else {
const { error } = await response.json();
throw new Error(error);
}
});
...
}
Or using SWRConfig
:
import { useGoogleOneTap } from "@assaf/react-one-tap";
function WithFetcher({ children }) {
const { headers, signOut } = useGoogleOneTap();
async function fetcher(url) {
if (!token) return null;
const response = await fetch(url, { headers });
if (response.ok) {
return await response.json();
} else {
const { error } = await response.json();
throw new Error(error);
}
};
return <SWRConfig value={{ fetcher }}>{children}</SWRConfig>;
}
useGoogleOneTap
returns the JWT access token (token
) and for convenience the authorization header to send to the server (headers
).
Create an authorization handler, varies by the server-side framework you use:
// Express, Connect, etc
import { authenticate } from "@assaf/react-one-tap";
const clientId = process.env.GOOGLE_CLIENT_ID;
const users = ['hi@example.com'];
function authorize(handler) {
return async function (request, response) {
const { status, profile, message } = await authenticate({ clientId, request });
if (!profile) return response.status(status).send(message);
const isAuthorized = profile.email_verified && users.includes(profile.email);
if (isAuthorized) handler(request, response);
else response.status(403).send("Access denied");
}
};
router.get('/customers', authorize(getCustomers));
// Next.js middelware
import { authenticate } from "@assaf/react-one-tap";
const clientId = process.env.GOOGLE_CLIENT_ID;
const users = ['hi@example.com'];
export default async function middleware(request) {
const { status, profile, message } = await authenticate({ clientId, request });
if (!profile) return new Response(message, { status });
const isAuthorized = profile.email_verified && users.includes(profile.email);
if (!isAuthorized) throw new Response("Access denied!", { status: 403 });
return NextResponse.next();
}
The authenticate
function looks for an Authorization
header with a bearer token in it. It verifies the token, this will reject revoked tokens (if the user signed out).
It returns three properties:
status
is one of 200, 401, or 403profile
is the user's profile, if authenticated succesfullymessage
is an error message to go along with 401/403 status codeFrom the profile, you have access to the user's name, email address, and whether that address was verified. See Profile.
You can check if the account is a G Suite account by looking at profile.hd
, and authenticate based on the domain (ie email: "me@example.com", hd: "example.com"
).
If you're storing state, use profile.sub
, which is the unique and immutable user identifier.
This allows users to change their email address. If you're using their email address from the profile, you may want to save is every so often. And also consider the email_verified
field.
FAQs
React component for Google One-Tap
We found that @assaf/react-one-tap demonstrated a not healthy version release cadence and project activity because the last version was released a year ago. It has 1 open source maintainer collaborating on the project.
Did you know?
Socket for GitHub automatically highlights issues in each pull request and monitors the health of all your open source dependencies. Discover the contents of your packages and block harmful activity before you install or update your dependencies.
Security News
Oracle seeks to dismiss fraud claims in the JavaScript trademark dispute, delaying the case and avoiding questions about its right to the name.
Security News
The Linux Foundation is warning open source developers that compliance with global sanctions is mandatory, highlighting legal risks and restrictions on contributions.
Security News
Maven Central now validates Sigstore signatures, making it easier for developers to verify the provenance of Java packages.