How to Implement OAuth 2.0 into a Golang App

How to Implement OAuth 2.0 into a Golang App

In this tutorial, we will focus on implementing authentication using OAuth 2.0 in a Golang application.

By Brian Wachira ·

Identity providers (IAMs) allow users to log in to related, yet independent, platforms using a single set of credentials.

To implement this, there are various authentication protocols and standards you can use, including Security Assertion Markup Language (SAML), Open Authorization (OAuth), and OpenID Connect (OIDC).

In this article, we'll focus on implementing authentication using OAuth 2.0 in a Golang application. You'll learn the ins and outs of the OAuth 2.0 protocol and follow a step-by-step guide to integrating it into Golang apps.

Table Of Contents

Let's get started!

Prerequisites

Before we dive into the implementation, make sure you have the following prerequisites in place:

  1. Knowledge of Golang: You should have a good understanding of the Golang programming language and its syntax. If you're new to Golang, we recommend checking out the official Golang documentation to get started.

  2. Golang development environment setup: Ensure that you have a working Golang development environment set up on your machine. You can download the latest version of Golang and follow the installation instructions for your operating system.

  3. Your preferred code editor. VS Code is my go-to choice.

  4. Have a general understanding of RESTful API design.

Before diving into OAuth 2.0 implementation in your Golang application, let's take a quick step back and understand what OAuth is all about. Once we've covered the basics, we'll jump into the core of the article, that is, integrating OAuth 2.0 into your Golang project.

What is OAuth 2.0?

OAuth (or Open Authorization) is an industry-standard protocol that streamlines user authorization in applications.

At its core, OAuth 2.0 is an authorization framework. It allows users to utilize their existing credentials to get access to resources on one application (ideally, the client application or website they are interacting with) using authorization from another trusted application like Google or Facebook—the identity providers.

This eliminates the need for repeatedly and manually entering credentials to verify their identity each time.

Essentially, while OAuth 2.0 involves user information, its primary purpose is to manage access control —providing a standardized way for applications to request and grant access to specific resources.

Key Components of OAuth 2.0 Authentication System

Generally, there are couple of essential components that make up the OAuth protocol authentication system. These are:

  1. Resource Server: The server that holds the protected resources (like user data) and validates access requests. It verifies the Client's authorization and, upon approval, delivers the requested resources.
  2. Client Application: The application requesting access to resources on the Resource Server. It initiates the OAuth 2.0 flow to obtain the necessary authorization.
  3. Authorization Server: The central authority responsible for authenticating users and issuing access tokens. It verifies user credentials and, if valid, grants tokens to the Client, allowing it to access resources on the Resource Server.

Now, let's break down the typical authentication flow using OAuth implementation with an identity provider.

How Does OAuth 2.0 Works?

Let's take a look at an illustration see Oauth 2.0 workflow.

oauth-workflow

