OAuth 2.0 Implementation in Golang
Tags: go, authentication, oauth2, google
Published: 2023-01-08 05:56:39

## Introduction :

Security is without doubt a very important feature for any public and even private facing service or API and it’s something that you need to pay a lot of attention to get right.

In this tutorial, we are going to see an in-depth explanation of OAuth2 and its implementation using Golang.

### OAuth 2.0 :

OAuth 2.0, which stands for “Open Authorization”, is a standard designed to allow a website or application to access resources hosted by other web apps on behalf of a user.

OAuth 2.0 is an authorization protocol and NOT an authentication protocol. As such, it is designed primarily as a means of granting access to a set of resources, for example, remote APIs or user data.

OAuth 2.0 uses Access Tokens. An Access Token is a piece of data that represents the authorization to access resources on behalf of the end user. OAuth 2.0 doesn’t define a specific format for Access Tokens. However, in some contexts, the JSON Web Token (JWT) format is often used. This enables token issuers to include data in the token itself. Also, for security reasons, Access Tokens may have an expiration date.

### What are we building :

In this tutorial, we are going to build a simple API using Google API for authentication and authorization of the user.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/3b2h9h363bj8x9iwfkbv.png)

## Prerequisites💯 :

To continue with the tutorial, firstly you need to have Golang and Fiber installed. If you've not gone through the previous tutorials on the Fiber Web Framework series you can see them [here](https://dev.to/siddheshk02/series/21222) :)

### Installations :

- [Golang](https://go.dev/doc/install)
- [Go-Fiber](https://docs.gofiber.io/): We'll see this ahead in the tutorial.

## Getting Started 🚀:

Let's get started by creating the main project directory go-oauth2 by using the following command.

(🟥Be careful, sometimes I've done the explanation by commenting in the code)

```
mkdir go-oauth2 //Creates a 'go-oauth2' directory
cd go-oauth2 //Change directory to 'go-oauth2'
```

Now initialize a mod file. (If you publish a module, this must be a path from which your module can be downloaded by Go tools. That would be your code's repository.)

```
go mod init github.com/<username>/go-oauth2
```

To install the Fiber Framework run the following command :

```
go get -u github.com/gofiber/fiber/v2
```

### Client ID and Client Secret :

Before moving ahead let's get the Client ID and Client Secret for Google API which we are going to store in .env file in our main directory go-oauth2 .

Follow the steps below for getting the Client Credentials for Google API :

- Open [Google APIs console](https://console.cloud.google.com/apis), Click on the Credentials page.
- Click Create Credentials > OAuth client ID. Select the 
  Application type as Web Application and add the name of the 
  Application. For this tutorial, I've entered the Application as 
  `Go-Auth2` .
- Click ADD URI under Authorized JavaScript origins and add 
  `http://localhost` . Again click ADD URI and add 
  `http://localhost:8080` as URI 2.
- Click ADD URI under Authorized redirect URIs and add 
  `http://localhost:8080/google_callback` .
- Copy the Client Credentials Displayed.

After getting the Credentials, store them in the `.env` file as shown below.

```
GOOGLE_CLIENT_ID : <CLIENT_ID> //Replace <CLIENT_ID> with your ID.
GOOGLE_CLIENT_SECRET : <CLIENT_SECRET> //Replace <CLIENT_SECRET> with your SECRET.
```
## Initializing 💻:

Let's set up our server by creating a new instance of Fiber. For this create a file `main.go` and add the following code to it :

```
package main

import (
	"github.com/Siddheshk02/go-oauth2/controllers" //imoprting the controllers package
	"github.com/gofiber/fiber/v2"
)

func main() {
	app := fiber.New()

	app.Post("/google_login", controllers.GoogleLogin)
	app.Post("/google_callback", controllers.GoogleCallback)

	app.Listen(":8080")

}
```
Firstly, we are going to work on Google Login. So, comment on the Google Callback route for now.

Now, make a package/folder `controllers` , in this folder create `google.go` file.

We are going to create the `GoogleLogin`, `GoogleCallback` functions in the `google.go` file

We are going to use the [golang.org/x/oauth2](https://pkg.go.dev/golang.org/x/oauth2) package, to install it run the command,

```
go get golang.org/x/oauth2
```
Before working on these functions, we need to define the oauth2 configurations for the Google API.

Let's define `oauth2.Config` variable object `GoogleLoginConfig` in the `GoogleConfig()` function in `config.go` file. For this create a package/folder `config` and a file `config.go` inside the folder.

```
package config

import (
	"log"
	"os"

	"github.com/joho/godotenv"
	"golang.org/x/oauth2"
	"golang.org/x/oauth2/google"
)

type Config struct {
	GoogleLoginConfig oauth2.Config
}

var AppConfig Config

func GoogleConfig() oauth2.Config {
	err := godotenv.Load(".env")
	if err != nil {
		log.Fatalf("Some error occured. Err: %s", err)
	}

	AppConfig.GoogleLoginConfig = oauth2.Config{
		RedirectURL:  "http://localhost:8080/google_callback",
		ClientID:     os.Getenv("GOOGLE_CLIENT_ID"),
		ClientSecret: os.Getenv("GOOGLE_CLIENT_SECRET"),
		Scopes: []string{"https://www.googleapis.com/auth/userinfo.email",
			"https://www.googleapis.com/auth/userinfo.profile"},
		Endpoint: google.Endpoint,
	}

	return AppConfig.GoogleLoginConfig
}
```

- **RedirectURL**: Redirect URLs are a critical part of the OAuth flow. After a user successfully authorizes an application, the authorization server will redirect the user back to the application.
- **ClientID**: This we earlier stored in the .env file. The Client_ID is a public identifier for apps. It is not guessable by third parties, so many implementations use something like a 32-character hex string. If the client ID is guessable, it makes it slightly easier to craft phishing attacks against arbitrary applications. It must also be unique across all clients that the authorization server handles.
- **ClientSecret**: This we earlier stored in the .env file. The 
  Client_Secret is known only to the application and the 
  authorization server. It is the application’s password. It must 
  be sufficiently random to not be guessable, which means you 
  should avoid using common UUID libraries which often take into 
  account the timestamp or MAC address of the server generating 
  it.
- **Scopes**: It is a mechanism in OAuth 2.0 to limit an application's 
  access to a user's account.

Update the `main.go` with the following code,

```
package main

import (
	"github.com/Siddheshk02/go-oauth2/config"
	"github.com/Siddheshk02/go-oauth2/controllers"
	"github.com/gofiber/fiber/v2"
)

func main() {
	app := fiber.New()

	config.GoogleConfig()

	app.Get("/google_login", controllers.GoogleLogin)
	//app.Post("/google_callback", controllers.GoogleCallback)

	app.Listen(":8080")

}
```
Now, let's work on the function `GoogleLogin()` in the `google.go` file.

```
package controllers

import (
	"github.com/Siddheshk02/go-oauth2/config"
	"github.com/gofiber/fiber/v2"
)

func GoogleLogin(c *fiber.Ctx) error {

	url := config.AppConfig.GoogleLoginConfig.AuthCodeURL("randomstate")

	c.Status(fiber.StatusSeeOther)
	c.Redirect(url)
	return c.JSON(url)
}
```
`randomstate` is called the State. It is a token to protect the user from CSRF attacks. You must always provide a non-empty string and validate that it matches the state query parameter on your redirect callback.

Now, let's test the login functions. Run the command `go run main.go` . Then go to the address `http://127.0.0.1:8080/google_login` in your browser. It must look like this,


![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/hfspr35d0bfe385tpeur.png)

If you are already signed in then it will show that particular mail id and Use another account option.

Now, the login function is done. Let's create the `GoogleCallback` function.

```
func GoogleCallback(c *fiber.Ctx) error {
	state := c.Query("state")
	if state != "randomstate" {
		return c.SendString("States don't Match!!")
	}

	code := c.Query("code")

	googlecon := config.GoogleConfig()

	token, err := googlecon.Exchange(context.Background(), code)
	if err != nil {
		return c.SendString("Code-Token Exchange Failed")
	}

	resp, err := http.Get("https://www.googleapis.com/oauth2/v2/userinfo?access_token=" + token.AccessToken)
	if err != nil {
		return c.SendString("User Data Fetch Failed")
	}

	userData, err := ioutil.ReadAll(resp.Body)
	if err != nil {
		return c.SendString("JSON Parsing Failed")
	}

	return c.SendString(string(userData))

}
```
Here, we are going to pass the state variable from the URL parameter and we are going to check the `randomstate` that we've set in the GoogleLogin function, matches the `randomstate` that we are getting. If it matches, then that's the correct data that we want.

Next, we are passing the code variable for getting the token from the google server using the function `Exchange()` .

After getting the access token, we are getting the user data in the variable `resp` . We are getting a JSON response and storing it in `userData` variable.

Let's test for the `/google_callback route`. Run the command `go run main.go` . Then go to the address `http://127.0.0.1:8080/google_login` in your browser. Sign in using your account. On Successful Sign-in, your data will be displayed.

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/cascj33e30s0sje14as0.png)

So, the API is ready. Further, you can add other features by connecting it to a Database and adding new users in your App, more routes, etc.

The complete code is saved in this [GitHub](https://github.com/Siddheshk02/go-oauth2) repository.

## Conclusion 🎉✨:

![Image description](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/ozcgho4dhlpzouloqvpg.gif)

To get more information about Golang concepts, projects, resources, etc. and to stay updated on the Tutorials do follow [Siddhesh on Twitter](https://twitter.com/siddhesh1102) and [GitHub](https://github.com/Siddheshk02).

Until then Keep Learning, Keep Building 🚀🚀