request / response

a blog about the web, Go, and building things

(written by Matt Silverlock)


A Custom Two-Way v-model Component in Vue.js

•••

In a migration of an internal admin dashboard from Vue 1 to Vue 2 (my JS framework of choice), two-way filters were deprecated. I’d been using two-way filters to format JSON data (stringifying it) and parsing user-input (strings) back to the original data type so that the dashboard wouldn’t need to know about the (often changing) schema of the API at the time.

In the process of re-writing the filter as a custom form input component, it needed to:

  • Be able to check the type of the data it was handling, and validate input against that type (if it was a Number, then Strings are invalid)
  • Be unaware the type of the data ahead-of-time (the API was in-flux, and I wanted it to adaptive)
  • Format more complex types (Objects/Arrays) appropriately for a form field.

Custom input components accept a value prop and emit an input event via the familiar v-model directive in Vue. Customising what happens in-between is where the value of writing your own input implementation comes in:

<tr v-for="(val, key) in item">
  <td class="label"></td>
  <td>
    <json-input :label=key v-model="item[key]"></json-input>
  </td>
</tr>

The parent component otherwise passes values to this component in v-model just like any other.

The Code

Although this is written as a single-file component, I’ve broken it down into two pieces.

The template section of the component is fairly straightforward:

<template>
  <input
    ref="input"
    v-bind:class="{ dirty: isDirty }"
    v-bind:value="format(value)"
    v-on:input="parse($event.target.value)"
  >
</template>

The key parts are v-bind:value (what we emit) and v-on:input (the input). The ref="input" attribute allows us to emit events via the this.$emit(ref, data) API.

Lodash includes well-tested type-checking functions: I use these for the initial checks instead of reinventing the wheel. Notably, isPlainObject should be preferred over isObject, as the latter has a broader meaning. I also use debounce to add a short delay to the input -> parse function call, so that we’re not overly aggressive about saying ‘invalid’ before the user has a chance to correct typos.

<script>
import debounce from "lodash.debounce"
import { isBoolean, isString, isPlainObject, isArrayLikeObject, isNumber, isFinite, toNumber } from "lodash"
export default {
  name: "json-input",
  props: {
    // The form label/key
    label: {
      type: String,
      required: true
    },
    // The form value
    value: {
      required: true
    }
  },
  data () {
    return {
      // dirty is true if the type of the field doesn't match the original
      // value passed.
      dirty: false,
      // typeChecked is true when the type of the original value has been
      // checked. This allows us to validate user-input against the original
      // (expected) type.
      typeChecked: false,
      isObject: false,
      isBoolean: false,
      isNumber: false,
      isString: false
    }
  },
  computed: {
    isDirty: function () {
      return this.dirty
    }
  },
  methods: {
    // init determines the JS type of the field (once) during initialization.
    init: function () {
      this.typeChecked = false
      this.isObject = false
      this.isBoolean = false
      this.isNumber = false
      this.isString = false
      if (isPlainObject(this.value) || isArrayLikeObject(this.value)) {
        this.isObject = true
      } else if (isNumber(this.value)) {
        this.isNumber = true
      } else if (isBoolean(this.value)) {
        this.isBoolean = true
      } else if (isString(this.value)) {
        this.isString = true
      }
      this.typeChecked = true
    },
    // format returns a formatted value based on its type; Objects are
    // JSON.stringify'ed, and Boolean & Number values are noted to prevent
    // reading them back as strings.
    format: function () {
      // Check the types of our fields on the initial format.
      if (!this.typeChecked) {
        this.init()
      }
      var res
      if (this.isObject) {
        res = JSON.stringify(this.value)
      } else if (this.isNumber) {
        res = this.value
      } else if (this.isBoolean) {
        res = this.value
      } else if (this.isString) {
        res = this.value
      } else {
        res = JSON.stringify(this.value)
      }
      return res
    },
    // Based on custom component events from
    // https://vuejs.org/v2/guide/components.html#Form-Input-Components-using-Custom-Events
    parse: debounce(function (value) {
      this.dirty = false
      if (this.isObject) {
        var res
        try {
          res = JSON.parse(value)
          this.$emit("input", this.format(res))
        } catch (e) {
          // Mark the field as dirty.
          this.dirty = true
          res = value
        }
        this.$emit("input", res)
        return
      }
      // Check the original type of the value; if the user-input does not conform
      // flag the field as dirty.
      if (this.isBoolean) {
        if (value === "true" || value === "false") {
          this.dirty = false
          // Convert back to a Boolean.
          this.$emit("input", (value === "true"))
          return
        }
        this.dirty = true
        this.$emit("input", value)
        return
      } else if (this.isNumber) {
        // Convert numbers back to numbers.
        let num = toNumber(value)
        if (isNumber(num) && isFinite(num)) {
          this.$emit("input", num)
          return
        }
        this.dirty = true
        this.$emit("input", value)
        return
      } else {
        // Write other types as-is.
        this.$emit("input", value)
        return
      }
    }, 1000)
  }
}
</script>