Let's break this down:

  1. A user initiates the authentication flow, for example, by clicking on the "Sign in with Google" button on the client application (website or mobile app).

  2. The client application triggers the authorization flow by sending a request to the authorization server (the identity provider), which then redirects the user to the authentication page provided by the authorization server (e.g., Google's login page).

  3. The user authenticates themselves with the authorization server (the identity provider) by providing their credentials (e.g., email and password).

  4. Once the authorization server validates the user's credentials, it issues authentication tokens (such as an access token and a refresh token).

    The authorization server then sends a response back to the client application, typically by redirecting the user's browser using the previously registered redirect URI and including the issued tokens in the redirect. This response confirms the successful authentication and authorization of the user.

  5. With the obtained access token, the client application can now make requests, passing along the access token, to the resource server (API) to access protected resources such as user profile data, dashboard access, etc.

  6. If the token is valid, the resource server grants access to the requested resources.

Please note that this is a generic overview of the process. The actual flow will vary depending on the authorization grant type, as well as the specific authentication by the authorization server.

OAuth 2.0 vs. Custom Authentication

Arguably, there isn't a better alternatively. However, there are a few reasons that make the OAuth implementation hassle-free, such as:

  • Efficiency and Scalability: Using an identity provider's authentication and data management approach is technically more efficient and more optimized for performance, especially when you want to scale to multiple users.

  • Convenience for Users: Users won't need to manage multiple identities with varying credentials, making the experience more seamless and convenient.

  • Quicker Implementation: Implementing OAuth is generally quicker compared to building a custom solution from scratch. Ideally, with OAuth, you can bypass the technical overheads like storing user authentication data securely; simply integrate the social provider, and you're ready to proceed.

Although there's no single solution that works for every situation, using OAuth offers a secure and convenient way to handle your authentication requirements, without the need to reinvent the wheel.

Implementation OAuth 2.0 into a Golang App

You can find and clone the project's source code on GitHub.

Step 1: Setting up the Identity Provider (Google)

Before using OAuth 2.0, you need to acquire two sets of credentials, a client ID and client secret, from the authorization server, which in this case is Google.

To get your app's client ID and client secret:

  1. Navigate to the Google Cloud Platform, and sign in to the console using your Gmail credentials.

  2. On the console window, click Select a project at the top left side of the screen, and select New project to create a new project. setting-up-identity-provider

  3. Provide a project name, select the organization the project will belong to, and click CREATE. create-project

Once created, navigate to the project's overview page.

Now, it's time to configure the OAuth Consent Screen. The consent screen will allow users to grant permission for your application to access their Google account information.

To configure the OAuth Consent Screen, you need to provide application information. Here's how to do that:

  1. Click on OAuth consent screen under APIs & Services in the left menu pane. configure-content-screen-1

  2. Select whether your application is Internal (accessible only within your organization) or External (accessible by anyone on the internet). configure-content-screen-2

For testing purposes, choose External as it will allow you to simulate the user experience more accurately. Then, click the Create button to proceed.

  1. On the next screen, you'll be prompted to enter your application's details, such as the application name and a user support email address. configure-content-screen-3

    While providing an app logo image is optional during testing, it's recommended to include one as Google will display this information to users when asking for consent to access their data.

Once you've filled in the required details, click Save and Continue to proceed to setting up the client app credentials.

Step 3: Creating the Client Credentials

At this point, you need to create credentials that your client app will use to communicate with the Google Authorization Server. To do that:

  1. In the APIs & Services overview page, click on the Credentials tab on the left menu pane. creating-credentials-1

  2. Click on Create Credentials, and select OAuth client ID from the dropdown options. creating-credentials-2

  3. On the settings page that follows, select the application type as Web Application, and provide a name for your app.

  4. Next, enter http://localhost:5000 as the authorized origin and http://localhost:5000/auth/google/callback as the authorized redirect URL.

    These URLs will be created on your server. Once you push your app to production, make sure to update the URLs with your app's domain URLs.

  5. Click Create to obtain the client ID and client secret. creating-credentials-3

Lastly, copy and keep the generated credentials. You will use them in your Go web service to allow it to communicate with the Google Authorization Server.

Great! Now that we've set up the necessary credentials and configurations, let's proceed to implementing social login with OAuth in a Go project.

Step 4: Setting up a Golang Project

Before we start coding, let's understand what we'll be building. We'll create a standard Go REST API that serves a single HTML page with a 'sign-in with Google' button that allows users to sign in with their Gmail credentials.

On successful authentication, we should be able to render a basic success page to users.

To get started, create a project folder locally, and navigate to that directory:

mkdir go-oauth-web-service
cd go-oauth-web-service

Next, initialize a new Go module to manage the project's dependencies:

go mod init go-oauth-web-service

This command will create a go.mod file, which will house all the packages we'll use in this project.

Now, create a main.go file and include the following code to ensure everything is working as expected:

package main

import (
    "fmt"
)

func main() {
    fmt.Println("Hello, World!")
}

Finally, run the following command in your terminal to spin up the Go app:

go run main.go

With that, you have successfully created a basic Go application.

Step 5: Installing the Necessary Dependencies

To build the authentication service, we'll use Gin, a popular HTTP web framework for Golang. However, like most web frameworks (e.g., Express.js for Node.js), it doesn't come with a lot of built-in features to handle sophisticated tasks. For this reason, we'll use it with a couple of other packages.

Go ahead and install Gin, alongside the other dependencies we'll use:

go get github.com/gin-gonic/gin 
go get github.com/joho/godotenv 
go get github.com/markbates/goth 
go get golang.org/x/oauth2/google

Here is a breakdown of what each one of them does:

  • The godotenv package will allow you to load environment variables from a .env file.
  • The goth package extends Go's OAuth package and allows you to implement OAuth providers easily.

With these packages installed, we're ready to start building the API.

Step 6: Implementing the OAuth 2.0 Flow in Golang Using Goth

Go has a simple and powerful set of standard library packages such as the net/http package. Nonetheless, we'll use Gin to build the API's web server and the REST endpoints.

It provides a clean and concise syntax for defining routes, handling requests, and structuring your application. Along with simplicity, Gin is termed as being the fastest full-featured web framework for Go by the creators, making it a good choice for building HTTP servers.

To get started, let's import the necessary packages in main.go file:

package main

import (
	
	"fmt"
	"html/template"
	"log"
	"net/http"
	"os"

	"github.com/gin-gonic/gin"
	"github.com/joho/godotenv"
	"github.com/markbates/goth"
	"github.com/markbates/goth/gothic"
	"github.com/markbates/goth/providers/google"

)

Now, let's create a Gin HTTP server, listening on port 5000. Inside the main function, we'll initialize a Gin HTTP server, define the API routes, as well as configure Goth. But first, let's include the following code that will load the environment variables.

func main() {
    r := gin.Default()   

	err := godotenv.Load()
	if err != nil {
		log.Fatal(".env file failed to load!")
	}

	clientID := os.Getenv("CLIENT_ID")
	clientSecret := os.Getenv("CLIENT_SECRET")
	clientCallbackURL := os.Getenv("CLIENT_CALLBACK_URL")

	if clientID == "" || clientSecret == "" || clientCallbackURL == "" {
		log.Fatal("Environment variables (CLIENT_ID, CLIENT_SECRET, CLIENT_CALLBACK_URL) are required")
	}

}

The code first loads environment variables from the .env. Once loaded, the code uses the Getenv method to access them. It then checks if any crucial variables are missing.

If a required variable is not found, we'll log an error message, and terminate the execution to prevent unexpected behavior.

Now, create a .env file in the root of your project folder, and include the the environment variables, replacing the placeholders with your actual Google client ID, client secret, and callback URL:

CLIENT_ID=<your-client-id>
CLIENT_SECRET=<your-google-client-secret>
CLIENT_CALLBACK_URL=<your-callback-url>

Head over to your Google Cloud Console and copy these values.

What is Goth?

At this point, it has become almost standard to offer identity providers (such as Google, Facebook, GitHub, etc.) as an authentication method in addition to the traditional email/password approach.

This allows users to sign in with their existing accounts from popular services, providing a more convenient and user-friendly experience.

Goth is an open-source Go package that simplifies the work involved in writing custom solutions for authentication needs, particularly when implementing social logins using OAuth protocols. It provides a simple and concise interface to integrate with various identity providers.

One of the key advantages of using Goth is that it supports a vast number of providers out of the box, which means you can offer multiple authentication options to your users without having to implement each provider's integration from scratch.

To set up Goth in your Go application and configure the Google identity provider, go ahead, and include the following code in your main function

goth.UseProviders(
    google.New(clientID, clientSecret, clientCallbackURL),
)

The goth.UseProviders method registers the identity providers that your application will support for user authentication. It allows you to specify which providers your application will integrate with, making their authentication mechanisms available to your users.

On the other hand, the google.New method configures Goth to specifically use Google as one of the supported identity providers. This method takes three parameters: clientID, clientSecret, and the callbackURL

By calling google.New with the required parameters, you are setting up the necessary configuration for your application to interact with Google's OAuth service.

Once registered, your application can authenticate users through Google's OAuth service, allowing them to sign in with their existing Google accounts.

Now, let's define the API endpoints that your application will need to handle the authentication flow.

Here is a quick overview of the routes we need to define:

  • '/': This is the starting point of your application. The root route will serve an index.html (landing) page.

  • /auth/:provider: When users click on the "Sign In" button, it will hit this route, and they will be redirected to the Google authentication page to initiate the authentication flow.

  • /auth/:provider/callback: The authorized redirect endpoint that will handle the provider's response

  • /success: Assuming successful authentication through the provider, this route will serve a confirmation page.

    Ideally, after this confirmation, you'll want to redirect authenticated users to a protected area of your application. This could be a specific dashboard, profile page, or any functionality that requires users to be signed in.

    However, in this tutorial, we want to render a basic HTML sucess page.

In the main function, define the following routes:

r.GET("/", home)
r.GET("/auth/:provider", signInWithProvider)
r.GET("/auth/:provider/callback", callbackHandler)
r.GET("/success", Success)

r.Run(":5000")

Finally, the r.Run() method will spin up the Gin HTTP server and start listening on port 5000.

With the routes set up, we need to define the handler functions for these endpoints.

Step 7: Handling the User Authentication Flow: Defining Handler Functions

Following the authentication flow we mentioned earlier, the idea is to have users sign in with their Google credentials.

Therefore, we need to define handler functions that use Goth to initiate the authentication, redirect to the Google authentication page, then create handlers for the callback and success routes.

For this example, we'll keep things simple by defining the handler functions directly in our main.go file. However,if you are working on a larger project, you can define the handler functions in separate modules, and then import them into main.go.

Let's start by defining the handler function for the root route.

func home(c *gin.Context) {
	tmpl, err := template.ParseFiles("templates/index.html")
	if err != nil {
		c.AbortWithStatus(http.StatusInternalServerError)
		return
	}
	err = tmpl.Execute(c.Writer, gin.H{})
	if err != nil {
		c.AbortWithStatus(http.StatusInternalServerError)
		return
	}
}

When a user accesses the root URL, this handler will be triggered. Since we're using the built-in template package, it will parse and render the index.html file (which we'll create later) to display a "Sign-in" button to users. When clicked, this button should redirect users to the Google authentication page.

