Add client side auth-proxy Role support.

Bug: b/249507110
Change-Id: I317bee5f3a4f9a04e2151315aefc68ada4d45b52
Reviewed-on: https://skia-review.googlesource.com/c/buildbot/+/586576
Auto-Submit: Joe Gregorio <jcgregorio@google.com>
Commit-Queue: Joe Gregorio <jcgregorio@google.com>
Reviewed-by: Ravi Mistry <rmistry@google.com>
diff --git a/go/alogin/BUILD.bazel b/go/alogin/BUILD.bazel
index 68b303a..b265339 100644
--- a/go/alogin/BUILD.bazel
+++ b/go/alogin/BUILD.bazel
@@ -5,5 +5,5 @@
     srcs = ["login.go"],
     importpath = "go.skia.org/infra/go/alogin",
     visibility = ["//visibility:public"],
-    deps = ["@com_github_gorilla_mux//:mux"],
+    deps = ["//go/roles"],
 )
diff --git a/go/alogin/login.go b/go/alogin/login.go
index 2e6f5ed..5c157bb 100644
--- a/go/alogin/login.go
+++ b/go/alogin/login.go
@@ -8,10 +8,10 @@
 import (
 	"net/http"
 
-	"github.com/gorilla/mux"
+	"go.skia.org/infra/go/roles"
 )
 
-// Email is an email address.
+// EMail is an email address.
 type EMail string
 
 // String returns the email address as a string.
@@ -34,6 +34,9 @@
 	// EMail is the email address of the logged in user, or the empty string if
 	// they are not logged in.
 	EMail EMail `json:"email"`
+
+	// All the Roles of the current user.
+	Roles roles.Roles `json:"roles"`
 }
 
 // Login is an abstraction of the functionality we use out of the go/login
@@ -47,13 +50,17 @@
 	// attempt to use a resource that requires authentication, such as
 	// redirecting them to a login URL or returning an http.StatusForbidden
 	// response code.
+	//
+	// TODO(jcgregorio) Can be removed once Perf migrates fully to auth-proxy.
 	NeedsAuthentication(w http.ResponseWriter, r *http.Request)
 
-	// RegisterHandlers registers HTTP handlers for any endpoints that need
-	// handling.
-	RegisterHandlers(router *mux.Router)
-
 	// Status returns the logged in status and other details about the current
 	// user.
 	Status(r *http.Request) Status
+
+	// All the authorized Roles for a user.
+	Roles(r *http.Request) roles.Roles
+
+	// Returns true if the currently logged in user has the given Role.
+	HasRole(r *http.Request, role roles.Role) bool
 }
diff --git a/go/alogin/mocks/BUILD.bazel b/go/alogin/mocks/BUILD.bazel
index 46a8ce7..dca2f3f 100644
--- a/go/alogin/mocks/BUILD.bazel
+++ b/go/alogin/mocks/BUILD.bazel
@@ -10,7 +10,7 @@
     visibility = ["//visibility:public"],
     deps = [
         "//go/alogin",
-        "@com_github_gorilla_mux//:mux",
+        "//go/roles",
         "@com_github_stretchr_testify//mock",
     ],
 )
diff --git a/go/alogin/mocks/Login.go b/go/alogin/mocks/Login.go
index 669d230..5bc10d7 100644
--- a/go/alogin/mocks/Login.go
+++ b/go/alogin/mocks/Login.go
@@ -9,7 +9,7 @@
 
 	mock "github.com/stretchr/testify/mock"
 
-	mux "github.com/gorilla/mux"
+	roles "go.skia.org/infra/go/roles"
 
 	testing "testing"
 )
@@ -19,6 +19,20 @@
 	mock.Mock
 }
 
+// HasRole provides a mock function with given fields: r, role
+func (_m *Login) HasRole(r *http.Request, role roles.Role) bool {
+	ret := _m.Called(r, role)
+
+	var r0 bool
+	if rf, ok := ret.Get(0).(func(*http.Request, roles.Role) bool); ok {
+		r0 = rf(r, role)
+	} else {
+		r0 = ret.Get(0).(bool)
+	}
+
+	return r0
+}
+
 // LoggedInAs provides a mock function with given fields: r
 func (_m *Login) LoggedInAs(r *http.Request) alogin.EMail {
 	ret := _m.Called(r)
@@ -38,9 +52,20 @@
 	_m.Called(w, r)
 }
 