There’s a reasonable amount to digest here, but it makes sense if you think of it in three steps:

  1. init() - called on the initial format only. It type-checks the initial data, and sets typeChecked = true so we don’t run this again for the life of the component. The Lodash functions we import simplify this for us.
  2. format() - this method is responsible for emitting the value (e.g. to the DOM): it stringifies objects, converts any number back to a Number proper, etc.
  3. parse() - validates all user input against that initial type we asserted in the init method. If the user input is invalid, we set this.dirty = true (and add a CSS class of ‘dirty’) and emit the invalid value as-is, for the user to correct. TODO: return “input should be a Number” as a helpful error.

Steps #2 and #3 are universal to any custom form input component: how the data comes in, and how it goes out. This doesn’t just apply to <input> either: you could easily write your own <select> or <textarea> component by adapting this approach.

Wrap

Here’s a working demo: enter a non-Number value into the input and it’ll flag it appropriately. Change the value/type of the data in the parent Vue instance, re-run it, and you’ll see the component validate the new type automatically.


Serving a Vue, React or Ember JavaScript Application with Go.

•••

It’s 2016. You’re about to tie together a Popular Front-End JavaScript framework with a web service written in Go, but you’re also looking for a way to have Go serve the static files as well as your REST API. You want to:

  • Serve your /api/ routes from your Go service
  • Also have it serve the static content for your application (e.g. your JS bundle, CSS, assets)
  • Any other route will serve the index.html entrypoint, so that deep-linking into your JavaScript application still works when using a front-end router - e.g. vue-router, react-router, Ember’s routing.

Here’s how.

The Folder Layout

Here’s a fairly simple folder layout: we have a simple Vue.js application sitting alongside a Go service. Our Go’s main() is contained in serve.go, with the datastore interface and handlers inside datastore/ and handlers/, respectively.

~ gorilla-vue tree -L 1
.
├── README.md
├── datastore
├── dist
├── handlers
├── index.html
├── node_modules
├── package.json
├── serve.go
├── src
└── webpack.config.js

~ gorilla-vue tree -L 1 dist
dist
├── build.js
└── build.js.map

With this in mind, let’s see how we can serve index.html and the contents of our dist/ directory.

Note: If you’re looking for tips on how to structure a Go service, read through @benbjohnson’s excellent Gophercon 2016 talk.

Serving Your JavaScript Entrypoint.

The example below uses gorilla/mux, but you can achieve this with vanilla net/http or httprouter, too.

The main takeaway is the combination of a catchall route and http.ServeFile, which effectively serves our index.html for any unknown routes (instead of 404’ing). This allows something like example.com/deep-link to still run your JS application, letting it handle the route explicitly.

package main

import (
	"encoding/json"
	"flag"
	"net/http"
	"os"
	"time"

	"log"

	"github.com/gorilla/handlers"
	"github.com/gorilla/mux"
)