We'll use template.ParseFiles to parse the HTML file once it's loaded, and tmpl.Execute to render it.

Before creating the index.html file, add this line of code to make sure the template is loaded:

r.LoadHTMLGlob("templates/*")

Now, in the root directory, go ahead and create a templates/index.html file, and add this code:

<!DOCTYPE html>
<html lang="en">
<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Sign in with Google</title>
</head>
<body 
     style="
         font-family: Arial, sans-serif; 
         background-color: #f0f0f0; 
         display: flex; 
         justify-content: center; 
         align-items: center; 
         height: 100vh;"
>
  <div 
     style="
        background-color: #fff; 
        padding: 40px; 
        border-radius: 8px; 
        box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1); 
        text-align: center;"
    >
        <h1 style="color: #333; margin-bottom: 20px;">Welcome</h1>
        <a href="/auth/google"  style="
        display: inline-flex; 
        align-items: center; 
        justify-content: center; 
        background-color: #4285f4; 
        color: #fff; 
        text-decoration: none; 
        padding: 12px 20px; 
        border-radius: 4px; 
        transition: background-color 0.3s ease;">
        <span style="font-size: 16px; font-weight: bold;">Sign in with Google</span>
        </a>
  </div>
</body>
</html>

This will render a sign-in button with an anchor tag that points to the Google authentication page.