-// RegisterHandlers provides a mock function with given fields: router
-func (_m *Login) RegisterHandlers(router *mux.Router) {
-	_m.Called(router)
+// Roles provides a mock function with given fields: r
+func (_m *Login) Roles(r *http.Request) roles.Roles {
+	ret := _m.Called(r)
+
+	var r0 roles.Roles
+	if rf, ok := ret.Get(0).(func(*http.Request) roles.Roles); ok {
+		r0 = rf(r)
+	} else {
+		if ret.Get(0) != nil {
+			r0 = ret.Get(0).(roles.Roles)
+		}
+	}
+
+	return r0
 }
 
 // Status provides a mock function with given fields: r
diff --git a/go/alogin/proxylogin/BUILD.bazel b/go/alogin/proxylogin/BUILD.bazel
index c001084..3ed040e 100644
--- a/go/alogin/proxylogin/BUILD.bazel
+++ b/go/alogin/proxylogin/BUILD.bazel
@@ -8,9 +8,10 @@
     visibility = ["//visibility:public"],
     deps = [
         "//go/alogin",
+        "//go/roles",
         "//go/skerr",
         "//go/sklog",
-        "@com_github_gorilla_mux//:mux",
+        "//kube/go/authproxy",
     ],
 )
 
@@ -20,6 +21,8 @@
     embed = [":proxylogin"],
     deps = [
         "//go/alogin",
+        "//go/roles",
+        "//kube/go/authproxy",
         "@com_github_stretchr_testify//require",
     ],
 )
diff --git a/go/alogin/proxylogin/proxyauth.go b/go/alogin/proxylogin/proxyauth.go
index d236bf0..fbfd63f 100644
--- a/go/alogin/proxylogin/proxyauth.go
+++ b/go/alogin/proxylogin/proxyauth.go
@@ -7,10 +7,11 @@
 	"regexp"
 	"strings"
 
-	"github.com/gorilla/mux"
 	"go.skia.org/infra/go/alogin"
+	"go.skia.org/infra/go/roles"
 	"go.skia.org/infra/go/skerr"
 	"go.skia.org/infra/go/sklog"
+	"go.skia.org/infra/kube/go/authproxy"
 )
 
 // proxyLogin implements alogin.Login by relying on a reverse proxy doing the
@@ -82,11 +83,6 @@
 	http.Error(w, "Forbidden", http.StatusForbidden)
 }
 
-// RegisterHandlers implements alogin.Login.
-func (p *proxyLogin) RegisterHandlers(router *mux.Router) {
-	// Noop.
-}
-
 func (p *proxyLogin) Status(r *http.Request) alogin.Status {
 	return alogin.Status{
 		EMail:     p.LoggedInAs(r),
@@ -95,5 +91,20 @@
 	}
 }
 
+// All the authorized Roles for a user.
+func (p *proxyLogin) Roles(r *http.Request) roles.Roles {
+	return roles.FromHeader(r.Header.Get(authproxy.WebAuthRoleHeaderName))
+}
+
+// Returns true if the currently logged in user has the given Role.
+func (p *proxyLogin) HasRole(r *http.Request, wantedRole roles.Role) bool {
+	for _, role := range p.Roles(r) {
+		if role == wantedRole {
+			return true
+		}
+	}
+	return false
+}
+
 // Assert proxyLogin implements alogin.Login.
 var _ alogin.Login = (*proxyLogin)(nil)
diff --git a/go/alogin/proxylogin/proxyauth_test.go b/go/alogin/proxylogin/proxyauth_test.go
index 9b9a3b0..6659159 100644
--- a/go/alogin/proxylogin/proxyauth_test.go
+++ b/go/alogin/proxylogin/proxyauth_test.go
@@ -7,6 +7,8 @@
 
 	"github.com/stretchr/testify/require"
 	"go.skia.org/infra/go/alogin"