func main() {
	var entry string
	var static string
	var port string

	flag.StringVar(&entry, "entry", "./index.html", "the entrypoint to serve.")
	flag.StringVar(&static, "static", ".", "the directory to serve static files from.")
	flag.StringVar(&port, "port", "8000", "the `port` to listen on.")
	flag.Parse()

	r := mux.NewRouter()

    // Note: In a larger application, we'd likely extract our route-building logic into our handlers
    // package, given the coupling between them.

	// It's important that this is before your catch-all route ("/")
	api := r.PathPrefix("/api/v1/").Subrouter()
	api.HandleFunc("/users", GetUsersHandler).Methods("GET")
	// Optional: Use a custom 404 handler for our API paths.
	// api.NotFoundHandler = JSONNotFound

	// Serve static assets directly.
	r.PathPrefix("/dist").Handler(http.FileServer(http.Dir(static)))

	// Catch-all: Serve our JavaScript application's entry-point (index.html).
	r.PathPrefix("/").HandlerFunc(IndexHandler(entry))

	srv := &http.Server{
		Handler: handlers.LoggingHandler(os.Stdout, r),
		Addr:    "127.0.0.1:" + port,
		// Good practice: enforce timeouts for servers you create!
		WriteTimeout: 15 * time.Second,
		ReadTimeout:  15 * time.Second,
	}

	log.Fatal(srv.ListenAndServe())
}

func IndexHandler(entrypoint string) func(w http.ResponseWriter, r *http.Request) {
	fn := func(w http.ResponseWriter, r *http.Request) {
		http.ServeFile(w, r, entrypoint)
	}

	return http.HandlerFunc(fn)
}

func GetUsersHandler(w http.ResponseWriter, r *http.Request) {
	data := map[string]interface{}{
		"id": "12345",
		"ts": time.Now().Format(time.RFC3339),
	}

	b, err := json.Marshal(data)
	if err != nil {
		http.Error(w, err.Error(), 400)
		return
	}

	w.Write(b)
}

Build that, and then run it, specifying where to find the files:

go build serve.go
./serve -entry=~/gorilla-vue/index.html -static=~/gorilla-vue/dist/

You can see an example app live here

Summary

That’s it! It’s pretty simple to get this up and running, and there’s already a few ‘next steps’ we could take: some useful caching middleware for setting Cache-Control headers when serving our static content or index.html or using Go’s html/template package to render the initial index.html (adding a CSRF meta tag, injecting hashed asset URLs).

If something is non-obvious and/or you get stuck, reach out via Twitter.


Testing Your (HTTP) Handlers in Go

•••

You’re building a web (HTTP) service in Go, and you want to unit test your handler functions. You’ve got a grip on Go’s net/http package, but you’re not sure where to start with testing that your handlers return the correct HTTP status codes, HTTP headers or response bodies.

Let’s walk through how you go about this, injecting the necessary dependencies, and mocking the rest.

A Basic Handler

We’ll start by writing a basic test: we want to make sure our handler returns a HTTP 200 (OK) status code. This is our handler:

// handlers.go
package handlers

// e.g. http.HandleFunc("/health-check", HealthCheckHandler)
func HealthCheckHandler(w http.ResponseWriter, r *http.Request) {
    // A very simple health check.
    w.WriteHeader(http.StatusOK)
    w.Header().Set("Content-Type", "application/json")

    // In the future we could report back on the status of our DB, or our cache 
    // (e.g. Redis) by performing a simple PING, and include them in the response.
    io.WriteString(w, `{"alive": true}`)
}

And this is our test:

// handlers_test.go
package handlers

import (
    "net/http"
    "testing"
)

