DEV Community

WangGithub0
WangGithub0

Posted on

2 1 1 1 1

Exploring OAuth 2.0: Enabling Google Authentication in a Pure Browser-Based Web App running on Cloudflare

As I mentioned last week, our ChatCraft run on cloudflare function, which is quite similar to node.js, so I tried to do the Google OAuth using Using OAuth 2.0 for Web Server Applications.

There are four steps:
Step 1: Redirect to Google's OAuth 2.0 server
Step 2: Google prompts user for consent
Step 3: Handle the OAuth 2.0 server response - get code
Step 4: Calling Google APIs - get user info using access_token

  • Step1, Step2 The fist 2 steps are quiet similar to what I did before. In order to get the provider, I also added it in state, and parse the state later. const url = buildUrl( "https://accounts.google.com/o/oauth2/v2/auth", // If there's a chatId, piggy-back it on the request as state chatId ? { client_id: GOOGLE_CLIENT_ID, redirect_uri: GOOGLE_REDIRECT_URI, response_type: GOOGLE_RESPONSE_TYPE, scope: GOOGLE_SCOPE, state: "provider=google&chat_id=" + chatId, } : { client_id: GOOGLE_CLIENT_ID, redirect_uri: GOOGLE_REDIRECT_URI, response_type: GOOGLE_RESPONSE_TYPE, scope: GOOGLE_SCOPE, state: "provider=google", } ); return Response.redirect(url, 302);

At first, I run the login with a provider /api/login?provider=${provider}&chat_id=${chatId}, so I can get the provider directly using reqUrl.searchParams.get("provider") then it directed to Google, after user consent, it redirect back with state state=provider%3Dgoogle%26chat_id%3Dl77..., so I used get state and then decodeURI+get provider:

Here is how I parse the state.

 let provider = reqUrl.searchParams.get("provider");
  if (!provider) {
    let state = reqUrl.searchParams.get("state");
    if (state) {
      state = decodeURIComponent(state);
      const stateParams = new URLSearchParams(state);
      provider = stateParams.get("provider");
    }
  }

//state=provider%3Dgoogle%26chat_id%3Dl77...
  let chatId = reqUrl.searchParams.get("chat_id");
  if (!chatId) {
    let state = reqUrl.searchParams.get("state");
    if (state) {
      state = decodeURIComponent(state);
      const stateParams = new URLSearchParams(state);
      chatId = stateParams.get("chatId");
    }
  }
Enter fullscreen mode Exit fullscreen mode

Image description

Image description

  • Step3 This step I tried to test using Postman firstly, then I got the code.
export async function requestGoogleAccessToken(
  code: string,
  CLIENT_ID: string,
  CLIENT_SECRET: string,
  GOOGLE_REDIRECT_URI: string
) {
  const url = buildUrl("https://accounts.google.com/o/oauth2/token", {
    code: code,
    client_id: CLIENT_ID,
    client_secret: CLIENT_SECRET,
    redirect_uri: GOOGLE_REDIRECT_URI,
    grant_type: "authorization_code",
  });

  const res = await fetch(url, {
    method: "POST",
    headers: {
      "User-Agent": "chatcraft.org",
    },
  });

  if (!res.ok) {
    throw new Error(`Failed to get Google token: ${res.status} ${await res.text()}`);
  }

  const result = (await res.json()) as {
    error?: string;
    access_token: string;
  };
  if (result.error) {
    throw new Error(`Error in Google token response: ${result.error}`);
  }

  return result.access_token;
}
Enter fullscreen mode Exit fullscreen mode
  • Step4 This step I also tried to test using Postman firstly, then I got the user information.
export async function requestGoogleUserInfo(token: string): Promise<User> {
  const res = await fetch("https://www.googleapis.com/oauth2/v1/userinfo", {
    headers: {
      Accept: "application/json",
      Authorization: `Bearer ${token}`,
      "User-Agent": "chatcraft.org",
    },
  });

  if (!res.ok) {
    throw new Error(`Failed to get Google User info: ${res.status} ${await res.text()}`);
  }

  const { email, name, picture } = (await res.json()) as {
    email: string;
    name: string;
    picture: string;
  };

  return { username: email, name: name, avatarUrl: picture };
}
Enter fullscreen mode Exit fullscreen mode

Finally, I made it!

Image description

I have been testing with my own account locally. However, when I deployed to the production environment, I noticed that the Google environment parameters are all undefined. After connecting with my professor, I found the environment variables name with my variables are different. After revising it, it threw "Error 400: redirect_uri_mismatch". Later we will check the redirect uri set in Google Authorized redirect URIs.

Sentry blog image

How to reduce TTFB

In the past few years in the web dev world, we’ve seen a significant push towards rendering our websites on the server. Doing so is better for SEO and performs better on low-powered devices, but one thing we had to sacrifice is TTFB.

In this article, we’ll see how we can identify what makes our TTFB high so we can fix it.

Read more

Top comments (0)

Sentry image

See why 4M developers consider Sentry, “not bad.”

Fixing code doesn’t have to be the worst part of your day. Learn how Sentry can help.

Learn more

👋 Kindness is contagious

Please leave a ❤️ or a friendly comment on this post if you found it helpful!

Okay