iTranslated by AI

The content below is an AI-generated translation. This is an experimental feature, and may contain errors. View original article
🐈

Implementing Google OAuth 2.0 Login with Blitz.js

に公開

Introduction

In this article, we will implement login using Google OAuth 2.0 in Blitz.js by leveraging Next.js API functions and Passport.js!

How Authentication Works in Blitz.js

In Blitz.js, when you generate a project with blitz new, email-based authentication and session management are already generated. Amazing.

For session management, Blitz.js seemingly adopts the same approach as SuperTokens, and it is said to be supervised by Rishabh Poddar, the CTO of SuperTokens.

Furthermore, Blitz.js supports the use of verify callbacks with Passport.js—a Node.js authentication middleware—using its plugin-like system called "Strategies."

You can find over 500 strategies on the Passport.js website:
http://www.passportjs.org/packages/

↓ The author of Blitz.js was featured on the SuperTokens homepage:

Creating Google Credentials


Go to the "Credentials" screen under "APIs & Services" in the Google Cloud Platform console.
Click Create Credentials -> OAuth client ID

Set the application type to "Web application" and give it a name.

Finally, add the generated Client ID and Client Secret to your .env.local file.

GOOGLE_CLIENT_ID="Your Client ID"
GOOGLE_CLIENT_SECRET="Your Client Secret"

Implementing Google Login Using Passport.js

We will use the Google OAuth 2.0 Strategy for Passport.js.

yarn add passport-google-oauth20

https://github.com/mstade/passport-google-oauth2

In Blitz.js, creating app/api/auth/[...auth].ts generates two routing configurations:

  • /api/auth/[strategyName] is the URL to start the login process.
  • /api/auth/[strategyName]/callback is the callback URL specified earlier in GCP.
    Very clever.

In this case, accessing /api/auth/google will redirect you to the Google login screen.

Also, we will update the User model generated by blitz new to include an icon image retrieved from Google.

blitz generate model User icon:string
app/api/auth/[...auth].ts
import { passportAuth } from "blitz"
import db from "db"
import { Strategy as GoogleStrategy } from "passport-google-oauth20"

export default passportAuth({
  successRedirectUrl: "/",
  errorRedirectUrl: "/",
  strategies: [
    {
      strategy: new GoogleStrategy(
        {
          clientID: process.env.GOOGLE_CLIENT_ID as string,
          clientSecret: process.env.GOOGLE_CLIENT_SECRET as string,
          callbackURL:
            process.env.NODE_ENV === "production"
              ? "deployment-url/api/auth/google/callback"
              : "http://localhost:3000/api/auth/google/callback",
          scope: ["email", "profile"],
        },
        async function (_token, _tokenSecret, profile, done) {
          const email = profile.emails && profile.emails[0]?.value
          const user = await db.user.upsert({
            where: { email },
            create: {
              email,
              name: profile.displayName,
              icon: profile.photos[0]?.value,
            },
            update: { email },
          })
          const publicData = {
            userId: user.id,
            roles: [user.role],
            source: "google",
          }
          done(null, { publicData })
        }
      ),
    },
  ],
})

With just this, Google login is implemented! Blitz.js is amazing.

Let's try it out

  • Change the login button on the top page to a Link to /api/auth/google and make it display the username.
  • Try displaying the icon image with <img src={currentUser.icon} alt="user-icon" />.
app/pages/index.tsx
const UserInfo = () => {
  const currentUser = useCurrentUser()
  const [logoutMutation] = useMutation(logout)

  if (currentUser) {
    return (
      <>
        <button
          className="button small"
          onClick={async () => {
            await logoutMutation()
          }}
        >
          Logout
        </button>
        <div>
          User id: <code>{currentUser.id}</code>
          <br />
          User role: <code>{currentUser.role}</code>
+          <br />
+          User name: <code>{currentUser.name}</code>
+          <img src={currentUser.icon} alt="user-icon" />
        </div>
      </>
    )
  } else {
    return (
      <>
        <Link href="/signup">
          <a className="button small">
            <strong>Sign Up</strong>
          </a>
        </Link>
        <Link href="/api/auth/google">
          <a className="button small">
            <strong>Log In With Google</strong>
          </a>
        </Link>
      </>
    )
  }
}

I was able to log in with an arbitrary Google account using the created login button and display the username and icon image.

Blitz.js is Amazing

It seems the official release is in April, so I'm looking forward to it.
https://blitzjs.com/

Discussion