+	"go.skia.org/infra/go/roles"
+	"go.skia.org/infra/kube/go/authproxy"
 )
 
 const (
@@ -19,7 +21,6 @@
 )
 
 func TestLoggedInAs_HeaderIsMissing_ReturnsEmptyString(t *testing.T) {
-
 	r := httptest.NewRequest("GET", "/", nil)
 	login, err := New(unknownHeaderName, "", loginURL, logoutURL)
 	require.NoError(t, err)
@@ -27,7 +28,6 @@
 }
 
 func TestLoggedInAs_HeaderPresent_ReturnsUserEmail(t *testing.T) {
-
 	r := httptest.NewRequest("GET", "/", nil)
 	r.Header.Set(goodHeaderName, emailAsString)
 	login, err := New(goodHeaderName, "", loginURL, logoutURL)
@@ -36,7 +36,6 @@
 }
 
 func TestLoggedInAs_RegexProvided_ReturnsUserEmail(t *testing.T) {
-
 	r := httptest.NewRequest("GET", "/", nil)
 	r.Header.Set(goodHeaderName, "accounts.google.com:"+emailAsString)
 	login, err := New(goodHeaderName, "accounts.google.com:(.*)", loginURL, logoutURL)
@@ -45,7 +44,6 @@
 }
 
 func TestLoggedInAs_RegexHasTooManySubGroups_ReturnsEmptyString(t *testing.T) {
-
 	r := httptest.NewRequest("GET", "/", nil)
 	r.Header.Set(goodHeaderName, emailAsString)
 	login, err := New(goodHeaderName, "(too)(many)(subgroups)", loginURL, logoutURL)
@@ -54,7 +52,6 @@
 }
 
 func TestNeedsAuthentication_EmitsStatusForbidden(t *testing.T) {
-
 	w := httptest.NewRecorder()
 	r := httptest.NewRequest("GET", "/", nil)
 	login, err := New(goodHeaderName, "", loginURL, logoutURL)
@@ -64,7 +61,6 @@
 }
 
 func TestStatus_HeaderPresent_ReturnsUserEmail(t *testing.T) {
-
 	r := httptest.NewRequest("GET", "/", nil)
 	r.Header.Set(goodHeaderName, emailAsString)
 	expected := alogin.Status{
@@ -78,7 +74,36 @@
 }
 
 func TestNew_InvalidRegex_ReturnsError(t *testing.T) {
-
 	_, err := New(goodHeaderName, "\\y", loginURL, logoutURL)
 	require.Error(t, err)
 }
+
+func TestRoles_HeaderPresent_ReturnAllRoles(t *testing.T) {
+	r := httptest.NewRequest("GET", "/", nil)
+	r.Header.Set(authproxy.WebAuthRoleHeaderName, roles.AllValidRoles.ToHeader())
+	login, err := New(goodHeaderName, "", loginURL, logoutURL)
+	require.NoError(t, err)
+	require.Equal(t, roles.AllValidRoles, login.Roles(r))
+}
+
+func TestRoles_HeaderMissing_ReturnsEmptyListOfRoles(t *testing.T) {
+	r := httptest.NewRequest("GET", "/", nil)
+	login, err := New(goodHeaderName, "", loginURL, logoutURL)
+	require.NoError(t, err)
+	require.Empty(t, login.Roles(r))
+}
+
+func TestHasRoles_HeaderPresent_ReturnsTrue(t *testing.T) {
+	r := httptest.NewRequest("GET", "/", nil)
+	r.Header.Set(authproxy.WebAuthRoleHeaderName, roles.AllValidRoles.ToHeader())
+	login, err := New(goodHeaderName, "", loginURL, logoutURL)
+	require.NoError(t, err)
+	require.True(t, login.HasRole(r, roles.Admin))
+}
+
+func TestHasRoles_HeaderMissingPresent_ReturnsFalse(t *testing.T) {
+	r := httptest.NewRequest("GET", "/", nil)
+	login, err := New(goodHeaderName, "", loginURL, logoutURL)
+	require.NoError(t, err)
+	require.False(t, login.HasRole(r, roles.Admin))
+}
diff --git a/go/alogin/sklogin/BUILD.bazel b/go/alogin/sklogin/BUILD.bazel
index 39e13ad..8691022 100644
--- a/go/alogin/sklogin/BUILD.bazel
+++ b/go/alogin/sklogin/BUILD.bazel
@@ -9,8 +9,8 @@
     deps = [
         "//go/alogin",
         "//go/login",
+        "//go/roles",
         "//go/skerr",
-        "@com_github_gorilla_mux//:mux",
     ],
 )
 
