Demystifying OAuth 2.0: A Beginner’s Guide to Google Sign-In in Go

Abinav Ghimire
readytowork, Inc.
Published in
5 min readMar 25, 2024
Go GCP

In today’s digital landscape, the integrity of user data and application security are non-negotiable. As developers, it’s our responsibility to prioritize authentication mechanisms that safeguard against unauthorized access. This tutorial is designed to equip you with the knowledge and tools needed to enhance authentication within your Go application using Google as the authentication provider. We’ll explore OAuth 2.0, a robust protocol for secure authorization, and delve into its integration with Google Sign-In.

GCP: Project Kickoff and OAuth2.0 Tokenization

To integrate OAuth 2.0 authentication effectively, it’s imperative to initiate a project within the Google Developer Console, housed within the expansive Google Cloud Platform. Obtaining OAuth authentication tokens is a pivotal step in this process. If you’re already operating within the Google ecosystem and possess an existing project, leveraging it can streamline the authentication implementation.

GCP dashboard

Creating a new project is straightforward. Begin by accessing the Google Cloud Console through this link. Once there, navigate to the top left corner and click on “Select a project.” This action prompts the creation of a new project or selection of an existing one, paving the way for OAuth 2.0 integration.

Create a new Project in GCP

After creating the project, select the newly created project from the “Select a Project” dropdown menu, which can be found at the top of the page, and go to “APIs & Services” from the navigation bar on the left side.

Now, let’s move to the credentials page and click on the “OAuth Client ID” option from the dropdown menu that appears after selecting “+ Create Credentials.” On the credentials creation page, opt for the “Web Application” option under “Application type.” In the “Authorized redirect URIs” section, include the callback URL specific to your application; this is where user-related data will be directed. Once you’ve configured the necessary details, click “Create.”

Create OAuth2.0 Creds

After clicking the “Create” button, you’ll gain access to your Client ID and Client Secret key. This step is crucial as these secret keys will facilitate authorizations from the specified URIs, ensuring secure interactions between your application and Google services.

On the same “APIs & Services” page, Within the OAuth consent screen, you’ll be prompted to fill in the “Application Name” field. This name will be displayed to users when they’re asked to grant permission to your application. After filling in the application name, ensure to provide other required information such as authorized domains, scopes, and other details based on your application’s requirements. Finally, hit the “Save” button to save your changes. By completing these steps, you’ll ensure that your application is properly configured to request user consent before accessing their data.

OAuth Consent Screen

Implementing OAuth 2.0 Authentication in Go

In our Go application, it’s essential to initialize the OAuth Service to facilitate various functionalities as needed. By utilizing the OAuth credentials and tokens obtained earlier, your application gains the necessary authorization to interact securely with Google’s external APIs and services. This setup enhances the overall functionality and reliability of your Go application, enabling it to perform tasks efficiently while adhering to security protocols.

In this article, we have used “golang.org/x/oauth2” and “golang.org/x/oauth2/google” to kickstart the service. Our configuration includes the redirect URL we provided earlier, along with scopes tailored to request user-related data. The code snippet provided below gives an overview of the service.

import (
"golang.org/x/oauth2"
"golang.org/x/oauth2/google"
)

// NewOAuthClient
func NewOAuthClient(env Env) *oauth2.Config {

OAuthClient := &oauth2.Config{
ClientID: env.OAuthClientId,
ClientSecret: env.OAuthClientSecret,
Endpoint: google.Endpoint,
RedirectURL: "http://localhost:8000/api/auth/callback/google",
Scopes: []string{
"https://www.googleapis.com/auth/userinfo.profile",
"https://www.googleapis.com/auth/userinfo.email"},
}

return OAuthClient
}

Next, we’ll leverage the initialized service to craft a function responsible for generating a URL and accepting additional required information. This URL will seamlessly be responsible for redirecting users to the Google account sign-in page. Additionally, we’ll develop a callback API tasked with handling the code generated by Google Cloud for authenticated users which will also pass the additional information that we provided back to us. We have already set up this callback API on the Google cloud credentials page.

// @Router    /oauth/sign-in[GET]
func (cc UserController) OAuthSignIn(c *gin.Context) {
extraInfo:="Extra info"
url :=oAuthService.AuthCodeURL(extraInfo)
c.JSON(200, url)
}

After a successful sign-in, Google sends a code to our callback URL. This code allows us to get tokens for the user and with this token, we can ask Google for more details about the user, like their email. Once we have the user’s email, we can perform additional tasks, such as checking if they’re already in our database creating a new user account for them, and setting tokens in client cookies.

//Struct to keep authenticated user data
type OAuthUser struct {
OAuthId string `json:"oauthId"`
Name string `json:"name"`
Email string `json:"email"`
VerifiedEmail bool `json:"verified_email"`
}

// @Router /api/auth/callback/google[GET]
func (cc UserController) OAuthCallback(c *gin.Context) {
resData := OAuthUser{}

// This state will contain data sent during sign in
state := c.Request.FormValue("state")

code := c.Request.FormValue("code")

//Exchange code for tokens
token, err := oAuthService.Exchange(context.Background(), code)

if err != nil {
fmt.Println("error getting oauth token")
return
}

//Use token for additional user info such as Emails
response, err := http.Get("https://www.googleapis.com/oauth2/v2/userinfo?access_token=" + token.AccessToken)

if err != nil {
fmt.Println("error getting oauth response")
return
}
defer response.Body.Close()
data, err := ioutil.ReadAll(response.Body)
if err != nil {
fmt.Println("error reading oauth response body")
return
}

if err := json.Unmarshal(data, &resData); err != nil {
fmt.Println("Error [OAuthSignUp] (ShouldBindJson) : ", err)
return
}

// Perform the logic as per requirement. For now, just setting client cookies
c.SetCookie("go-node-sso", token.AccessToken, 3600, "/", "localhost", false, true)
c.JSON(200,"Success")
}

For simplicity, signing out users can be achieved by clearing their cookies. This straightforward approach effectively logs users out of the system, ensuring a seamless and user-friendly experience.

// Clear the cookie to sign out the user
c.SetCookie("go-node-sso", "", -1, "/", "localhost", false, true)

Conclusion

In conclusion, implementing OAuth 2.0 authentication in Go applications opens up a world of possibilities for secure and seamless user interactions. By leveraging libraries such as golang.org/x/oauth2 and golang.org/x/oauth2/google, developers can streamline the authentication process and enhance the overall user experience. From initializing OAuth services to handling user sign-ins and sign-outs, we’ve covered essential aspects of OAuth integration in Go.

--

--