func TestHealthCheckHandler(t *testing.T) {
    // Create a request to pass to our handler. We don't have any query parameters for now, so we'll
    // pass 'nil' as the third parameter.
    req, err := http.NewRequest("GET", "/health-check", nil)
    if err != nil {
        t.Fatal(err)
    }

    // We create a ResponseRecorder (which satisfies http.ResponseWriter) to record the response.
    rr := httptest.NewRecorder()
    handler := http.HandlerFunc(HealthCheckHandler)

    // Our handlers satisfy http.Handler, so we can call their ServeHTTP method 
    // directly and pass in our Request and ResponseRecorder.
    handler.ServeHTTP(rr, req)

    // Check the status code is what we expect.
    if status := rr.Code; status != http.StatusOK {
        t.Errorf("handler returned wrong status code: got %v want %v",
            status, http.StatusOK)
    }

    // Check the response body is what we expect.
    expected := `{"alive": true}`
    if rr.Body.String() != expected {
        t.Errorf("handler returned unexpected body: got %v want %v",
            rr.Body.String(), expected)
    }
}

As you can see, Go’s testing and httptest packages make testing our handlers extremely simple. We construct a *http.Request, a *httptest.ResponseRecorder, and then check how our handler has responded: status code, body, etc.

If our handler also expected specific query parameters or looked for certain headers, we could also test those:

    // e.g. GET /api/projects?page=1&per_page=100
    req, err := http.NewRequest("GET", "/api/projects",
        // Note: url.Values is a map[string][]string
        url.Values{"page": {"1"}, "per_page": {"100"}})
    if err != nil {
        t.Fatal(err)
    }

    // Our handler might also expect an API key.
    req.Header.Set("Authorization", "Bearer abc123")

    // Then: call handler.ServeHTTP(rr, req) like in our first example.

Further, if you want to test that a handler (or middleware) is mutating the request in a particular way, you can define an anonymous function inside your test and capture variables from within by declaring them in the outer scope.

    // Declare it outside the anonymous function
    var token string
    test http.HandlerFunc(func(w http.ResponseWriter, r *http.Request){
        // Note: Use the assignment operator '=' and not the initialize-and-assign 
        // ':=' operator so we don't shadow our token variable above.
        token = GetToken(r)
        // We'll also set a header on the response as a trivial example of 
        // inspecting headers.
        w.Header().Set("Content-Type", "application/json")
    })

    // Check the status, body, etc.

    if token != expectedToken {
        t.Errorf("token does not match: got %v want %v", token, expectedToken)
    }

    if ctype := rr.Header().Get("Content-Type"); ctype != "application/json") {
        t.Errorf("content type header does not match: got %v want %v",
            ctype, "application/json")
    }

Tip: make strings like application/json or Content-Type package-level constants, so you don’t have to type (or typo) them over and over. A typo in your tests can cause unintended behaviour, becasue you’re not testing what you think you are.

You should also make sure to test not just for success, but for failure too: test that your handlers return errors when they should (e.g. a HTTP 403, or a HTTP 500).

Populating context.Context in Tests

What about when our handlers are expecting data to be passed to them in a context.Context? How we can create a context and populate it with (e.g.) an auth token and/or our User type?

Assumption: you have a custom handler type that provides a ServeHTTPC(context.Context, http.ResponseWriter, *http.Request). Go 1.7 will add context.Context to http.Request, which will make this even easier.

Note that for the below example, I’m using the Goji mux/router for context.Context compatible handlers today, but this approach will work for any router/mux/framework that works with context.Context.

func TestGetProjectsHandler(t *testing.T) {
    req, err := http.NewRequest("GET", "/api/users", nil)
    if err != nil {
        t.Fatal(err)
    }

    rr := httptest.NewRecorder()
    // e.g. func GetUsersHandler(ctx context.Context, w http.ResponseWriter, r *http.Request)
    handler := goji.HandlerFunc(GetUsersHandler)

    // Create a new context.Context and populate it with data.
    ctx = context.Background()
    ctx = context.WithValue(ctx, "app.auth.token", "abc123")
    ctx = context.WithValue(ctx, "app.user",
        &YourUser{ID: "qejqjq", Email: "user@example.com"})

    // Pass in our context, *http.Request and ResponseRecorder.
    handler.ServeHTTPC(ctx, rr, req)

    // Check the status code is what we expect.
    if status := rr.Code; status != http.StatusOK {
        t.Errorf("handler returned wrong status code: got %v want %v",
            status, http.StatusOK)
    }

    // We could also test that our handler correctly mutates our context.Context:
    // this is useful if our handler is a piece of middleware.
    if id , ok := ctx.Value("app.req.id").(string); !ok {
        t.Errorf("handler did not populate the request ID: got %v", id)
    }
}