Let's proceed to creating the handler that will handle user requests to initiate the Google authentication flow.

func signInWithProvider(c *gin.Context) {

	provider := c.Param("provider")
	q := c.Request.URL.Query()
	q.Add("provider", provider)
	c.Request.URL.RawQuery = q.Encode()

	gothic.BeginAuthHandler(c.Writer, c.Request)
}

Ideally, once the users hit the /auth/:provider endpoint, google should be included in the request as the parameter. This would allow Goth's gothic.BeginAuthHandler to seamlessly handle the request and redirect users to the Google sign-in page for authentication.

However, there's a slight hitch. Since Goth is primarily written in Gorilla (a lightweight Go web framework), Gorilla's syntax for accessing parameters from URLs is a bit different from Gin. Therefore, it might cause a bit of an error.

To address this compatibility issue, the code uses c.Param("provider") to extract the provider name from the URL parameter.

Since Gin might not directly pass this information to Goth's handler, the code modifies the request's URL query string. It essentially adds the provider name as a key-value pair ("provider": provider) using q.Add. This ensures Goth can access the provider information it needs.

Finally, the modified query string is encoded and used to update the request URL. This manipulation guarantees that Goth's BeginAuthHandler receives the provider details, and can then redirect users to the appropriate Google sign-in page to proceed with authentication.

