Golang oAuth2 Google Example for Web and API

Introduction

The oAuth2 protocol has almost become a standard for securing websites and API services. Developers no longer need to store and manage userIDs and passwords for their users. Offloading the authentication to oAuth providers such as Google, Facebook, Linkedin, Github keeps the authentication with username and password, within those providers rather than passing through the developer’s application. This reduces the risk that an application will leak user credentials and puts more control in the users hands on for managing authentication with their accounts.

I wanted to provide this capability in my own apps and went looking for a pattern to use in Go. Typically when i’m on a hunt like this I find bits and pieces that I stitch together into a reference pattern. For this oAuth pattern most of the result I found referenced back to a couple limited examples on the google site.

Then it happened, I stumbled on a fantastic write up and sample app that covers every main point better than I could have written.

So rather than me trying to replicate or modify it, go read this beautiful 2 part series by @skarlso

- Part 1: http://skarlso.github.io/2016/06/12/google-signin-with-go/
- Part 2: http://skarlso.github.io/2016/11/02/google-signin-with-go-part2/ 
- Github: https://github.com/Skarlso/google-oauth-go-sample

This shows perfectly how to perform the initial login functions. Later in this article I’ll briefly discuss some enhancements I made to reuse the token for multiple api calls

Initial Login with oAuth

In this example @skarlso is using google just for the site authentication


tok, err := conf.Exchange(oauth2.NoContext, c.Query(“code”))
if err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
client := conf.Client(oauth2.NoContext, tok)
email, err := client.Get(“https://www.googleapis.com/oauth2/v3/userinfo")

He then saves the email in session as the user id

session.Set(“user-id”, u.Email)

From then on he uses the session state to ensure the user is logged in, not needing to go back to google


v := session.Get(“user-id”)
if v == nil {
c.HTML(http.StatusUnauthorized, “error.tmpl”, gin.H{“message”: “Please login.”})
c.Abort()
}

This is the core of the web auth pattern and @skarlso has provided a fantastic sample application showing that.

Token Reuse for API calls

For my use not only did I want to log the user in but I also wanted to use those credentials for subsequent google api calls.

First we needed to update the scope we’re using


Scopes: []string{
https://www.googleapis.com/auth/userinfo.email",
https://www.googleapis.com/auth/cloud-platform",
},

Then in the AuthHandler I saved the token details before requesting the client


tok, err := conf.Exchange(oauth2.NoContext, c.Query(“code”))
if err != nil {
c.AbortWithError(http.StatusBadRequest, err)
return
}
// save the token
session.Set(“AccessToken”, tok.AccessToken)
session.Set(“RefreshToken”, tok.RefreshToken)
session.Set(“TokenType”, tok.TokenType)
session.Set(“Expiry”, tok.Expiry.Format(time.RFC3339))
session.Save()
client := conf.Client(oauth2.NoContext, tok)

Now I can use that info in other handlers to recreate the client and make API calls.


func ListStuff(c *gin.Context) {
   ctx := context.Background()
session := sessions.Default©
   token := new(oauth2.Token)
token.AccessToken = session.Get(“AccessToken”).(string)
token.RefreshToken = session.Get(“RefreshToken”).(string)
token.RefreshToken = session.Get(“RefreshToken”).(string)
t := session.Get(“Expiry”).(string)
token.Expiry, _ = time.Parse(time.RFC3339, t)
token.TokenType = session.Get(“TokenType”).(string)
   client := conf.Client(ctx, token)
// …
}

There are a few enhancements we can make to this still, like saving the token directly rather than separating it out into individual strings, but this gives you a taste of the basic pattern

Conclusion

There you have it, we’ve used google oAuth to log the user in, then we reused those tokens for future calls by the user. This allow us to create an interface to google APIs that act based on the users credentials and access rights.

One clap, two clap, three clap, forty?

By clapping more or less, you can signal to us which stories really stand out.