auth

Paddy 2014-11-11 Parent:41d8f972720c

69:42bc3e44f4fe Go to Latest

auth/http_test.go

Stub out sessions. Stop using the Login type when getting profile by Login, removing Logins, or recording Login use. The Login value has to be unique, anyways, and we don't actually know the Login type when getting a profile by Login. That's sort of the point. Create the concept of Sessions and a sessionStore type to manage our authentication sessions with the server. As per OWASP, we're basically just going to use a transparent, SHA256-generated random string as an ID, and store it client-side and server-side and just pass it back and forth. Add the ProfileID to the Grant type, because we need to remember who granted access. That's sort of important. Set a defaultGrantExpiration constant to an hour, so we have that one constant when creating new Grants. Create a helper that pulls the session ID out of an auth cookie, checks it against the sessionStore, and returns the Session if it's valid. Create a helper that pulls the username and password out of a basic auth header. Create a helper that authenticates a user's login and passphrase, checking them against the profileStore securely. Stub out how the cookie checking is going to work for getting grant approval. Fix the stored Grant RedirectURI to be the passed in redirect URI, not the RedirectURI that we ultimately redirect to. This is in accordance with the spec. Store the profile ID from our session in the created Grant. Stub out a GetTokenHandler that will allow users to exchange a Grant for a Token. Set a constant for the current passphrase scheme, which we will increment for each revision to the passphrase scheme, for backwards compatibility. Change the Profile iterations property to an int, not an int64, to match the code.secondbit.org/pass library (which is matching the PBKDF2 library).