@@ -20,7 +20,6 @@
     embed = [":sklogin"],
     deps = [
         "//go/alogin",
-        "@com_github_gorilla_mux//:mux",
         "@com_github_stretchr_testify//require",
     ],
 )
diff --git a/go/alogin/sklogin/sklogin.go b/go/alogin/sklogin/sklogin.go
index b6cda2e..8769685 100644
--- a/go/alogin/sklogin/sklogin.go
+++ b/go/alogin/sklogin/sklogin.go
@@ -5,9 +5,9 @@
 	"fmt"
 	"net/http"
 
-	"github.com/gorilla/mux"
 	"go.skia.org/infra/go/alogin"
 	"go.skia.org/infra/go/login"
+	"go.skia.org/infra/go/roles"
 	"go.skia.org/infra/go/skerr"
 )
 
@@ -45,14 +45,6 @@
 	http.Redirect(w, r, login.LoginURL(w, r), http.StatusTemporaryRedirect)
 }
 
-// RegisterHandlers implements alogin.Login.
-func (_ *sklogin) RegisterHandlers(router *mux.Router) {
-	router.HandleFunc(loginPath, login.LoginHandler)
-	router.HandleFunc(logoutPath, login.LogoutHandler)
-	router.HandleFunc("/loginstatus/", login.StatusHandler)
-	router.HandleFunc("/oauth2callback/", login.OAuth2CallbackHandler)
-}
-
 func (s *sklogin) Status(r *http.Request) alogin.Status {
 	return alogin.Status{
 		EMail:     s.LoggedInAs(r),
@@ -61,5 +53,15 @@
 	}
 }
 
+// All the authorized Roles for a user.
+func (s *sklogin) Roles(r *http.Request) roles.Roles {
+	panic("sklogin does not support Roles.")
+}
+
+// Returns true if the currently logged in user has the given Role.
+func (s *sklogin) HasRole(r *http.Request, role roles.Role) bool {
+	panic("sklogin does not support Roles.")
+}
+
 // Assert sklogin implements alogin.Login.
 var _ alogin.Login = (*sklogin)(nil)
diff --git a/go/alogin/sklogin/sklogin_test.go b/go/alogin/sklogin/sklogin_test.go
index a9b385d..e51bb9f 100644
--- a/go/alogin/sklogin/sklogin_test.go
+++ b/go/alogin/sklogin/sklogin_test.go
@@ -4,22 +4,11 @@
 	"net/http/httptest"
 	"testing"
 
-	"github.com/gorilla/mux"
 	"github.com/stretchr/testify/require"
 	"go.skia.org/infra/go/alogin"
 )
 