You can read more about this Gin framework compatibility issue in this GitHub issue. If you are curious, you can also review this issue on Chi's compatibility with Goth.

Now that we've covered initiating the authentication flow, let's explore the handler function for the callback URL. Go ahead include this handler function code:

func callbackHandler(c *gin.Context) {

  proider := c.Param("provider")
  q := c.Request.URL.Query()
	q.Add("provider", provider)
	c.Request.URL.RawQuery = q.Encode()

  
  _, err := gothic.CompleteUserAuth(c.Writer, c.Request)
  if err != nil {
      c.AbortWithError(http.StatusInternalServerError, err)
      return
  }

  c.Redirect(http.StatusTemporaryRedirect, "/success")
}

Similar to Sign-In handler, it retrieves the provider name (provider) from the URL parameter, and ensures Goth can access the provider information it needs.

The core functionality of this handler lies in the gothic.CompleteUserAuth function, which finalizes the user authentication process.

Essentially, after a successful sign-in, it retrieves authentication credentials including user details and tokens such as access and refresh tokens ( this will vary depending on the provider's implementation). You can then store and further tailor the authentication process according to your application's needs.

Now, since the callback handler will redirect uses to the /success route, go ahead and include this code to able to display a basic "successfull authentication page."

func Success(c *gin.Context) {

	c.Data(http.StatusOK, "text/html; charset=utf-8", []byte(fmt.Sprintf(`
      <div style="
          background-color: #fff;
          padding: 40px;
          border-radius: 8px;
          box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
          text-align: center;
      ">
          <h1 style="
              color: #333;
              margin-bottom: 20px;
          ">You have Successfull signed in!</h1>
          
          </div>
      </div>
  `)))
}

You can customize this basic success HTML content and styling further to match your application's design.

Step 8: Accessing Protected Resources

After a successful authentication flow, your application will have access to the user's information returned as a response from the provider.

Depending on the provider's implementation, the data returned could include the generated access tokens, which you can use to further enhance the app's security by using them to manage authorized requests to protected resources on behalf of the authenticated user.

The best part is that you can easily access these token values by using gothic.CompleteUserAuth in the callback handler.

Make sure to install the Gorilla/sessions package to manage sessions and store data.

go get github.com/gorilla/sessions

Then, set up a store as follows:

var store = sessions.NewCookieStore([]byte(os.Getenv("SESSION_KEY")))

It's important to store the access token securely, such as in an encrypted cookie or a server-side session store. This way, you can retrieve the token when making requests to protected resources without exposing it to the client-side.

For instance, assuming you want to store the token in a server-side session store, you can use the session package to do just that:

session := sessions.Default(c)
session.Set("access_token", accessToken)
err = session.Save()
if err != nil {
    c.AbortWithError(http.StatusInternalServerError, err)
    return
}

Now, when users access protected resources, you can retrieve the access token from the session (or wherever you stored it) and include it in the request headers, and then verify its validity on the server-side before granting access.

To further fortify your Go apps, implement proper authorization mechanisms to prevent unauthorized access to protected resources.

Keep in mind that access tokens have a limited lifetime, and you may need to handle token expiration and refresh the token when necessary.

Finally, to test the application, run:

go run main.go

And navigate to localhost:5000, click the "Sign in" button to redirect to the Google authentication page, and sign in.

sign-in

After a successful sign-in, you should be redirected back to the success URL, as follows:

success

Alternatively, you can also opt to run unit tests to assert that the authentication functionality is working as expected, specifying various edge cases.

Summary and Next Steps

The OAuth protocol is a secure and efficient way to implement social logins with different identity providers, which arguably improves the authentication experience for users.

No need for users to keep remembering multiple credentials; they only need to sign in using the preferred identity provider, and that's it.

If you want to learn more about authentication and authorization, go ahead, and explore these resources: