# Easy social login in Go

<div data-node-type="callout">
<div data-node-type="callout-emoji">🤕</div>
<div data-node-type="callout-text">The package used in this article is no longer maintained; you should switch to something more robust, like <a target="_blank" rel="noopener noreferrer nofollow" href="https://github.com/markbates/goth" style="pointer-events: none">Goth</a>.</div>
</div>

In the era of the conversion rate, where a user sign-up is fundamental and must be quick, the social login became a required tile of the UX. Not having this option will probably reduce the registrations, but nowadays enabling this feature is really simple, since every programming language has a lot of libraries that, with few lines of code, create a complete oAuth flow.

In this article we'll explore the case of Go with [Gocialite](https://github.com/danilopolani/gocialite), a Socialite (Laravel) inspired package very simple to use.

The idea of Gocialite is to provide both an easy package but also a hackable one. I was trying Goth, but with Revel, it was very hard to make it working, so why don't write a library more simple? For example, if you want to contribute, you just have to clone a file, change same variable et voilà.

But now, let's coding.

For this example we'll use [Gin](https://github.com/gin-gonic/gin) as router, but you can use as wall whatever you want, like Gorilla/Mux or others.

## Starting code and homepage

The first thing is of course to create a new Go project, in this case we'll name it "codelikepro/social". Inside it, there will be only a file named `main.go`. We'll use terminal, but of course you can create folders and the file manually.

```bash
$ mkdir -p $GOPATH/src/github.com/codelikepro/social
$ touch $GOPATH/src/github.com/codelikepro/social/main.go
```

Now that our file exists, we need to start writing the package name and the necessary imports.

First of all, install gocialite and gin with `go get`:

```bash
$ go get github.com/danilopolani/gocialite
$ go get github.com/gin-gonic/gin
```

These are the only two external packages we'll use, so we are ready to code. Note I commented `fmt` and `net/http` packages because we'll need them later.

Open `main.go` with our favorite editor and write the following:

```go
package main

import (
	// "fmt"
	// "net/http"

	"github.com/danilopolani/gocialite"
	"github.com/gin-gonic/gin"
)

// Define our gocialite instance
var gocial = gocialite.NewDispatcher()

func main() {

}
```

At this point in our `main` function we'll initialize our Gin router and render a homepage with a simple button *Sign in with Github*.

```go
func main() {
	router := gin.Default()

	router.GET("/", indexHandler)
	// router.GET("/auth/github", redirectHandler)
	// router.GET("/auth/github/callback", callbackHandler)

	router.Run("127.0.0.1:9090")
}

// Show the homepage
func indexHandler(c *gin.Context) {
    c.Writer.Write([]byte("<html><head><title>Gocialite example</title></head><body>" +
    "<a href='/auth/github'><button>Login with GitHub</button></a>" +
    "</body></html>"))
}

// Redirect user to social login
func redirectHandler() {

}

// Collect user info
func callbackHandler() {

}
```

With Gin we declared three routes:

* A homepage, responding to route */*
    
* A redirect bridge at */auth/github*. This page will only redirect the user to correct login of the provided driver.
    
* A callback page at */auth/github/callback* where we'll collect the user information.
    

If we test our script running, inside our project folder, `go run main.go`, we'll just see a button – not working, *obviously*.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1687894248609/5ce76924-d879-4e55-bd69-3880a81e948e.png align="center")

## Redirect the user

The next step is to create our *bridge*, where we retrieve the redirect URL and we send the user to it.

Uncomment the route `router.GET("/auth/github", redirectHandler)` and let's write the `redirectHandler` function.

If you don't have a GitHub app, go to [New OAuth Application](https://github.com/settings/applications/new) and create a new one. Make sure to use `http://localhost:9090/auth/github/callback` as **Authorization callback URL**.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1687894115947/ed8c5cce-2f93-4a86-99be-2dc57c9f3a28.png align="center")

In the next step you'll Client ID and Client Secret, to populate our `appSettings`.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1687894261370/c8ea921a-ef5b-45c5-b784-74607fc49d01.png align="center")

With our client information obtained, we are ready to write the code to redirect the user.