Mocking Database Calls

Our handlers expect that we pass them a datastore.ProjectStore (an interface type) with three methods (Create, Get, Delete). We want to stub this for testing so that we can test that our handlers (endpoints) return the correct status codes.

You should read this Thoughtbot article and this article from Alex Edwards if you’re looking to use interfaces to abstract access to your database.

// handlers_test.go
package handlers

// Throws errors on all of its methods.
type badProjectStore struct {
    // This would be a concrete type that satisfies datastore.ProjectStore.
    // We embed it here so that our goodProjectStub type has all the methods
    // needed to satisfy datastore.ProjectStore, without having to stub out
    // every method (we might not want to test all of them, or some might be
    // not need to be stubbed.
    *datastore.Project
}

func (ps *projectStoreStub) CreateProject(project *datastore.Project) error {
    return datastore.NetworkError{errors.New("Bad connection"}
}

func (ps *projectStoreStub) GetProject(id string) (*datastore.Project, error) {
    return nil, datastore.NetworkError{errors.New("Bad connection"}
}

func TestGetProjectsHandlerError(t *testing.T) {
    var store datastore.ProjectStore = &badProjectStore{}

    // We inject our environment into our handlers.
    // Ref: http://elithrar.github.io/article/http-handler-error-handling-revisited/
    env := handlers.Env{Store: store, Key: "abc"}

    req, err := http.NewRequest("GET", "/api/projects", nil)
    if err != nil {
        t.Fatal(err)
    }

    rr := httptest.Recorder()
    // Handler is a custom handler type that accepts an env and a http.Handler
    // GetProjectsHandler here calls GetProject, and should raise a HTTP 500 if
    // it fails.
    handler := Handler{env, GetProjectsHandler)
    handler.ServeHTTP(rr, req)

    // We're now checking that our handler throws an error (a HTTP 500) when it
    // should.
    if status := rr.Code; status != http.StatusInternalServeError {
        t.Errorf("handler returned wrong status code: got %v want %v"
            rr.Code, http.StatusOK)
    }

    // We'll also check that it returns a JSON body with the expected error.
    expected := []byte(`{"status": 500, "error": "Bad connection"}`)
    if !bytes.Equals(rr.Body.Bytes(), expected) {
        t.Errorf("handler returned unexpected body: got %v want %v",
        rr.Body.Bytes(), expected)
    }

This was a slightly more complex example—but highlights how we might:

  • Stub out our database implementation: the unit tests in package handlers should not need a test database.
  • Create a stub that intentionally throws errors, so we can test that our handlers throw the right status code (e.g. a HTTP 500) and/or write the expected response.
  • How you might go about creating a ‘good’ stub that returns a (static) *datastore.Project and test that (for example) we can marshal it as JSON. This would catch the case where changes to the upstream type might cause it to be incompatible with encoding/json.

What Next?

This is by no means an exhaustive guide, but it should get you started. If you’re stuck building a more complex example, then ask over on the Gophers Slack community, or take a look at the packages that import httptest via GoDoc.


Combining Custom Domains, Go Packages and Jekyll

•••

Update: With Jekyll & GitHub Pages’ ability to serve extensionless permalinks, I’ve updated the article to show you how to use your own domain of top of the (ever reliable) gopkg.in - turning example.com/repo.v1 into the canonical import URL, but with gopkg.in serving the latest tag for you.

Here’s a short tutorial on how to combine GitHub Pages and gopkg.in to both version and serve your Go libraries and projects from a vanity import. Think yourdomain.com/pkgname.v1 instead of gopkg directly, or github.com/you/yourproject. This allows you to vendor your libraries without having to create multiple repositories, and therefore multiple sets of documentation, issues, and distinct/confusing import URLs.

Importantly, it works today, is maintainable, and is compatible with those vendoring your library downstream.

Note: the go-imports tag will be able to specify a branch/revision when issue 10913 is resolved, but users on older versions of Go won’t be able to pull your package down.

Domain Setup

I’ll assume you have your own domain and know enough to point a CNAME or A record for your domain to a host or IP. If not, GitHub’s documentation on this is pretty good, so reach for that if you get stuck.

Once you’ve set up the CNAME file for your GitHub Pages branch—usually gh-pages or the master branch if you have a user.github.io repository—you can get started with the rest.

Creating the Import URL

Assuming a new or existing Jekyll installation, create a new layout under _layouts in the root of your Jekyll project:

$ vim _layouts/imports.html

Put the following template into the layout you’ve just created:


<!DOCTYPE html>
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en-us">
<head>
  <meta http-equiv="content-type" content="text/html; charset=utf-8">

  <!-- Go Imports -->
  <meta name="go-import" content="{{ page.go-import }}">
  <meta name="go-source" content="{{ page.go-source }}">
  <meta http-equiv="refresh" content="0; {{ page.redirect }}">

</head>
<body>
</body>
</html>
    

We’ve also added a ‘go-source’ <meta> tag so that GoDoc knows where to fetch our API documentation from, and a re-direct so that anyone hitting that page in the browser is re-directed to GoDoc. You could alternatively have this re-direct to the domain itself, the GitHub repo or no-where at all.

We’ll also need to configure our Jekyll installation to use extensionless permalinks in _config.yml:

vim _config.yml

# Site settings
title: 'your project'
tagline: 'Useful X for solving Y'
baseurl: "/" 
permalink:  "/:title"

Once you’ve done this, create a pkgname.vN.html file at (e.g.) root of your Jekyll project: e.g. pkgname.v1.html. Future versions

# GitHub Pages + Jekyll 3.x can serve this as example.com/pkgame.v1 without the extension.
$ vim pkgname.v1.html

Now you can configure the template—this is the one you’ll re-use for future versions or other packages you create.


---
layout: imports

go-import: "example.com/pkgname.v1 git https://gopkg.in/someuser/pkgname.v1
go-source: 
    > 
      example.com/pkgname.v1
      _
      https://github.com/someuser/pkgname/tree/v1{/dir}
      https://github.com/someuser/pkgname/blob/v1{/dir}/{file}#L{line}

redirect: "https://godoc.org/example.com/pkgname.v1"
---

Commit that and push it to your GitHub Pages repository, and users can now import your package via go get -u example.com/pkgname.v1 (and eventually, a v2!).

Canonical Import Paths

Go 1.4 also introduced canonical import paths, which ensure that the package is imported using the canonical path (read: our new custom domain) instead of the underlying repository, which can cause headaches later due to duplicate imports and/or a lack of updates (if you change the underlying repo). Thankfully, this is easy enough to fix—add the canonical path alongside your package declarations:

package mypkg // import example.com/pkgname.v1

Users can’t ‘accidentally’ import github.com/you/mypkg now (it won’t compile).

Notes

As a final note, doing all of this is much easier with a new project or library. You can move an existing repository over to a custom domain, but existing users on the old import path may run into issues. Make sure to document it well!


Preventing Cross-Site Request Forgery in Go

•••

Cross-Site Request Forgery (CSRF) is probably one of the most common browser-based attacks on the web. In short, an attacker’s site ‘tricks’ a user into performing an action on your site using the user’s existing session. Often this is disguised as an innocuous-looking link/button, and without any way to validate that the request is occurring “cross-site”, a user might end up adding an attacker’s email address to their account, or transferring currency to them.

If you’re coming from a large framework in another language—you might have CSRF protection enabled by default. Since Go is a language and not a web framework, there’s a little legwork we’ll need to do to secure our own applications in the same manner. I contributed the gorilla/csrf package to the Gorilla Project (a collection of useful HTTP libs for Go), so we’ll use that.

Adding CSRF Protection

The example below provides a minimal (but practical) example of how to add CSRF protection to a Go web application:

package main

import (
    "net/http"

    // Don't forget to `go get github.com/gorilla/csrf`
    "github.com/gorilla/csrf"
    "github.com/gorilla/mux"
)

func main() {
    r := mux.NewRouter()
    r.HandleFunc("/signup", ShowSignupForm)
    // All POST requests without a valid token will return HTTP 403 Forbidden.
    r.HandleFunc("/signup/post", SubmitSignupForm)

    CSRF := csrf.Protect([]byte("32-byte-long-auth-key"))
    // PS: Don't forget to pass csrf.Secure(false) if you're developing locally
    // over plain HTTP (just don't leave it on in production).
    log.Fatal(http.ListenAndServe(":8000", CSRF(r)))
}

func ShowSignupForm(w http.ResponseWriter, r *http.Request) {
    // signup_form.tmpl just needs a  template tag for
    // csrf.TemplateField to inject the CSRF token into. Easy!
    t.ExecuteTemplate(w, "signup_form.tmpl", map[string]interface{
        csrf.TemplateTag: csrf.TemplateField(r),
    })
}

func SubmitSignupForm(w http.ResponseWriter, r *http.Request) {
    // We can trust that requests making it this far have satisfied
    // our CSRF protection requirements.
}

With the above we get:

  • Automatic CSRF protection on all non-idempotent requests (effectively anything that’s not a GET, HEAD, OPTIONS or TRACE)
  • A token available in the request context for us to inject into our responses
  • A useful template helper via csrf.TemplateField that replaces a {{.csrfField}} template tag with a hidden input field containing the CSRF token for you

JavaScript Clients

Alternatively, if your Go application is the backend for a React, Ember or other client-side JavaScript application, you can render the token in a <meta> tag in the head of your index.html template (i.e. the entry point of your JS application) provided your Go application is rendering it. Your JavaScript client can then get the token from this tag and return it via the X-CSRF-Token header when making AJAX requests.

Here’s a quick demo, with the HTML template representing your index.html template first:

<head>
     ...
     <meta name=“gorilla.csrf.Token” content=“{{.csrfToken}}”>
     ...
</head>

… and in your JS code:

// Using the the https://github.com/github/fetch polyfill
fetch(/auth/login', {
  method: 'post',
  headers: {
     // Vanilla, unadorned JavaScript
     X-CSRF-Token’: document.getElementsByTagName(meta)[gorilla.csrf.Token].getAttribute(content)
  },
  body: new FormData(loginForm)
})

If you’re using jQuery, an AJAX prefilter is well-suited to this task—pass the header name & token to xhr.setRequestHeader inside your prefilter to automatically add the CSRF token header to every request.

How Does It Work?

The CSRF prevention approach used here is kept simple, and uses the proven double-submitted cookie method. This is similar to the approach used by Django and Rails, and relies on comparing the cookie value with the submitted form value (or HTTP header value, in the case of AJAX).

gorilla/csrf also attempts to mitigate the BREACH attack (in short: detecting secrets through HTTP compression) by randomizing the CSRF token in the response. This is done by XOR’ing the CSRF token against a randomly generated nonce (via Go’s crypto/rand) and creating a ‘masked’ token that effectively changes on every request. The nonce is then appended to the XOR output - e.g. $maskedtoken$nonce - and used to XOR the masked token (reversing it) on the next request. Since underlying token used for comparison (stored in a signed cookie) doesn’t change, which means that this approach doesn’t break user experience across multiple tabs.

An XOR operation is used over a hash function or AES primarily for performance, but also because the mitigation is provided by making our secrets unpredictable across requests at large.

What Next?

I’ve pushed a slightly updated version of gorilla/csrf—see the branch here over on the goji/csrf repo that leverages net/context instead of gorilla/context.

context.Context is quickly becoming the de-facto way to pass request-scoped values in Go HTTP applications and is likely to be incorporated into net/http with Go 1.7, but there’s little stopping you from adopting it now.



© 2017 Matt Silverlock | His other site | Code snippets are MIT licensed | Built with Jekyll