-func TestRegisterHandlers_HandlersAreRegistered(t *testing.T) {
-
-	router := mux.NewRouter()
-	(&sklogin{}).RegisterHandlers(router)
-	var out mux.RouteMatch
-	r := httptest.NewRequest("GET", "/logout/", nil)
-	require.True(t, router.Match(r, &out))
-}
-
 func TestStatus_CookiesAreNotPresent_EMailIsNotReturnedInStatus(t *testing.T) {
-
 	r := httptest.NewRequest("GET", "/", nil)
 	expected := alogin.Status{
 		EMail:     alogin.NotLoggedIn,
diff --git a/infra-sk/go/ts/BUILD.bazel b/infra-sk/go/ts/BUILD.bazel
index 09a3fe1..8ef9d5e 100644
--- a/infra-sk/go/ts/BUILD.bazel
+++ b/infra-sk/go/ts/BUILD.bazel
@@ -7,6 +7,7 @@
     visibility = ["//visibility:private"],
     deps = [
         "//go/alogin",
+        "//go/roles",
         "//go/sklog",
         "//go/util",
         "@com_github_skia_dev_go2ts//:go2ts",
diff --git a/infra-sk/go/ts/main.go b/infra-sk/go/ts/main.go
index 255654d..fd84cd6 100644
--- a/infra-sk/go/ts/main.go
+++ b/infra-sk/go/ts/main.go
@@ -10,6 +10,7 @@
 
 	"github.com/skia-dev/go2ts"
 	"go.skia.org/infra/go/alogin"
+	"go.skia.org/infra/go/roles"
 	"go.skia.org/infra/go/sklog"
 	"go.skia.org/infra/go/util"
 )
@@ -19,7 +20,10 @@
 	flag.Parse()
 
 	generator := go2ts.New()
-	generator.AddToNamespace(alogin.Status{}, "alogin")
+	generator.Add(alogin.Status{})
+	generator.AddMultipleUnion(
+		roles.AllRoles,
+	)
 
 	err := util.WithWriteFile(*outputPath, func(w io.Writer) error {
 		return generator.Render(w)
diff --git a/infra-sk/modules/alogin-sk/alogin-sk-demo.ts b/infra-sk/modules/alogin-sk/alogin-sk-demo.ts
index 527ea62..2807c32 100644
--- a/infra-sk/modules/alogin-sk/alogin-sk-demo.ts
+++ b/infra-sk/modules/alogin-sk/alogin-sk-demo.ts
@@ -1,11 +1,12 @@
 // eslint-disable-next-line import/no-extraneous-dependencies
 import fetchMock from 'fetch-mock';
-import { alogin } from '../json';
+import { Status } from '../json';
 
-const response: alogin.Status = {
+const response: Status = {
   email: 'user@google.com',
   login: 'https://skia.org/login/',
   logout: 'https://skia.org/logout/',
+  roles: ['viewer'],
 };
 fetchMock.get('/_/login/status', response);
 
diff --git a/infra-sk/modules/alogin-sk/alogin-sk.ts b/infra-sk/modules/alogin-sk/alogin-sk.ts
index 4cb20d6..c29ab81 100644
--- a/infra-sk/modules/alogin-sk/alogin-sk.ts
+++ b/infra-sk/modules/alogin-sk/alogin-sk.ts
@@ -13,7 +13,7 @@
 import { errorMessage } from 'elements-sk/errorMessage';
 import { html } from 'lit-html';
 import { ElementSk } from '../ElementSk';
-import { alogin } from '../json';
+import { Status } from '../json';
 
 const defaultStatusURL = '/_/login/status';
 
@@ -21,7 +21,7 @@
  * Returns a Promise that resolves when we have received the login status, and
  * rejects if there was an error retrieving the login status.
  */
-const loggedIn = async (url: string = defaultStatusURL): Promise<alogin.Status> => {
+const loggedIn = async (url: string = defaultStatusURL): Promise<Status> => {
   const resp = await fetch(url);
   if (!resp.ok) {
     await errorMessage(`Failed to load login status: ${resp.statusText}`);
@@ -30,16 +30,18 @@
   return resp.json();
 };
 
-const defaultStatus: alogin.Status = {
+const defaultStatus: Status = {
   email: '',
   login: '',
   logout: '',
+  roles: [],
 };
 
-const fakeStatus: alogin.Status = {
+const fakeStatus: Status = {
   email: 'test@example.com',
   login: '/login/',
   logout: '/logout/',
+  roles: ['viewer'],
 };
 
 export class AloginSk extends ElementSk {
@@ -48,9 +50,9 @@
    *
    * The value of this may be altered by the 'testing_offline' attribute.
    */
-  statusPromise: Promise<alogin.Status> = Promise.resolve(defaultStatus);
+  statusPromise: Promise<Status> = Promise.resolve(defaultStatus);
 
-  private status: alogin.Status = defaultStatus;
+  private status: Status = defaultStatus;
 
   constructor() {
     super(AloginSk.template);
diff --git a/infra-sk/modules/json/index.ts b/infra-sk/modules/json/index.ts
index 9dc3022..738ed8e 100644
--- a/infra-sk/modules/json/index.ts
+++ b/infra-sk/modules/json/index.ts
@@ -1,11 +1,14 @@
 // DO NOT EDIT. This file is automatically generated.
 
-export namespace alogin {
-	export interface Status {
-		login: string;
-		logout: string;
-		email: alogin.EMail;
-	}
+export interface Status {
+	login: string;
+	logout: string;
+	email: EMail;
+	roles: Roles;
 }
 
-export namespace alogin { export type EMail = string; }
+export type EMail = string;
+
+export type Role = "viewer" | "editor" | "admin" | "";
+
+export type Roles = Role[] | null;
diff --git a/kube/go/authproxy/auth/impl.go b/kube/go/authproxy/auth/impl.go
index 82aa4a9..c192cd2 100644
--- a/kube/go/authproxy/auth/impl.go
+++ b/kube/go/authproxy/auth/impl.go
@@ -36,7 +36,10 @@
 		redirectURL = login.DEFAULT_REDIRECT_URL
 	}
 
-	return login.Init(redirectURL, login.DEFAULT_ALLOWED_DOMAINS, "")
+	return login.Init(redirectURL,
+		"", /* Empty means accept all signed in domain. */
+		"", /* Get secrets from Secret Manager*/
+	)
 }
 
 // Confirm authImpl implements Auth.
diff --git a/kube/go/authproxy/authproxy.go b/kube/go/authproxy/authproxy.go
index 8c2b987..23472f6 100644
--- a/kube/go/authproxy/authproxy.go
+++ b/kube/go/authproxy/authproxy.go
@@ -56,9 +56,9 @@
 	// of authentication from the core of the app. See
 	// https://grafana.com/blog/2015/12/07/grafana-authproxy-have-it-your-way/ for
 	// how Grafana uses this to support almost any authentication handler.
-	webAuthHeaderName = "X-WEBAUTH-USER"
+	WebAuthHeaderName = "X-WEBAUTH-USER"
 
-	webAuthRoleHeaderName = "X-ROLES"
+	WebAuthRoleHeaderName = "X-ROLES"
 )
 
 type proxy struct {
@@ -81,8 +81,8 @@
 
 func (p proxy) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 	email := p.authProvider.LoggedInAs(r)
-	r.Header.Del(webAuthHeaderName)
-	r.Header.Add(webAuthHeaderName, email)
+	r.Header.Del(WebAuthHeaderName)
+	r.Header.Add(WebAuthHeaderName, email)
 
 	authorizedRoles := roles.Roles{}
 	for role, allowed := range p.allowedRoles {
@@ -91,8 +91,8 @@
 		}
 	}
 
-	r.Header.Del(webAuthRoleHeaderName)
-	r.Header.Add(webAuthRoleHeaderName, authorizedRoles.ToHeader())
+	r.Header.Del(WebAuthRoleHeaderName)
+	r.Header.Add(WebAuthRoleHeaderName, authorizedRoles.ToHeader())
 
 	if r.Method == "POST" && p.allowPost {
 		p.reverseProxy.ServeHTTP(w, r)
diff --git a/kube/go/authproxy/authproxy_test.go b/kube/go/authproxy/authproxy_test.go
index 06704a4..1d61f13 100644
--- a/kube/go/authproxy/authproxy_test.go
+++ b/kube/go/authproxy/authproxy_test.go
@@ -32,8 +32,8 @@
 
 func assertValidEmailAndRole(t *testing.T) http.HandlerFunc {
 	return func(w http.ResponseWriter, r *http.Request) {
-		require.Equal(t, []string{viewerEmail}, r.Header.Values(webAuthHeaderName))
-		require.Equal(t, []string{string(roles.Viewer)}, r.Header.Values(webAuthRoleHeaderName))
+		require.Equal(t, []string{viewerEmail}, r.Header.Values(WebAuthHeaderName))
+		require.Equal(t, []string{string(roles.Viewer)}, r.Header.Values(WebAuthRoleHeaderName))
 	}
 }
 
@@ -57,7 +57,7 @@
 func TestProxyServeHTTP_AllowPostAndNotAuthenticated_WebAuthHeaderValueIsEmptyString(t *testing.T) {
 	u, called, w, r := setupForTest(t, func(w http.ResponseWriter, r *http.Request) {
 		// Note that if the header webAuthHeaderName hadn't been set then the value would be nil.
-		require.Equal(t, []string{""}, r.Header.Values(webAuthHeaderName))
+		require.Equal(t, []string{""}, r.Header.Values(WebAuthHeaderName))
 		require.Equal(t, []string(nil), r.Header.Values("X-SOME-UNSET-HEADER"))
 	})
 	authMock := mocks.NewAuth(t)
@@ -83,8 +83,8 @@
 
 func TestProxyServeHTTP_UserIsLoggedInAndBelongsToTwoRoles_HeaderWithBothRolesIsIncludedInRequest(t *testing.T) {
 	u, called, w, r := setupForTest(t, func(w http.ResponseWriter, r *http.Request) {
-		require.Equal(t, []string{viewerEmail}, r.Header.Values(webAuthHeaderName))
-		receivedRoles := strings.Split(r.Header.Get(webAuthRoleHeaderName), ",")
+		require.Equal(t, []string{viewerEmail}, r.Header.Values(WebAuthHeaderName))
+		receivedRoles := strings.Split(r.Header.Get(WebAuthRoleHeaderName), ",")
 		sort.Strings(receivedRoles)
 		expectedRoles := []string{
 			string(roles.Editor),
@@ -108,7 +108,7 @@
 
 func TestProxyServeHTTP_UserIsNotLoggedIn_HeaderWithUserEmailIsStrippedFromRequest(t *testing.T) {
 	u, called, w, r := setupForTest(t, func(w http.ResponseWriter, r *http.Request) {})
-	r.Header.Add(webAuthHeaderName, viewerEmail) // Try to spoof the header.
+	r.Header.Add(WebAuthHeaderName, viewerEmail) // Try to spoof the header.
 	authMock := mocks.NewAuth(t)
 	authMock.On("LoggedInAs", r).Return("")
 	authMock.On("LoginURL", w, r).Return("http://example.org/login")
@@ -133,7 +133,7 @@
 
 func TestProxyServeHTTP_UserIsLoggedIn_HeaderWithUserEmailIsIncludedInRequestAndSpoofedEmailIsRemoved(t *testing.T) {
 	u, called, w, r := setupForTest(t, assertValidEmailAndRole(t))
-	r.Header.Add(webAuthHeaderName, "haxor@example.org") // Try to spoof the header.
+	r.Header.Add(WebAuthHeaderName, "haxor@example.org") // Try to spoof the header.
 	authMock := mocks.NewAuth(t)
 	authMock.On("LoggedInAs", r).Return(viewerEmail)
 
@@ -145,11 +145,11 @@
 
 func TestProxyServeHTTP_UserIsNotLoggedInAndPassiveFlagIsSet_RequestIsPassedAlongWithoutEmailHeader(t *testing.T) {
 	u, called, w, r := setupForTest(t, func(w http.ResponseWriter, r *http.Request) {
-		require.Equal(t, []string{""}, r.Header.Values(webAuthHeaderName))
-		require.Equal(t, []string{""}, r.Header.Values(webAuthRoleHeaderName))
+		require.Equal(t, []string{""}, r.Header.Values(WebAuthHeaderName))
+		require.Equal(t, []string{""}, r.Header.Values(WebAuthRoleHeaderName))
 	})
 
-	r.Header.Add(webAuthHeaderName, "haxor@example.org") // Try to spoof the header.
+	r.Header.Add(WebAuthHeaderName, "haxor@example.org") // Try to spoof the header.
 	authMock := mocks.NewAuth(t)
 	authMock.On("LoggedInAs", r).Return("")
 
@@ -162,7 +162,7 @@
 func TestProxyServeHTTP_UserIsLoggedInAndPassiveFlagIsSet_RequestIsPassedAlongWithEmailHeader(t *testing.T) {
 	u, called, w, r := setupForTest(t, assertValidEmailAndRole(t))
 
-	r.Header.Add(webAuthHeaderName, "haxor@example.org") // Try to spoof the header.
+	r.Header.Add(WebAuthHeaderName, "haxor@example.org") // Try to spoof the header.
 	authMock := mocks.NewAuth(t)
 	authMock.On("LoggedInAs", r).Return(viewerEmail)
 
diff --git a/perf/go/frontend/frontend.go b/perf/go/frontend/frontend.go
index 39ed4ba..b25a005 100644
--- a/perf/go/frontend/frontend.go
+++ b/perf/go/frontend/frontend.go
@@ -1476,7 +1476,6 @@
 	router.HandleFunc("/r/", f.templateHandler("trybot.html"))
 	router.HandleFunc("/g/{dest:[ect]}/{hash:[a-zA-Z0-9]+}", f.gotoHandler)
 	router.HandleFunc("/help/", f.helpHandler)
-	f.loginProvider.RegisterHandlers(router)
 
 	// JSON handlers.