```go
// Redirect user to social login
func redirectHandler(c *gin.Context) {
    // Define our settings
    appSettings := map[string]string{
        "clientID":     "xxxxxxxxxxxxxx",
        "clientSecret": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
        "redirectURL":  "http://localhost:9090/auth/github/callback",
    }

    // Retrieve the URL
    authURL, err := gocial.New().
        Driver("github").
        Scopes([]string{"public_repo"}). // Specify custom scopes
        Redirect(
            appSettings["clientID"],
            appSettings["clientSecret"],
            appSettings["redirectURL"],
		)

	// Check for errors
	if err != nil {
		c.Writer.Write([]byte("Error: " + err.Error()))
		return
	}

	// Redirect to authURL with status 302
	c.Redirect(http.StatusFound, authURL)
}
```

We used `Driver()` method to specify our provider, in this case, *Github*, but you can use [a lot of other social](https://github.com/danilopolani/gocialite/blob/master/README.md#available-drivers). Then, with `Scopes()`, we defined our custom scopes. In our case, we want access to its public repositories, but if you don't need custom privileges you can omit the function. **Note** that we didn't pass "user" scopes because Gocialite includes them by itself.

Finally, in the `Redirect()` function we pass our information and we retrieve our authURL.

Before redirecting the user, we check for errors and then, with `c.Redirect()`, a Gin function, we send the user to GitHub login approval with status 302 (Temporary redirect) taken from the [net/http](https://golang.org/pkg/net/http/#pkg-constants) package (click it to see all the available status). **Remember** to uncomment the `"net/http"` line in our imports!

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1687894173549/b6db2a97-a16c-4d69-a76c-4b7e624639e5.png align="center")

## Get user info

The last part of our flow is the callback to handle user information and we are going to write it in the `callbackHandler` function.

Here we have to pass to the `Handle()` method two values retrievable from the query string: **state** and **code**. **State** is a secret string that Gocialite passed in the beginning to the provider and now it checks if it's equal, in order to block [CSRF attacks](https://en.wikipedia.org/wiki/Cross-site_request_forgery). **Code** is a string needed to start the exchange with the provider for user authentication.

It will return three variables: a *user*, a *token* and an *error*. If there's an error, the first two will be nil, otherwise the error will be nil.

```go
func callbackHandler(c *gin.Context) {
	// Retrieve query params for state and code
	state := c.Query("state")
	code := c.Query("code")

	// Handle callback and check for errors
	user, token, err := gocial.Handle(state, code)
	if err != nil {
		c.Writer.Write([]byte("Error: " + err.Error()))
		return
	}

	// Print token information
    fmt.Printf("%#v", token)
	// Print in terminal user information
    fmt.Printf("%#v", user)

	// If no errors, show provider name
	c.Writer.Write([]byte("Hi, " + user.FullName))
}
```

If everything went well, it will print in your browser "*Hi, NAME*", in my case "*Hi, Grork*".

We also use `fmt.Printf` with the `%#v` format to print in the terminal the full `user` and `token` content, but in production you should remove them.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1687894293945/6894db54-d986-43b4-b951-99c4fe961df1.png align="center")

The `user` variable will contain the main information of the current logged-in user:

* ID
    
* Username
    
* FirstName
    
* LastName
    
* FullName
    
* Email
    
* Avatar
    
* Raw
    

They will be all strings except for raw, which is a map of interfaces (`map[string]interface{}`) containing the original JSON returned by the provider. Note that all fields (except *Raw*) could be empty.

The token will be an [oauth2.Token](https://godoc.org/golang.org/x/oauth2#Token) struct containing the following data:

* AccessToken *string*
    
* TokenType *string*
    
* RefreshToken *string*
    
* Expiry *time.Time*
    

If you want to set up a multi provider social login, [here there is a full example](https://github.com/danilopolani/gocialite/wiki/Multi-provider-example).

---

If you like Gocialite, feel free to leave a star!

<iframe src="https://ghbtns.com/github-btn.html?user=danilopolani&repo=gocialite&type=star&count=true&size=large" width="160px" height="30px"></iframe>
