oauth2: Token.Extra should be type agnostic

Change-Id: Ic8496d10de47181f6b9c16360b5101c6314d71ee
Reviewed-on: https://go-review.googlesource.com/1653
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
diff --git a/token.go b/token.go
index dc88b3d..e04a2dd 100644
--- a/token.go
+++ b/token.go
@@ -70,18 +70,18 @@
 	return t2
 }
 
-// Extra returns an extra field returned from the server during token
-// retrieval.
-func (t *Token) Extra(key string) string {
+// Extra returns an extra field.
+// Extra fields are key-value pairs returned by the server as a
+// part of the token retrieval response.
+func (t *Token) Extra(key string) interface{} {
 	if vals, ok := t.raw.(url.Values); ok {
+		// TODO(jbd): Cast numeric values to int64 or float64.
 		return vals.Get(key)
 	}
 	if raw, ok := t.raw.(map[string]interface{}); ok {
-		if val, ok := raw[key].(string); ok {
-			return val
-		}
+		return raw[key]
 	}
-	return ""
+	return nil
 }
 
 // expired reports whether the token is expired.
diff --git a/token_test.go b/token_test.go
new file mode 100644
index 0000000..74d6366
--- /dev/null
+++ b/token_test.go
@@ -0,0 +1,30 @@
+// Copyright 2014 The oauth2 Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style
+// license that can be found in the LICENSE file.
+
+package oauth2
+
+import "testing"
+
+func TestTokenExtra(t *testing.T) {
+	type testCase struct {
+		key  string
+		val  interface{}
+		want interface{}
+	}
+	const key = "extra-key"
+	cases := []testCase{
+		{key: key, val: "abc", want: "abc"},
+		{key: key, val: 123, want: 123},
+		{key: key, val: "", want: ""},
+		{key: "other-key", val: "def", want: nil},
+	}
+	for _, tc := range cases {
+		extra := make(map[string]interface{})
+		extra[tc.key] = tc.val
+		tok := &Token{raw: extra}
+		if got, want := tok.Extra(key), tc.want; got != want {
+			t.Errorf("Extra(%q) = %q; want %q", key, got, want)
+		}
+	}
+}