History
paddy@52 1 package auth
paddy@52 2
paddy@52 3 import (
paddy@66 4 "bytes"
paddy@52 5 "html/template"
paddy@66 6 "io/ioutil"
paddy@52 7 "net/http"
paddy@52 8 "net/http/httptest"
paddy@53 9 "net/url"
paddy@52 10 "testing"
paddy@56 11 "time"
paddy@56 12
paddy@56 13 "code.secondbit.org/uuid"
paddy@52 14 )
paddy@52 15
paddy@53 16 const (
paddy@53 17 scopeSet = 1 << iota
paddy@53 18 stateSet
paddy@53 19 uriSet
paddy@53 20 )
paddy@53 21
paddy@65 22 func stripParam(param string, u *url.URL) {
paddy@65 23 q := u.Query()
paddy@65 24 q.Del(param)
paddy@65 25 u.RawQuery = q.Encode()
paddy@65 26 }
paddy@65 27
paddy@52 28 func TestGetGrantCodeSuccess(t *testing.T) {
paddy@52 29 t.Parallel()
paddy@52 30 store := NewMemstore()
paddy@52 31 testContext := Context{
paddy@52 32 template: template.Must(template.New(getGrantTemplateName).Parse("Get auth grant")),
paddy@52 33 clients: store,
paddy@52 34 grants: store,
paddy@52 35 profiles: store,
paddy@52 36 tokens: store,
paddy@52 37 }
paddy@56 38 client := Client{
paddy@56 39 ID: uuid.NewID(),
paddy@56 40 Secret: "super secret!",
paddy@56 41 OwnerID: uuid.NewID(),
paddy@56 42 Name: "My test client",
paddy@56 43 Logo: "https://secondbit.org/logo.png",
paddy@56 44 Website: "https://secondbit.org",
paddy@56 45 Type: "public",
paddy@56 46 }
paddy@56 47 uri, err := url.Parse("https://test.secondbit.org/redirect")
paddy@56 48 if err != nil {
paddy@56 49 t.Fatal("Can't parse URL:", err)
paddy@56 50 }
paddy@56 51 endpoint := Endpoint{
paddy@56 52 ID: uuid.NewID(),
paddy@56 53 ClientID: client.ID,
paddy@56 54 URI: *uri,
paddy@56 55 Added: time.Now(),
paddy@56 56 }
paddy@56 57 err = testContext.SaveClient(client)
paddy@56 58 if err != nil {
paddy@56 59 t.Fatal("Can't store client:", err)
paddy@56 60 }
paddy@56 61 err = testContext.AddEndpoint(client.ID, endpoint)
paddy@56 62 if err != nil {
paddy@56 63 t.Fatal("Can't store endpoint:", err)
paddy@56 64 }
paddy@52 65 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@52 66 if err != nil {
paddy@52 67 t.Fatal("Can't build request:", err)
paddy@52 68 }
paddy@58 69 for i := 0; i < 1<<3; i++ {
paddy@53 70 w := httptest.NewRecorder()
paddy@53 71 params := url.Values{}
paddy@53 72 // see OAuth 2.0 spec, section 4.1.1
paddy@53 73 params.Set("response_type", "code")
paddy@56 74 params.Set("client_id", client.ID.String())
paddy@53 75 if i&uriSet != 0 {
paddy@58 76 params.Set("redirect_uri", endpoint.URI.String())
paddy@53 77 }
paddy@53 78 if i&scopeSet != 0 {
paddy@53 79 params.Set("scope", "testscope")
paddy@53 80 }
paddy@53 81 if i&stateSet != 0 {
paddy@53 82 params.Set("state", "my super secure state string")
paddy@53 83 }
paddy@53 84 req.URL.RawQuery = params.Encode()
paddy@66 85 req.Method = "GET"
paddy@66 86 req.Body = nil
paddy@66 87 req.Header.Del("Content-Type")
paddy@53 88 GetGrantHandler(w, req, testContext)
paddy@53 89 if w.Code != http.StatusOK {
paddy@53 90 t.Errorf("Expected status code to be %d, got %d for %s", http.StatusOK, w.Code, req.URL.String())
paddy@53 91 }
paddy@53 92 if w.Body.String() != "Get auth grant" {
paddy@53 93 t.Errorf("Expected body to be `%s`, got `%s` for %s", "Get auth grant", w.Body.String(), req.URL.String())
paddy@53 94 }
paddy@66 95 req.Method = "POST"
paddy@66 96 req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
paddy@66 97 w = httptest.NewRecorder()
paddy@66 98 data := url.Values{}
paddy@66 99 data.Set("grant", "approved")
paddy@66 100 body := bytes.NewBufferString(data.Encode())
paddy@66 101 req.Body = ioutil.NopCloser(body)
paddy@66 102 GetGrantHandler(w, req, testContext)
paddy@66 103 if w.Code != http.StatusFound {
paddy@66 104 t.Errorf("Expected status code to be %d, got %d for %s", http.StatusFound, w.Code, req.URL.String())
paddy@66 105 }
paddy@66 106 redirectedTo := w.Header().Get("Location")
paddy@66 107 red, err := url.Parse(redirectedTo)
paddy@66 108 if err != nil {
paddy@66 109 t.Fatalf(`Being redirected to a non-URL "%s" threw error "%s" for "%s"\n`, redirectedTo, err, req.URL.String())
paddy@66 110 }
paddy@66 111 t.Log("Redirected to", redirectedTo)
paddy@66 112 if red.Query().Get("code") == "" {
paddy@66 113 t.Fatalf(`Expected code param in redirect URL to be set, but it wasn't for %s`, req.URL.String())
paddy@66 114 }
paddy@67 115 if _, err := testContext.GetGrant(red.Query().Get("code")); err != nil {
paddy@67 116 t.Fatalf(`Unexpected error "%s: retrieving the grant "%s" supplied in the redirect URL for %s`, err, red.Query().Get("code"), req.URL.String())
paddy@66 117 }
paddy@66 118 err = testContext.DeleteGrant(red.Query().Get("code"))
paddy@66 119 if err != nil {
paddy@66 120 t.Log(`Unexpected error "%s" deleting grant "%s" for %s`, err, red.Query().Get("code"), req.URL.String())
paddy@66 121 }
paddy@66 122 stripParam("code", red)
paddy@66 123 if red.Query().Get("state") != params.Get("state") {
paddy@66 124 t.Errorf(`Expected state param in redirect URL to be "%s", got "%s" for %s`, params.Get("state"), red.Query().Get("state"), req.URL.String())
paddy@66 125 }
paddy@66 126 stripParam("state", red)
paddy@66 127 if red.String() != endpoint.URI.String() {
paddy@66 128 t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI.String(), red.String())
paddy@66 129 }
paddy@52 130 }
paddy@52 131 }
paddy@56 132
paddy@62 133 func TestGetGrantCodeInvalidClient(t *testing.T) {
paddy@62 134 t.Parallel()
paddy@62 135 store := NewMemstore()
paddy@62 136 testContext := Context{
paddy@62 137 template: template.Must(template.New(getGrantTemplateName).Parse("{{ .error }}")),
paddy@62 138 clients: store,
paddy@62 139 grants: store,
paddy@62 140 profiles: store,
paddy@62 141 tokens: store,
paddy@62 142 }
paddy@62 143 client := Client{
paddy@62 144 ID: uuid.NewID(),
paddy@62 145 Secret: "super secret!",
paddy@62 146 OwnerID: uuid.NewID(),
paddy@62 147 Name: "My test client",
paddy@62 148 Type: "public",
paddy@62 149 }
paddy@62 150 err := testContext.SaveClient(client)
paddy@62 151 if err != nil {
paddy@62 152 t.Fatal("Can't store client:", err)
paddy@62 153 }
paddy@62 154 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@62 155 if err != nil {
paddy@62 156 t.Fatal("Can't build request:", err)
paddy@62 157 }
paddy@62 158 w := httptest.NewRecorder()
paddy@62 159 params := url.Values{}
paddy@62 160 params.Set("response_type", "code")
paddy@62 161 params.Set("redirect_uri", "https://test.secondbit.org/")
paddy@62 162 req.URL.RawQuery = params.Encode()
paddy@62 163 GetGrantHandler(w, req, testContext)
paddy@62 164 if w.Code != http.StatusBadRequest {
paddy@62 165 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
paddy@62 166 }
paddy@62 167 if w.Body.String() != "Client ID must be specified in the request." {
paddy@62 168 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "Client ID must be specified in the request.", w.Body.String())
paddy@62 169 }
paddy@62 170 w = httptest.NewRecorder()
paddy@62 171 params.Set("client_id", "Not an ID")
paddy@62 172 req.URL.RawQuery = params.Encode()
paddy@62 173 GetGrantHandler(w, req, testContext)
paddy@62 174 if w.Code != http.StatusBadRequest {
paddy@62 175 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
paddy@62 176 }
paddy@62 177 if w.Body.String() != "client_id is not a valid Client ID." {
paddy@62 178 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "client_id is not a valid Client ID.", w.Body.String())
paddy@62 179 }
paddy@62 180 w = httptest.NewRecorder()
paddy@62 181 params.Set("client_id", uuid.NewID().String())
paddy@62 182 req.URL.RawQuery = params.Encode()
paddy@62 183 GetGrantHandler(w, req, testContext)
paddy@62 184 if w.Code != http.StatusBadRequest {
paddy@62 185 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
paddy@62 186 }
paddy@62 187 if w.Body.String() != "The specified Client couldn&rsquo;t be found." {
paddy@62 188 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The specified Client couldn&rsquo;t be found.", w.Body.String())
paddy@62 189 }
paddy@62 190 }
paddy@62 191
paddy@56 192 func TestGetGrantCodeInvalidURI(t *testing.T) {
paddy@56 193 t.Parallel()
paddy@56 194 store := NewMemstore()
paddy@56 195 testContext := Context{
paddy@56 196 template: template.Must(template.New(getGrantTemplateName).Parse("{{ .error }}")),
paddy@56 197 clients: store,
paddy@56 198 grants: store,
paddy@56 199 profiles: store,
paddy@56 200 tokens: store,
paddy@56 201 }
paddy@56 202 client := Client{
paddy@56 203 ID: uuid.NewID(),
paddy@56 204 Secret: "super secret!",
paddy@56 205 OwnerID: uuid.NewID(),
paddy@56 206 Name: "My test client",
paddy@56 207 Type: "public",
paddy@56 208 }
paddy@56 209 uri, err := url.Parse("https://test.secondbit.org/redirect")
paddy@56 210 if err != nil {
paddy@56 211 t.Fatal("Can't parse URL:", err)
paddy@56 212 }
paddy@56 213 err = testContext.SaveClient(client)
paddy@56 214 if err != nil {
paddy@56 215 t.Fatal("Can't store client:", err)
paddy@56 216 }
paddy@56 217 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@56 218 if err != nil {
paddy@56 219 t.Fatal("Can't build request:", err)
paddy@56 220 }
paddy@56 221 w := httptest.NewRecorder()
paddy@56 222 params := url.Values{}
paddy@56 223 params.Set("response_type", "code")
paddy@56 224 params.Set("client_id", client.ID.String())
paddy@64 225 req.URL.RawQuery = params.Encode()
paddy@64 226 GetGrantHandler(w, req, testContext)
paddy@64 227 if w.Code != http.StatusBadRequest {
paddy@64 228 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
paddy@64 229 }
paddy@64 230 if w.Body.String() != "The redirect_uri specified is not valid." {
paddy@64 231 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String())
paddy@64 232 }
paddy@64 233 endpoint := Endpoint{
paddy@64 234 ID: uuid.NewID(),
paddy@64 235 ClientID: client.ID,
paddy@64 236 URI: *uri,
paddy@64 237 Added: time.Now(),
paddy@64 238 }
paddy@64 239 err = testContext.AddEndpoint(client.ID, endpoint)
paddy@64 240 if err != nil {
paddy@64 241 t.Fatal("Can't store endpoint:", err)
paddy@64 242 }
paddy@64 243 w = httptest.NewRecorder()
paddy@56 244 params.Set("redirect_uri", "https://test.secondbit.org/wrong")
paddy@56 245 req.URL.RawQuery = params.Encode()
paddy@56 246 GetGrantHandler(w, req, testContext)
paddy@56 247 if w.Code != http.StatusBadRequest {
paddy@56 248 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
paddy@56 249 }
paddy@56 250 if w.Body.String() != "The redirect_uri specified is not valid." {
paddy@56 251 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String())
paddy@56 252 }
paddy@64 253 endpoint2 := Endpoint{
paddy@64 254 ID: uuid.NewID(),
paddy@64 255 ClientID: client.ID,
paddy@64 256 URI: *uri,
paddy@64 257 Added: time.Now(),
paddy@64 258 }
paddy@64 259 err = testContext.AddEndpoint(client.ID, endpoint2)
paddy@60 260 if err != nil {
paddy@64 261 t.Fatal("Can't store endpoint:", err)
paddy@60 262 }
paddy@60 263 w = httptest.NewRecorder()
paddy@64 264 params.Set("redirect_uri", "")
paddy@64 265 req.URL.RawQuery = params.Encode()
paddy@64 266 GetGrantHandler(w, req, testContext)
paddy@64 267 if w.Code != http.StatusBadRequest {
paddy@64 268 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
paddy@64 269 }
paddy@64 270 if w.Body.String() != "The redirect_uri specified is not valid." {
paddy@64 271 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String())
paddy@64 272 }
paddy@64 273 w = httptest.NewRecorder()
paddy@64 274 params.Set("redirect_uri", "://not a URL")
paddy@60 275 req.URL.RawQuery = params.Encode()
paddy@60 276 GetGrantHandler(w, req, testContext)
paddy@60 277 if w.Code != http.StatusBadRequest {
paddy@60 278 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code)
paddy@60 279 }
paddy@60 280 if w.Body.String() != "The redirect_uri specified is not valid." {
paddy@60 281 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String())
paddy@60 282 }
paddy@56 283 }
paddy@65 284
paddy@65 285 func TestGetGrantCodeInvalidResponseType(t *testing.T) {
paddy@65 286 t.Parallel()
paddy@65 287 store := NewMemstore()
paddy@65 288 testContext := Context{
paddy@65 289 template: template.Must(template.New(getGrantTemplateName).Parse("{{ .error }}")),
paddy@65 290 clients: store,
paddy@65 291 grants: store,
paddy@65 292 profiles: store,
paddy@65 293 tokens: store,
paddy@65 294 }
paddy@65 295 client := Client{
paddy@65 296 ID: uuid.NewID(),
paddy@65 297 Secret: "super secret!",
paddy@65 298 OwnerID: uuid.NewID(),
paddy@65 299 Name: "My test client",
paddy@65 300 Logo: "https://secondbit.org/logo.png",
paddy@65 301 Website: "https://secondbit.org",
paddy@65 302 Type: "public",
paddy@65 303 }
paddy@65 304 uri, err := url.Parse("https://test.secondbit.org/redirect")
paddy@65 305 if err != nil {
paddy@65 306 t.Fatal("Can't parse URL:", err)
paddy@65 307 }
paddy@65 308 endpoint := Endpoint{
paddy@65 309 ID: uuid.NewID(),
paddy@65 310 ClientID: client.ID,
paddy@65 311 URI: *uri,
paddy@65 312 Added: time.Now(),
paddy@65 313 }
paddy@65 314 err = testContext.SaveClient(client)
paddy@65 315 if err != nil {
paddy@65 316 t.Fatal("Can't store client:", err)
paddy@65 317 }
paddy@65 318 err = testContext.AddEndpoint(client.ID, endpoint)
paddy@65 319 if err != nil {
paddy@65 320 t.Fatal("Can't store endpoint:", err)
paddy@65 321 }
paddy@65 322 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@65 323 if err != nil {
paddy@65 324 t.Fatal("Can't build request:", err)
paddy@65 325 }
paddy@65 326 params := url.Values{}
paddy@65 327 params.Set("response_type", "totally not code")
paddy@65 328 params.Set("client_id", client.ID.String())
paddy@65 329 params.Set("redirect_uri", endpoint.URI.String())
paddy@65 330 params.Set("scope", "testscope")
paddy@65 331 params.Set("state", "my super secure state string")
paddy@65 332 req.URL.RawQuery = params.Encode()
paddy@65 333 w := httptest.NewRecorder()
paddy@65 334 GetGrantHandler(w, req, testContext)
paddy@65 335 if w.Code != http.StatusFound {
paddy@65 336 t.Errorf("Expected status code to be %d, got %d", http.StatusFound, w.Code)
paddy@65 337 }
paddy@65 338 redirectedTo := w.Header().Get("Location")
paddy@65 339 red, err := url.Parse(redirectedTo)
paddy@65 340 if err != nil {
paddy@65 341 t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err)
paddy@65 342 }
paddy@65 343 if red.Query().Get("error") != "invalid_request" {
paddy@65 344 t.Errorf(`Expected error param in redirect URL to be "%s", got "%s"`, "invalid_request", red.Query().Get("error"))
paddy@65 345 }
paddy@65 346 stripParam("error", red)
paddy@65 347 if red.Query().Get("state") != params.Get("state") {
paddy@65 348 t.Errorf(`Expected state param in redirect URL to be "%s", got "%s"`, params.Get("state"), red.Query().Get("state"))
paddy@65 349 }
paddy@65 350 stripParam("state", red)
paddy@65 351 if red.String() != endpoint.URI.String() {
paddy@65 352 t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI.String(), red.String())
paddy@65 353 }
paddy@65 354 stripParam("response_type", req.URL)
paddy@65 355 w = httptest.NewRecorder()
paddy@65 356 GetGrantHandler(w, req, testContext)
paddy@65 357 if w.Code != http.StatusFound {
paddy@65 358 t.Errorf("Expected status code to be %d, got %d", http.StatusFound, w.Code)
paddy@65 359 }
paddy@65 360 redirectedTo = w.Header().Get("Location")
paddy@65 361 red, err = url.Parse(redirectedTo)
paddy@65 362 if err != nil {
paddy@65 363 t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err)
paddy@65 364 }
paddy@65 365 if red.Query().Get("error") != "invalid_request" {
paddy@65 366 t.Errorf(`Expected error param in redirect URL to be "%s", got "%s"`, "invalid_request", red.Query().Get("error"))
paddy@65 367 }
paddy@65 368 stripParam("error", red)
paddy@65 369 if red.Query().Get("state") != params.Get("state") {
paddy@65 370 t.Errorf(`Expected state param in redirect URL to be "%s", got "%s"`, params.Get("state"), red.Query().Get("state"))
paddy@65 371 }
paddy@65 372 stripParam("state", red)
paddy@65 373 if red.String() != endpoint.URI.String() {
paddy@65 374 t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI.String(), red.String())
paddy@65 375 }
paddy@65 376 }
paddy@66 377
paddy@66 378 func TestGetGrantCodeDenied(t *testing.T) {
paddy@66 379 t.Parallel()
paddy@66 380 store := NewMemstore()
paddy@66 381 testContext := Context{
paddy@66 382 template: template.Must(template.New(getGrantTemplateName).Parse("{{ .error }}")),
paddy@66 383 clients: store,
paddy@66 384 grants: store,
paddy@66 385 profiles: store,
paddy@66 386 tokens: store,
paddy@66 387 }
paddy@66 388 client := Client{
paddy@66 389 ID: uuid.NewID(),
paddy@66 390 Secret: "super secret!",
paddy@66 391 OwnerID: uuid.NewID(),
paddy@66 392 Name: "My test client",
paddy@66 393 Logo: "https://secondbit.org/logo.png",
paddy@66 394 Website: "https://secondbit.org",
paddy@66 395 Type: "public",
paddy@66 396 }
paddy@66 397 uri, err := url.Parse("https://test.secondbit.org/redirect")
paddy@66 398 if err != nil {
paddy@66 399 t.Fatal("Can't parse URL:", err)
paddy@66 400 }
paddy@66 401 endpoint := Endpoint{
paddy@66 402 ID: uuid.NewID(),
paddy@66 403 ClientID: client.ID,
paddy@66 404 URI: *uri,
paddy@66 405 Added: time.Now(),
paddy@66 406 }
paddy@66 407 err = testContext.SaveClient(client)
paddy@66 408 if err != nil {
paddy@66 409 t.Fatal("Can't store client:", err)
paddy@66 410 }
paddy@66 411 err = testContext.AddEndpoint(client.ID, endpoint)
paddy@66 412 if err != nil {
paddy@66 413 t.Fatal("Can't store endpoint:", err)
paddy@66 414 }
paddy@66 415 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil)
paddy@66 416 if err != nil {
paddy@66 417 t.Fatal("Can't build request:", err)
paddy@66 418 }
paddy@66 419 params := url.Values{}
paddy@66 420 params.Set("response_type", "code")
paddy@66 421 params.Set("client_id", client.ID.String())
paddy@66 422 params.Set("redirect_uri", endpoint.URI.String())
paddy@66 423 params.Set("scope", "testscope")
paddy@66 424 params.Set("state", "my super secure state string")
paddy@66 425 data := url.Values{}
paddy@66 426 data.Set("grant", "denied")
paddy@66 427 req.URL.RawQuery = params.Encode()
paddy@66 428 req.Body = ioutil.NopCloser(bytes.NewBufferString(data.Encode()))
paddy@66 429 req.Method = "POST"
paddy@66 430 w := httptest.NewRecorder()
paddy@66 431 GetGrantHandler(w, req, testContext)
paddy@66 432 if w.Code != http.StatusFound {
paddy@66 433 t.Errorf("Expected status code to be %d, got %d", http.StatusFound, w.Code)
paddy@66 434 }
paddy@66 435 redirectedTo := w.Header().Get("Location")
paddy@66 436 red, err := url.Parse(redirectedTo)
paddy@66 437 if err != nil {
paddy@66 438 t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err)
paddy@66 439 }
paddy@66 440 if red.Query().Get("error") != "access_denied" {
paddy@66 441 t.Errorf(`Expected error param in redirect URL to be "%s", got "%s"`, "access_denied", red.Query().Get("error"))
paddy@66 442 }
paddy@66 443 stripParam("error", red)
paddy@66 444 if red.Query().Get("state") != params.Get("state") {
paddy@66 445 t.Errorf(`Expected state param in redirect URL to be "%s", got "%s"`, params.Get("state"), red.Query().Get("state"))
paddy@66 446 }
paddy@66 447 stripParam("state", red)
paddy@66 448 if red.String() != endpoint.URI.String() {
paddy@66 449 t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI.String(), red.String())
paddy@66 450 }
paddy@66 451 }