oauth2: allow callers to pass arbitrary auth URL parameters

Many OAuth 2.0 implementations support parameters beyond those supported
by this library. This change exports a SetParam function for
constructing arbitrary key/value parameters.

Change-Id: Ice4179e7c5341bbeac8a53e389b32d59415740fa
Reviewed-on: https://go-review.googlesource.com/8054
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/oauth2.go b/oauth2.go
index dae0066..0c6a1ed 100644
--- a/oauth2.go
+++ b/oauth2.go
@@ -79,22 +79,28 @@
 	// result in your application obtaining a refresh token the
 	// first time your application exchanges an authorization
 	// code for a user.
-	AccessTypeOnline  AuthCodeOption = setParam{"access_type", "online"}
-	AccessTypeOffline AuthCodeOption = setParam{"access_type", "offline"}
+	AccessTypeOnline  AuthCodeOption = SetParam("access_type", "online")
+	AccessTypeOffline AuthCodeOption = SetParam("access_type", "offline")
 
 	// ApprovalForce forces the users to view the consent dialog
 	// and confirm the permissions request at the URL returned
 	// from AuthCodeURL, even if they've already done so.
-	ApprovalForce AuthCodeOption = setParam{"approval_prompt", "force"}
+	ApprovalForce AuthCodeOption = SetParam("approval_prompt", "force")
 )
 
+// An AuthCodeOption is passed to Config.AuthCodeURL.
+type AuthCodeOption interface {
+	setValue(url.Values)
+}
+
 type setParam struct{ k, v string }
 
 func (p setParam) setValue(m url.Values) { m.Set(p.k, p.v) }
 
-// An AuthCodeOption is passed to Config.AuthCodeURL.
-type AuthCodeOption interface {
-	setValue(url.Values)
+// SetParam builds an AuthCodeOption which passes key/value parameters
+// to a provider's authorization endpoint.
+func SetParam(key, value string) AuthCodeOption {
+	return setParam{key, value}
 }
 
 // AuthCodeURL returns a URL to OAuth 2.0 provider's consent page
diff --git a/oauth2_test.go b/oauth2_test.go
index df40ee9..b3696a1 100644
--- a/oauth2_test.go
+++ b/oauth2_test.go
@@ -61,6 +61,15 @@
 	}
 }
 
+func TestAuthCodeURL_CustomParam(t *testing.T) {
+	conf := newConf("server")
+	param := SetParam("foo", "bar")
+	url := conf.AuthCodeURL("baz", param)
+	if url != "server/auth?client_id=CLIENT_ID&foo=bar&redirect_uri=REDIRECT_URL&response_type=code&scope=scope1+scope2&state=baz" {
+		t.Errorf("Auth code URL doesn't match the expected, found: %v", url)
+	}
+}
+
 func TestAuthCodeURL_Optional(t *testing.T) {
 	conf := &Config{
 		ClientID: "CLIENT_ID",