auth
auth/oauth2_test.go
Enable the implict grant flow. Add the implicit grant flow. This can't be done in a grant type, because it's not specified through the grant_type parameter, for some absurd reason. Whatever. We basically achieved this by refactoring how we respond to the authorization endpoint, keying off the "response_type" parameter.
| paddy@52 | 1 package auth |
| paddy@52 | 2 |
| paddy@52 | 3 import ( |
| paddy@66 | 4 "bytes" |
| paddy@81 | 5 "encoding/json" |
| paddy@52 | 6 "html/template" |
| paddy@66 | 7 "io/ioutil" |
| paddy@52 | 8 "net/http" |
| paddy@52 | 9 "net/http/httptest" |
| paddy@53 | 10 "net/url" |
| paddy@52 | 11 "testing" |
| paddy@56 | 12 "time" |
| paddy@56 | 13 |
| paddy@107 | 14 "code.secondbit.org/uuid.hg" |
| paddy@52 | 15 ) |
| paddy@52 | 16 |
| paddy@53 | 17 const ( |
| paddy@53 | 18 scopeSet = 1 << iota |
| paddy@53 | 19 stateSet |
| paddy@53 | 20 uriSet |
| paddy@53 | 21 ) |
| paddy@53 | 22 |
| paddy@65 | 23 func stripParam(param string, u *url.URL) { |
| paddy@65 | 24 q := u.Query() |
| paddy@65 | 25 q.Del(param) |
| paddy@65 | 26 u.RawQuery = q.Encode() |
| paddy@65 | 27 } |
| paddy@65 | 28 |
| paddy@87 | 29 func TestGetAuthorizationCodeCodeSuccess(t *testing.T) { |
| paddy@52 | 30 t.Parallel() |
| paddy@52 | 31 store := NewMemstore() |
| paddy@52 | 32 testContext := Context{ |
| paddy@116 | 33 template: template.Must(template.New(getAuthorizationCodeTemplateName).Parse("{{ if not .error }}Get auth grant{{ else }}{{ .error }}{{ end }}")), |
| paddy@87 | 34 clients: store, |
| paddy@87 | 35 authCodes: store, |
| paddy@87 | 36 profiles: store, |
| paddy@87 | 37 tokens: store, |
| paddy@87 | 38 sessions: store, |
| paddy@52 | 39 } |
| paddy@56 | 40 client := Client{ |
| paddy@56 | 41 ID: uuid.NewID(), |
| paddy@56 | 42 Secret: "super secret!", |
| paddy@56 | 43 OwnerID: uuid.NewID(), |
| paddy@56 | 44 Name: "My test client", |
| paddy@56 | 45 Logo: "https://secondbit.org/logo.png", |
| paddy@56 | 46 Website: "https://secondbit.org", |
| paddy@56 | 47 Type: "public", |
| paddy@56 | 48 } |
| paddy@56 | 49 endpoint := Endpoint{ |
| paddy@56 | 50 ID: uuid.NewID(), |
| paddy@56 | 51 ClientID: client.ID, |
| paddy@116 | 52 URI: "https://test.secondbit.org/redirect", |
| paddy@56 | 53 Added: time.Now(), |
| paddy@56 | 54 } |
| paddy@116 | 55 err := testContext.SaveClient(client) |
| paddy@56 | 56 if err != nil { |
| paddy@56 | 57 t.Fatal("Can't store client:", err) |
| paddy@56 | 58 } |
| paddy@115 | 59 err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint}) |
| paddy@56 | 60 if err != nil { |
| paddy@56 | 61 t.Fatal("Can't store endpoint:", err) |
| paddy@56 | 62 } |
| paddy@85 | 63 profile := Profile{ |
| paddy@85 | 64 ID: uuid.NewID(), |
| paddy@85 | 65 } |
| paddy@85 | 66 err = testContext.SaveProfile(profile) |
| paddy@85 | 67 if err != nil { |
| paddy@85 | 68 t.Fatal("Can't store profile:", err) |
| paddy@85 | 69 } |
| paddy@77 | 70 session := Session{ |
| paddy@85 | 71 ID: "testsession", |
| paddy@85 | 72 Active: true, |
| paddy@85 | 73 ProfileID: profile.ID, |
| paddy@77 | 74 } |
| paddy@77 | 75 err = testContext.CreateSession(session) |
| paddy@77 | 76 if err != nil { |
| paddy@77 | 77 t.Fatal("Can't store session:", err) |
| paddy@77 | 78 } |
| paddy@52 | 79 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@52 | 80 if err != nil { |
| paddy@52 | 81 t.Fatal("Can't build request:", err) |
| paddy@52 | 82 } |
| paddy@77 | 83 cookie := &http.Cookie{ |
| paddy@77 | 84 Name: authCookieName, |
| paddy@77 | 85 Value: session.ID, |
| paddy@77 | 86 } |
| paddy@77 | 87 req.AddCookie(cookie) |
| paddy@58 | 88 for i := 0; i < 1<<3; i++ { |
| paddy@53 | 89 w := httptest.NewRecorder() |
| paddy@53 | 90 params := url.Values{} |
| paddy@53 | 91 // see OAuth 2.0 spec, section 4.1.1 |
| paddy@53 | 92 params.Set("response_type", "code") |
| paddy@56 | 93 params.Set("client_id", client.ID.String()) |
| paddy@53 | 94 if i&uriSet != 0 { |
| paddy@116 | 95 params.Set("redirect_uri", endpoint.URI) |
| paddy@53 | 96 } |
| paddy@53 | 97 if i&scopeSet != 0 { |
| paddy@53 | 98 params.Set("scope", "testscope") |
| paddy@53 | 99 } |
| paddy@53 | 100 if i&stateSet != 0 { |
| paddy@53 | 101 params.Set("state", "my super secure state string") |
| paddy@53 | 102 } |
| paddy@53 | 103 req.URL.RawQuery = params.Encode() |
| paddy@66 | 104 req.Method = "GET" |
| paddy@66 | 105 req.Body = nil |
| paddy@66 | 106 req.Header.Del("Content-Type") |
| paddy@87 | 107 GetAuthorizationCodeHandler(w, req, testContext) |
| paddy@53 | 108 if w.Code != http.StatusOK { |
| paddy@116 | 109 t.Log("Response body:", w.Body.String()) |
| paddy@53 | 110 t.Errorf("Expected status code to be %d, got %d for %s", http.StatusOK, w.Code, req.URL.String()) |
| paddy@53 | 111 } |
| paddy@53 | 112 if w.Body.String() != "Get auth grant" { |
| paddy@53 | 113 t.Errorf("Expected body to be `%s`, got `%s` for %s", "Get auth grant", w.Body.String(), req.URL.String()) |
| paddy@53 | 114 } |
| paddy@66 | 115 req.Method = "POST" |
| paddy@66 | 116 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@66 | 117 w = httptest.NewRecorder() |
| paddy@66 | 118 data := url.Values{} |
| paddy@66 | 119 data.Set("grant", "approved") |
| paddy@66 | 120 body := bytes.NewBufferString(data.Encode()) |
| paddy@66 | 121 req.Body = ioutil.NopCloser(body) |
| paddy@87 | 122 GetAuthorizationCodeHandler(w, req, testContext) |
| paddy@66 | 123 if w.Code != http.StatusFound { |
| paddy@66 | 124 t.Errorf("Expected status code to be %d, got %d for %s", http.StatusFound, w.Code, req.URL.String()) |
| paddy@66 | 125 } |
| paddy@66 | 126 redirectedTo := w.Header().Get("Location") |
| paddy@66 | 127 red, err := url.Parse(redirectedTo) |
| paddy@66 | 128 if err != nil { |
| paddy@66 | 129 t.Fatalf(`Being redirected to a non-URL "%s" threw error "%s" for "%s"\n`, redirectedTo, err, req.URL.String()) |
| paddy@66 | 130 } |
| paddy@66 | 131 t.Log("Redirected to", redirectedTo) |
| paddy@66 | 132 if red.Query().Get("code") == "" { |
| paddy@66 | 133 t.Fatalf(`Expected code param in redirect URL to be set, but it wasn't for %s`, req.URL.String()) |
| paddy@66 | 134 } |
| paddy@87 | 135 if _, err := testContext.GetAuthorizationCode(red.Query().Get("code")); err != nil { |
| paddy@67 | 136 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 | 137 } |
| paddy@87 | 138 err = testContext.DeleteAuthorizationCode(red.Query().Get("code")) |
| paddy@66 | 139 if err != nil { |
| paddy@82 | 140 t.Logf(`Unexpected error "%s" deleting grant "%s" for %s`, err, red.Query().Get("code"), req.URL.String()) |
| paddy@66 | 141 } |
| paddy@66 | 142 stripParam("code", red) |
| paddy@66 | 143 if red.Query().Get("state") != params.Get("state") { |
| paddy@66 | 144 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 | 145 } |
| paddy@66 | 146 stripParam("state", red) |
| paddy@116 | 147 if red.String() != endpoint.URI { |
| paddy@116 | 148 t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI, red.String()) |
| paddy@66 | 149 } |
| paddy@52 | 150 } |
| paddy@52 | 151 } |
| paddy@56 | 152 |
| paddy@87 | 153 func TestGetAuthorizationCodeCodeInvalidClient(t *testing.T) { |
| paddy@62 | 154 t.Parallel() |
| paddy@62 | 155 store := NewMemstore() |
| paddy@62 | 156 testContext := Context{ |
| paddy@87 | 157 template: template.Must(template.New(getAuthorizationCodeTemplateName).Parse("{{ .error }}")), |
| paddy@87 | 158 clients: store, |
| paddy@87 | 159 authCodes: store, |
| paddy@87 | 160 profiles: store, |
| paddy@87 | 161 tokens: store, |
| paddy@87 | 162 sessions: store, |
| paddy@62 | 163 } |
| paddy@62 | 164 client := Client{ |
| paddy@62 | 165 ID: uuid.NewID(), |
| paddy@62 | 166 Secret: "super secret!", |
| paddy@62 | 167 OwnerID: uuid.NewID(), |
| paddy@62 | 168 Name: "My test client", |
| paddy@62 | 169 Type: "public", |
| paddy@62 | 170 } |
| paddy@62 | 171 err := testContext.SaveClient(client) |
| paddy@62 | 172 if err != nil { |
| paddy@62 | 173 t.Fatal("Can't store client:", err) |
| paddy@62 | 174 } |
| paddy@77 | 175 session := Session{ |
| paddy@77 | 176 ID: "testsession", |
| paddy@77 | 177 Active: true, |
| paddy@77 | 178 } |
| paddy@77 | 179 err = testContext.CreateSession(session) |
| paddy@77 | 180 if err != nil { |
| paddy@77 | 181 t.Fatal("Can't store session:", err) |
| paddy@77 | 182 } |
| paddy@62 | 183 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@62 | 184 if err != nil { |
| paddy@62 | 185 t.Fatal("Can't build request:", err) |
| paddy@62 | 186 } |
| paddy@62 | 187 w := httptest.NewRecorder() |
| paddy@62 | 188 params := url.Values{} |
| paddy@62 | 189 params.Set("response_type", "code") |
| paddy@62 | 190 params.Set("redirect_uri", "https://test.secondbit.org/") |
| paddy@62 | 191 req.URL.RawQuery = params.Encode() |
| paddy@77 | 192 cookie := &http.Cookie{ |
| paddy@77 | 193 Name: authCookieName, |
| paddy@77 | 194 Value: session.ID, |
| paddy@77 | 195 } |
| paddy@77 | 196 req.AddCookie(cookie) |
| paddy@87 | 197 GetAuthorizationCodeHandler(w, req, testContext) |
| paddy@62 | 198 if w.Code != http.StatusBadRequest { |
| paddy@62 | 199 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code) |
| paddy@62 | 200 } |
| paddy@62 | 201 if w.Body.String() != "Client ID must be specified in the request." { |
| paddy@62 | 202 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "Client ID must be specified in the request.", w.Body.String()) |
| paddy@62 | 203 } |
| paddy@62 | 204 w = httptest.NewRecorder() |
| paddy@62 | 205 params.Set("client_id", "Not an ID") |
| paddy@62 | 206 req.URL.RawQuery = params.Encode() |
| paddy@87 | 207 GetAuthorizationCodeHandler(w, req, testContext) |
| paddy@62 | 208 if w.Code != http.StatusBadRequest { |
| paddy@62 | 209 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code) |
| paddy@62 | 210 } |
| paddy@62 | 211 if w.Body.String() != "client_id is not a valid Client ID." { |
| paddy@62 | 212 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "client_id is not a valid Client ID.", w.Body.String()) |
| paddy@62 | 213 } |
| paddy@62 | 214 w = httptest.NewRecorder() |
| paddy@62 | 215 params.Set("client_id", uuid.NewID().String()) |
| paddy@62 | 216 req.URL.RawQuery = params.Encode() |
| paddy@87 | 217 GetAuthorizationCodeHandler(w, req, testContext) |
| paddy@62 | 218 if w.Code != http.StatusBadRequest { |
| paddy@62 | 219 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code) |
| paddy@62 | 220 } |
| paddy@62 | 221 if w.Body.String() != "The specified Client couldn’t be found." { |
| paddy@62 | 222 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The specified Client couldn’t be found.", w.Body.String()) |
| paddy@62 | 223 } |
| paddy@62 | 224 } |
| paddy@62 | 225 |
| paddy@87 | 226 func TestGetAuthorizationCodeCodeInvalidURI(t *testing.T) { |
| paddy@56 | 227 t.Parallel() |
| paddy@56 | 228 store := NewMemstore() |
| paddy@56 | 229 testContext := Context{ |
| paddy@87 | 230 template: template.Must(template.New(getAuthorizationCodeTemplateName).Parse("{{ .error }}")), |
| paddy@87 | 231 clients: store, |
| paddy@87 | 232 authCodes: store, |
| paddy@87 | 233 profiles: store, |
| paddy@87 | 234 tokens: store, |
| paddy@87 | 235 sessions: store, |
| paddy@56 | 236 } |
| paddy@56 | 237 client := Client{ |
| paddy@56 | 238 ID: uuid.NewID(), |
| paddy@56 | 239 Secret: "super secret!", |
| paddy@56 | 240 OwnerID: uuid.NewID(), |
| paddy@56 | 241 Name: "My test client", |
| paddy@56 | 242 Type: "public", |
| paddy@56 | 243 } |
| paddy@116 | 244 err := testContext.SaveClient(client) |
| paddy@56 | 245 if err != nil { |
| paddy@56 | 246 t.Fatal("Can't store client:", err) |
| paddy@56 | 247 } |
| paddy@77 | 248 session := Session{ |
| paddy@77 | 249 ID: "testsession", |
| paddy@77 | 250 Active: true, |
| paddy@77 | 251 } |
| paddy@77 | 252 err = testContext.CreateSession(session) |
| paddy@77 | 253 if err != nil { |
| paddy@77 | 254 t.Fatal("Can't store session:", err) |
| paddy@77 | 255 } |
| paddy@56 | 256 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@56 | 257 if err != nil { |
| paddy@56 | 258 t.Fatal("Can't build request:", err) |
| paddy@56 | 259 } |
| paddy@77 | 260 cookie := &http.Cookie{ |
| paddy@77 | 261 Name: authCookieName, |
| paddy@77 | 262 Value: session.ID, |
| paddy@77 | 263 } |
| paddy@77 | 264 req.AddCookie(cookie) |
| paddy@56 | 265 w := httptest.NewRecorder() |
| paddy@56 | 266 params := url.Values{} |
| paddy@56 | 267 params.Set("response_type", "code") |
| paddy@56 | 268 params.Set("client_id", client.ID.String()) |
| paddy@64 | 269 req.URL.RawQuery = params.Encode() |
| paddy@87 | 270 GetAuthorizationCodeHandler(w, req, testContext) |
| paddy@64 | 271 if w.Code != http.StatusBadRequest { |
| paddy@64 | 272 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code) |
| paddy@64 | 273 } |
| paddy@64 | 274 if w.Body.String() != "The redirect_uri specified is not valid." { |
| paddy@64 | 275 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String()) |
| paddy@64 | 276 } |
| paddy@64 | 277 endpoint := Endpoint{ |
| paddy@64 | 278 ID: uuid.NewID(), |
| paddy@64 | 279 ClientID: client.ID, |
| paddy@116 | 280 URI: "https://test.secondbit.org/redirect", |
| paddy@64 | 281 Added: time.Now(), |
| paddy@64 | 282 } |
| paddy@115 | 283 err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint}) |
| paddy@64 | 284 if err != nil { |
| paddy@64 | 285 t.Fatal("Can't store endpoint:", err) |
| paddy@64 | 286 } |
| paddy@64 | 287 w = httptest.NewRecorder() |
| paddy@56 | 288 params.Set("redirect_uri", "https://test.secondbit.org/wrong") |
| paddy@56 | 289 req.URL.RawQuery = params.Encode() |
| paddy@87 | 290 GetAuthorizationCodeHandler(w, req, testContext) |
| paddy@56 | 291 if w.Code != http.StatusBadRequest { |
| paddy@56 | 292 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code) |
| paddy@56 | 293 } |
| paddy@56 | 294 if w.Body.String() != "The redirect_uri specified is not valid." { |
| paddy@56 | 295 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String()) |
| paddy@56 | 296 } |
| paddy@64 | 297 endpoint2 := Endpoint{ |
| paddy@64 | 298 ID: uuid.NewID(), |
| paddy@64 | 299 ClientID: client.ID, |
| paddy@116 | 300 URI: "https://test.secondbit.org/redirect", |
| paddy@64 | 301 Added: time.Now(), |
| paddy@64 | 302 } |
| paddy@115 | 303 err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint2}) |
| paddy@60 | 304 if err != nil { |
| paddy@64 | 305 t.Fatal("Can't store endpoint:", err) |
| paddy@60 | 306 } |
| paddy@60 | 307 w = httptest.NewRecorder() |
| paddy@64 | 308 params.Set("redirect_uri", "") |
| paddy@64 | 309 req.URL.RawQuery = params.Encode() |
| paddy@87 | 310 GetAuthorizationCodeHandler(w, req, testContext) |
| paddy@64 | 311 if w.Code != http.StatusBadRequest { |
| paddy@64 | 312 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code) |
| paddy@64 | 313 } |
| paddy@64 | 314 if w.Body.String() != "The redirect_uri specified is not valid." { |
| paddy@64 | 315 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String()) |
| paddy@64 | 316 } |
| paddy@64 | 317 w = httptest.NewRecorder() |
| paddy@64 | 318 params.Set("redirect_uri", "://not a URL") |
| paddy@60 | 319 req.URL.RawQuery = params.Encode() |
| paddy@87 | 320 GetAuthorizationCodeHandler(w, req, testContext) |
| paddy@60 | 321 if w.Code != http.StatusBadRequest { |
| paddy@60 | 322 t.Errorf("Expected status code to be %d, got %d", http.StatusBadRequest, w.Code) |
| paddy@60 | 323 } |
| paddy@60 | 324 if w.Body.String() != "The redirect_uri specified is not valid." { |
| paddy@60 | 325 t.Errorf(`Expected output to be "%s", got "%s" instead.`, "The redirect_uri specified is not valid.", w.Body.String()) |
| paddy@60 | 326 } |
| paddy@56 | 327 } |
| paddy@65 | 328 |
| paddy@87 | 329 func TestGetAuthorizationCodeCodeInvalidResponseType(t *testing.T) { |
| paddy@65 | 330 t.Parallel() |
| paddy@65 | 331 store := NewMemstore() |
| paddy@65 | 332 testContext := Context{ |
| paddy@87 | 333 template: template.Must(template.New(getAuthorizationCodeTemplateName).Parse("{{ .error }}")), |
| paddy@87 | 334 clients: store, |
| paddy@87 | 335 authCodes: store, |
| paddy@87 | 336 profiles: store, |
| paddy@87 | 337 tokens: store, |
| paddy@87 | 338 sessions: store, |
| paddy@65 | 339 } |
| paddy@65 | 340 client := Client{ |
| paddy@65 | 341 ID: uuid.NewID(), |
| paddy@65 | 342 Secret: "super secret!", |
| paddy@65 | 343 OwnerID: uuid.NewID(), |
| paddy@65 | 344 Name: "My test client", |
| paddy@65 | 345 Logo: "https://secondbit.org/logo.png", |
| paddy@65 | 346 Website: "https://secondbit.org", |
| paddy@65 | 347 Type: "public", |
| paddy@65 | 348 } |
| paddy@65 | 349 endpoint := Endpoint{ |
| paddy@65 | 350 ID: uuid.NewID(), |
| paddy@65 | 351 ClientID: client.ID, |
| paddy@116 | 352 URI: "https://test.secondbit.org/redirect", |
| paddy@65 | 353 Added: time.Now(), |
| paddy@65 | 354 } |
| paddy@116 | 355 err := testContext.SaveClient(client) |
| paddy@65 | 356 if err != nil { |
| paddy@65 | 357 t.Fatal("Can't store client:", err) |
| paddy@65 | 358 } |
| paddy@115 | 359 err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint}) |
| paddy@65 | 360 if err != nil { |
| paddy@65 | 361 t.Fatal("Can't store endpoint:", err) |
| paddy@65 | 362 } |
| paddy@77 | 363 session := Session{ |
| paddy@77 | 364 ID: "testsession", |
| paddy@77 | 365 Active: true, |
| paddy@77 | 366 } |
| paddy@77 | 367 err = testContext.CreateSession(session) |
| paddy@77 | 368 if err != nil { |
| paddy@77 | 369 t.Fatal("Can't store session:", err) |
| paddy@77 | 370 } |
| paddy@65 | 371 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@65 | 372 if err != nil { |
| paddy@65 | 373 t.Fatal("Can't build request:", err) |
| paddy@65 | 374 } |
| paddy@77 | 375 cookie := &http.Cookie{ |
| paddy@77 | 376 Name: authCookieName, |
| paddy@77 | 377 Value: session.ID, |
| paddy@77 | 378 } |
| paddy@77 | 379 req.AddCookie(cookie) |
| paddy@65 | 380 params := url.Values{} |
| paddy@65 | 381 params.Set("response_type", "totally not code") |
| paddy@65 | 382 params.Set("client_id", client.ID.String()) |
| paddy@116 | 383 params.Set("redirect_uri", endpoint.URI) |
| paddy@65 | 384 params.Set("scope", "testscope") |
| paddy@65 | 385 params.Set("state", "my super secure state string") |
| paddy@65 | 386 req.URL.RawQuery = params.Encode() |
| paddy@65 | 387 w := httptest.NewRecorder() |
| paddy@87 | 388 GetAuthorizationCodeHandler(w, req, testContext) |
| paddy@65 | 389 if w.Code != http.StatusFound { |
| paddy@65 | 390 t.Errorf("Expected status code to be %d, got %d", http.StatusFound, w.Code) |
| paddy@65 | 391 } |
| paddy@65 | 392 redirectedTo := w.Header().Get("Location") |
| paddy@65 | 393 red, err := url.Parse(redirectedTo) |
| paddy@65 | 394 if err != nil { |
| paddy@65 | 395 t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err) |
| paddy@65 | 396 } |
| paddy@65 | 397 if red.Query().Get("error") != "invalid_request" { |
| paddy@65 | 398 t.Errorf(`Expected error param in redirect URL to be "%s", got "%s"`, "invalid_request", red.Query().Get("error")) |
| paddy@65 | 399 } |
| paddy@65 | 400 stripParam("error", red) |
| paddy@65 | 401 if red.Query().Get("state") != params.Get("state") { |
| paddy@65 | 402 t.Errorf(`Expected state param in redirect URL to be "%s", got "%s"`, params.Get("state"), red.Query().Get("state")) |
| paddy@65 | 403 } |
| paddy@65 | 404 stripParam("state", red) |
| paddy@116 | 405 if red.String() != endpoint.URI { |
| paddy@116 | 406 t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI, red.String()) |
| paddy@65 | 407 } |
| paddy@65 | 408 stripParam("response_type", req.URL) |
| paddy@65 | 409 w = httptest.NewRecorder() |
| paddy@87 | 410 GetAuthorizationCodeHandler(w, req, testContext) |
| paddy@65 | 411 if w.Code != http.StatusFound { |
| paddy@65 | 412 t.Errorf("Expected status code to be %d, got %d", http.StatusFound, w.Code) |
| paddy@65 | 413 } |
| paddy@65 | 414 redirectedTo = w.Header().Get("Location") |
| paddy@65 | 415 red, err = url.Parse(redirectedTo) |
| paddy@65 | 416 if err != nil { |
| paddy@65 | 417 t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err) |
| paddy@65 | 418 } |
| paddy@65 | 419 if red.Query().Get("error") != "invalid_request" { |
| paddy@65 | 420 t.Errorf(`Expected error param in redirect URL to be "%s", got "%s"`, "invalid_request", red.Query().Get("error")) |
| paddy@65 | 421 } |
| paddy@65 | 422 stripParam("error", red) |
| paddy@65 | 423 if red.Query().Get("state") != params.Get("state") { |
| paddy@65 | 424 t.Errorf(`Expected state param in redirect URL to be "%s", got "%s"`, params.Get("state"), red.Query().Get("state")) |
| paddy@65 | 425 } |
| paddy@65 | 426 stripParam("state", red) |
| paddy@116 | 427 if red.String() != endpoint.URI { |
| paddy@116 | 428 t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI, red.String()) |
| paddy@65 | 429 } |
| paddy@65 | 430 } |
| paddy@66 | 431 |
| paddy@87 | 432 func TestGetAuthorizationCodeCodeDenied(t *testing.T) { |
| paddy@66 | 433 t.Parallel() |
| paddy@66 | 434 store := NewMemstore() |
| paddy@66 | 435 testContext := Context{ |
| paddy@87 | 436 template: template.Must(template.New(getAuthorizationCodeTemplateName).Parse("{{ .error }}")), |
| paddy@87 | 437 clients: store, |
| paddy@87 | 438 authCodes: store, |
| paddy@87 | 439 profiles: store, |
| paddy@87 | 440 tokens: store, |
| paddy@87 | 441 sessions: store, |
| paddy@66 | 442 } |
| paddy@66 | 443 client := Client{ |
| paddy@66 | 444 ID: uuid.NewID(), |
| paddy@66 | 445 Secret: "super secret!", |
| paddy@66 | 446 OwnerID: uuid.NewID(), |
| paddy@66 | 447 Name: "My test client", |
| paddy@66 | 448 Logo: "https://secondbit.org/logo.png", |
| paddy@66 | 449 Website: "https://secondbit.org", |
| paddy@66 | 450 Type: "public", |
| paddy@66 | 451 } |
| paddy@66 | 452 endpoint := Endpoint{ |
| paddy@66 | 453 ID: uuid.NewID(), |
| paddy@66 | 454 ClientID: client.ID, |
| paddy@116 | 455 URI: "https://test.secondbit.org/redirect", |
| paddy@66 | 456 Added: time.Now(), |
| paddy@66 | 457 } |
| paddy@116 | 458 err := testContext.SaveClient(client) |
| paddy@66 | 459 if err != nil { |
| paddy@66 | 460 t.Fatal("Can't store client:", err) |
| paddy@66 | 461 } |
| paddy@115 | 462 err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint}) |
| paddy@66 | 463 if err != nil { |
| paddy@66 | 464 t.Fatal("Can't store endpoint:", err) |
| paddy@66 | 465 } |
| paddy@77 | 466 session := Session{ |
| paddy@77 | 467 ID: "testsession", |
| paddy@77 | 468 Active: true, |
| paddy@77 | 469 } |
| paddy@77 | 470 err = testContext.CreateSession(session) |
| paddy@77 | 471 if err != nil { |
| paddy@77 | 472 t.Fatal("Can't store session:", err) |
| paddy@77 | 473 } |
| paddy@66 | 474 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@66 | 475 if err != nil { |
| paddy@66 | 476 t.Fatal("Can't build request:", err) |
| paddy@66 | 477 } |
| paddy@77 | 478 cookie := &http.Cookie{ |
| paddy@77 | 479 Name: authCookieName, |
| paddy@77 | 480 Value: session.ID, |
| paddy@77 | 481 } |
| paddy@77 | 482 req.AddCookie(cookie) |
| paddy@66 | 483 params := url.Values{} |
| paddy@66 | 484 params.Set("response_type", "code") |
| paddy@66 | 485 params.Set("client_id", client.ID.String()) |
| paddy@116 | 486 params.Set("redirect_uri", endpoint.URI) |
| paddy@66 | 487 params.Set("scope", "testscope") |
| paddy@66 | 488 params.Set("state", "my super secure state string") |
| paddy@66 | 489 data := url.Values{} |
| paddy@66 | 490 data.Set("grant", "denied") |
| paddy@66 | 491 req.URL.RawQuery = params.Encode() |
| paddy@66 | 492 req.Body = ioutil.NopCloser(bytes.NewBufferString(data.Encode())) |
| paddy@66 | 493 req.Method = "POST" |
| paddy@66 | 494 w := httptest.NewRecorder() |
| paddy@87 | 495 GetAuthorizationCodeHandler(w, req, testContext) |
| paddy@66 | 496 if w.Code != http.StatusFound { |
| paddy@66 | 497 t.Errorf("Expected status code to be %d, got %d", http.StatusFound, w.Code) |
| paddy@66 | 498 } |
| paddy@66 | 499 redirectedTo := w.Header().Get("Location") |
| paddy@66 | 500 red, err := url.Parse(redirectedTo) |
| paddy@66 | 501 if err != nil { |
| paddy@66 | 502 t.Fatalf("Being redirected to a non-URL (%s) threw error: %s\n", redirectedTo, err) |
| paddy@66 | 503 } |
| paddy@66 | 504 if red.Query().Get("error") != "access_denied" { |
| paddy@66 | 505 t.Errorf(`Expected error param in redirect URL to be "%s", got "%s"`, "access_denied", red.Query().Get("error")) |
| paddy@66 | 506 } |
| paddy@66 | 507 stripParam("error", red) |
| paddy@66 | 508 if red.Query().Get("state") != params.Get("state") { |
| paddy@66 | 509 t.Errorf(`Expected state param in redirect URL to be "%s", got "%s"`, params.Get("state"), red.Query().Get("state")) |
| paddy@66 | 510 } |
| paddy@66 | 511 stripParam("state", red) |
| paddy@116 | 512 if red.String() != endpoint.URI { |
| paddy@116 | 513 t.Errorf(`Expected redirect URL to be "%s", got "%s"`, endpoint.URI, red.String()) |
| paddy@66 | 514 } |
| paddy@66 | 515 } |
| paddy@77 | 516 |
| paddy@87 | 517 func TestGetAuthorizationCodeCodeLoginRedirect(t *testing.T) { |
| paddy@80 | 518 t.Parallel() |
| paddy@80 | 519 req, err := http.NewRequest("GET", "https://test.auth.secondbit.org/oauth2/grant", nil) |
| paddy@80 | 520 if err != nil { |
| paddy@80 | 521 t.Fatal("Can't build request:", err) |
| paddy@80 | 522 } |
| paddy@80 | 523 w := httptest.NewRecorder() |
| paddy@87 | 524 GetAuthorizationCodeHandler(w, req, Context{template: template.Must(template.New(getAuthorizationCodeTemplateName).Parse("{{ .internal_error }}"))}) |
| paddy@80 | 525 if w.Code != http.StatusInternalServerError { |
| paddy@80 | 526 t.Errorf("Expected status code to be %d, got %d", http.StatusInternalServerError, w.Code) |
| paddy@80 | 527 } |
| paddy@80 | 528 expectation := "Missing login URL." |
| paddy@80 | 529 if w.Body.String() != expectation { |
| paddy@80 | 530 t.Errorf(`Expected body to be "%s", got "%s"`, expectation, w.Body.String()) |
| paddy@80 | 531 } |
| paddy@80 | 532 uri, err := url.Parse("https://client.secondbit.org/") |
| paddy@80 | 533 if err != nil { |
| paddy@82 | 534 t.Error("Unexpected error", err) |
| paddy@80 | 535 } |
| paddy@80 | 536 testContext := Context{ |
| paddy@80 | 537 loginURI: uri, |
| paddy@80 | 538 } |
| paddy@80 | 539 w = httptest.NewRecorder() |
| paddy@87 | 540 GetAuthorizationCodeHandler(w, req, testContext) |
| paddy@80 | 541 if w.Code != http.StatusFound { |
| paddy@80 | 542 t.Errorf("Expected status code to be %d, got %d", http.StatusFound, w.Code) |
| paddy@80 | 543 } |
| paddy@80 | 544 redirectedTo := w.Header().Get("Location") |
| paddy@80 | 545 expectation = "https://client.secondbit.org/?from=https%3A%2F%2Ftest.auth.secondbit.org%2Foauth2%2Fgrant" |
| paddy@80 | 546 if redirectedTo != expectation { |
| paddy@80 | 547 t.Errorf("Expected to be redirected to '%s', was redirected to '%s'", expectation, redirectedTo) |
| paddy@80 | 548 } |
| paddy@80 | 549 } |
| paddy@80 | 550 |
| paddy@78 | 551 func TestCheckCookie(t *testing.T) { |
| paddy@78 | 552 t.Parallel() |
| paddy@78 | 553 req, err := http.NewRequest("GET", "https://auth.secondbit.org", nil) |
| paddy@78 | 554 if err != nil { |
| paddy@78 | 555 t.Error("Unexpected error creating base request:", err) |
| paddy@78 | 556 } |
| paddy@78 | 557 store := NewMemstore() |
| paddy@78 | 558 testContext := Context{ |
| paddy@78 | 559 sessions: store, |
| paddy@78 | 560 } |
| paddy@78 | 561 session, err := checkCookie(req, testContext) |
| paddy@78 | 562 if err != ErrNoSession { |
| paddy@78 | 563 t.Errorf("Expected ErrNoSession, got %s", err) |
| paddy@78 | 564 } |
| paddy@78 | 565 session = Session{ |
| paddy@78 | 566 ID: "testsession", |
| paddy@78 | 567 Active: true, |
| paddy@78 | 568 } |
| paddy@78 | 569 err = testContext.CreateSession(session) |
| paddy@78 | 570 if err != nil { |
| paddy@78 | 571 t.Error("Unexpected error persisting session:", err) |
| paddy@78 | 572 } |
| paddy@78 | 573 invalidSession := Session{ |
| paddy@78 | 574 ID: "testsession2", |
| paddy@78 | 575 Active: false, |
| paddy@78 | 576 } |
| paddy@78 | 577 err = testContext.CreateSession(invalidSession) |
| paddy@78 | 578 if err != nil { |
| paddy@78 | 579 t.Error("Unexpected error persisting session:", err) |
| paddy@78 | 580 } |
| paddy@78 | 581 result, err := checkCookie(req, testContext) |
| paddy@78 | 582 if err != ErrNoSession { |
| paddy@78 | 583 t.Errorf("Expected ErrNoSession, got %s", err) |
| paddy@78 | 584 } |
| paddy@78 | 585 req.AddCookie(&http.Cookie{ |
| paddy@78 | 586 Name: "wrongcookie", |
| paddy@78 | 587 Value: "wrong value", |
| paddy@78 | 588 }) |
| paddy@78 | 589 result, err = checkCookie(req, testContext) |
| paddy@78 | 590 if err != ErrNoSession { |
| paddy@78 | 591 t.Error("Expected ErrNoSession, got", err) |
| paddy@78 | 592 } |
| paddy@78 | 593 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil) |
| paddy@78 | 594 if err != nil { |
| paddy@78 | 595 t.Error("Unexpected error creating base request:", err) |
| paddy@78 | 596 } |
| paddy@78 | 597 req.AddCookie(&http.Cookie{ |
| paddy@78 | 598 Name: "Stillwrongcookie", |
| paddy@78 | 599 Value: session.ID, |
| paddy@78 | 600 }) |
| paddy@78 | 601 result, err = checkCookie(req, testContext) |
| paddy@78 | 602 if err != ErrNoSession { |
| paddy@78 | 603 t.Error("Expected ErrNoSession, got", err) |
| paddy@78 | 604 } |
| paddy@78 | 605 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil) |
| paddy@78 | 606 if err != nil { |
| paddy@78 | 607 t.Error("Unexpected error creating base request:", err) |
| paddy@78 | 608 } |
| paddy@78 | 609 req.AddCookie(&http.Cookie{ |
| paddy@78 | 610 Name: authCookieName, |
| paddy@78 | 611 Value: "wrong value", |
| paddy@78 | 612 }) |
| paddy@78 | 613 result, err = checkCookie(req, testContext) |
| paddy@78 | 614 if err != ErrInvalidSession { |
| paddy@78 | 615 t.Error("Expected ErrInvalidSession, got", err) |
| paddy@78 | 616 } |
| paddy@78 | 617 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil) |
| paddy@78 | 618 if err != nil { |
| paddy@78 | 619 t.Error("Unexpected error creating base request:", err) |
| paddy@78 | 620 } |
| paddy@78 | 621 req.AddCookie(&http.Cookie{ |
| paddy@78 | 622 Name: authCookieName, |
| paddy@78 | 623 Value: invalidSession.ID, |
| paddy@78 | 624 }) |
| paddy@78 | 625 result, err = checkCookie(req, testContext) |
| paddy@78 | 626 if err != ErrInvalidSession { |
| paddy@78 | 627 t.Error("Expected ErrInvalidSession, got", err) |
| paddy@78 | 628 } |
| paddy@78 | 629 req, err = http.NewRequest("GET", "https://auth.secondbit.org", nil) |
| paddy@78 | 630 if err != nil { |
| paddy@78 | 631 t.Error("Unexpected error creating base request:", err) |
| paddy@78 | 632 } |
| paddy@78 | 633 req.AddCookie(&http.Cookie{ |
| paddy@78 | 634 Name: authCookieName, |
| paddy@78 | 635 Value: session.ID, |
| paddy@78 | 636 }) |
| paddy@78 | 637 result, err = checkCookie(req, testContext) |
| paddy@78 | 638 if err != nil { |
| paddy@78 | 639 t.Error("Unexpected error:", err) |
| paddy@78 | 640 } |
| paddy@78 | 641 success, field, expectation, outcome := compareSessions(session, result) |
| paddy@78 | 642 if !success { |
| paddy@78 | 643 t.Errorf(`Expected field %s to be %v, but got %v`, field, expectation, outcome) |
| paddy@78 | 644 } |
| paddy@78 | 645 } |
| paddy@78 | 646 |
| paddy@78 | 647 func TestBuildLoginRedirect(t *testing.T) { |
| paddy@78 | 648 t.Parallel() |
| paddy@78 | 649 req, err := http.NewRequest("GET", "https://client.secondbit.org/my/awesome/path?has=query¶ms=to&screw=this&all=up", nil) |
| paddy@78 | 650 if err != nil { |
| paddy@78 | 651 t.Error("Unexpected error creating base request:", err) |
| paddy@78 | 652 } |
| paddy@78 | 653 result := buildLoginRedirect(req, Context{}) |
| paddy@78 | 654 if result != "" { |
| paddy@78 | 655 t.Error("Expected empty string as the result, got", result) |
| paddy@78 | 656 } |
| paddy@78 | 657 uri, err := url.Parse("https://auth.secondbit.org/login?query=string&other=param") |
| paddy@78 | 658 if err != nil { |
| paddy@78 | 659 t.Error("Unexpected error parsing URL:", err) |
| paddy@78 | 660 } |
| paddy@78 | 661 c := Context{loginURI: uri} |
| paddy@78 | 662 result = buildLoginRedirect(req, c) |
| paddy@78 | 663 expectation := "https://auth.secondbit.org/login?from=https%3A%2F%2Fclient.secondbit.org%2Fmy%2Fawesome%2Fpath%3Fhas%3Dquery%26params%3Dto%26screw%3Dthis%26all%3Dup&other=param&query=string" |
| paddy@78 | 664 if result != expectation { |
| paddy@78 | 665 t.Errorf(`Expected result string to be "%s", was "%s"`, expectation, result) |
| paddy@78 | 666 } |
| paddy@78 | 667 } |
| paddy@79 | 668 |
| paddy@79 | 669 func TestAuthenticateHelper(t *testing.T) { |
| paddy@79 | 670 t.Parallel() |
| paddy@79 | 671 store := NewMemstore() |
| paddy@79 | 672 context := Context{ |
| paddy@79 | 673 profiles: store, |
| paddy@79 | 674 } |
| paddy@79 | 675 profile := Profile{ |
| paddy@79 | 676 ID: uuid.NewID(), |
| paddy@79 | 677 Name: "Test User", |
| paddy@116 | 678 Passphrase: "f3a4ac4f1d657b2e6e776d24213e39406d50a87a52691a2a78891425af1271d0", |
| paddy@116 | 679 Iterations: 1, |
| paddy@116 | 680 Salt: "d82d92cfa8bfb5a08270ebbf39a3710d24b352b937fcc8959ebcb40384cc616b", |
| paddy@79 | 681 PassphraseScheme: 1, |
| paddy@79 | 682 Compromised: false, |
| paddy@79 | 683 LockedUntil: time.Time{}, |
| paddy@79 | 684 PassphraseReset: "", |
| paddy@79 | 685 PassphraseResetCreated: time.Time{}, |
| paddy@79 | 686 Created: time.Now(), |
| paddy@79 | 687 LastSeen: time.Time{}, |
| paddy@79 | 688 } |
| paddy@79 | 689 login := Login{ |
| paddy@79 | 690 Type: "email", |
| paddy@79 | 691 Value: "test@example.com", |
| paddy@79 | 692 ProfileID: profile.ID, |
| paddy@79 | 693 Created: time.Now(), |
| paddy@79 | 694 LastUsed: time.Time{}, |
| paddy@79 | 695 } |
| paddy@79 | 696 err := context.SaveProfile(profile) |
| paddy@79 | 697 if err != nil { |
| paddy@79 | 698 t.Error("Error saving profile:", err) |
| paddy@79 | 699 } |
| paddy@79 | 700 err = context.AddLogin(login) |
| paddy@79 | 701 if err != nil { |
| paddy@79 | 702 t.Error("Error adding login:", err) |
| paddy@79 | 703 } |
| paddy@103 | 704 response, err := authenticate("test@example.com", "mysecurepassphrase", context) |
| paddy@79 | 705 if err != nil { |
| paddy@79 | 706 t.Error("Unexpected error:", err) |
| paddy@79 | 707 } |
| paddy@79 | 708 success, field, expectation, result := compareProfiles(profile, response) |
| paddy@79 | 709 if !success { |
| paddy@79 | 710 t.Errorf(`Expected field %s to be "%v", got "%v"`, field, expectation, result) |
| paddy@79 | 711 } |
| paddy@103 | 712 response, err = authenticate("test2@example.com", "mysecurepassphrase", context) |
| paddy@79 | 713 if err != ErrIncorrectAuth { |
| paddy@79 | 714 t.Error("Expected ErrIncorrectAuth, got", err) |
| paddy@79 | 715 } |
| paddy@79 | 716 response, err = authenticate("test@example.com", "not the right password", context) |
| paddy@79 | 717 if err != ErrIncorrectAuth { |
| paddy@79 | 718 t.Error("Expected ErrIncorrectAuth, got", err) |
| paddy@79 | 719 } |
| paddy@79 | 720 scheme := 1000 |
| paddy@79 | 721 phrase := "doesn't really matter, the scheme doesn't exist" |
| paddy@79 | 722 change := ProfileChange{ |
| paddy@79 | 723 PassphraseScheme: &scheme, |
| paddy@79 | 724 Passphrase: &phrase, |
| paddy@79 | 725 } |
| paddy@79 | 726 err = context.UpdateProfile(profile.ID, change) |
| paddy@79 | 727 if err != nil { |
| paddy@79 | 728 t.Error("Unexpected error:", err) |
| paddy@79 | 729 } |
| paddy@79 | 730 response, err = authenticate("test@example.com", "not the right password", context) |
| paddy@79 | 731 if err != ErrInvalidPassphraseScheme { |
| paddy@79 | 732 t.Error("Expected ErrIncorrectAuth, got", err) |
| paddy@79 | 733 } |
| paddy@79 | 734 } |
| paddy@81 | 735 |
| paddy@81 | 736 func TestGetTokenHandler(t *testing.T) { |
| paddy@81 | 737 t.Parallel() |
| paddy@81 | 738 store := NewMemstore() |
| paddy@81 | 739 context := Context{ |
| paddy@87 | 740 clients: store, |
| paddy@87 | 741 authCodes: store, |
| paddy@87 | 742 tokens: store, |
| paddy@81 | 743 } |
| paddy@81 | 744 client := Client{ |
| paddy@81 | 745 ID: uuid.NewID(), |
| paddy@81 | 746 Secret: "sometimes I feel like I don't know what I'm doing", |
| paddy@81 | 747 OwnerID: uuid.NewID(), |
| paddy@81 | 748 Name: "A Super Awesome Client!", |
| paddy@81 | 749 Logo: "https://logos.secondbit.org/client.png", |
| paddy@81 | 750 Website: "https://client.secondbit.org/", |
| paddy@81 | 751 Type: "confidential", |
| paddy@81 | 752 } |
| paddy@87 | 753 authCode := AuthorizationCode{ |
| paddy@81 | 754 Code: "testcode", |
| paddy@81 | 755 Created: time.Now(), |
| paddy@81 | 756 ExpiresIn: 600, |
| paddy@81 | 757 ClientID: client.ID, |
| paddy@81 | 758 Scope: "testscope", |
| paddy@81 | 759 RedirectURI: "https://client.secondbit.org/", |
| paddy@81 | 760 State: "teststate", |
| paddy@81 | 761 ProfileID: uuid.NewID(), |
| paddy@81 | 762 } |
| paddy@87 | 763 err := context.SaveAuthorizationCode(authCode) |
| paddy@81 | 764 if err != nil { |
| paddy@87 | 765 t.Error("Error saving auth code:", err) |
| paddy@81 | 766 } |
| paddy@81 | 767 err = context.SaveClient(client) |
| paddy@81 | 768 if err != nil { |
| paddy@81 | 769 t.Error("Error saving client:", err) |
| paddy@81 | 770 } |
| paddy@81 | 771 data := url.Values{} |
| paddy@81 | 772 data.Set("grant_type", "authorization_code") |
| paddy@87 | 773 data.Set("code", authCode.Code) |
| paddy@87 | 774 data.Set("redirect_uri", authCode.RedirectURI) |
| paddy@81 | 775 body := bytes.NewBufferString(data.Encode()) |
| paddy@81 | 776 req, err := http.NewRequest("POST", "https://auth.secondbit.org/", ioutil.NopCloser(body)) |
| paddy@81 | 777 if err != nil { |
| paddy@81 | 778 t.Error("Error constructing request:", err) |
| paddy@81 | 779 } |
| paddy@81 | 780 req.Header.Set("Content-Type", "application/x-www-form-urlencoded") |
| paddy@81 | 781 req.SetBasicAuth(client.ID.String(), client.Secret) |
| paddy@81 | 782 w := httptest.NewRecorder() |
| paddy@81 | 783 GetTokenHandler(w, req, context) |
| paddy@81 | 784 resp := tokenResponse{} |
| paddy@81 | 785 err = json.Unmarshal(w.Body.Bytes(), &resp) |
| paddy@81 | 786 if err != nil { |
| paddy@81 | 787 t.Error("Error unmarshalling response:", err) |
| paddy@85 | 788 t.Error("Response:", w.Body.String()) |
| paddy@81 | 789 } |
| paddy@81 | 790 if resp.AccessToken == "" { |
| paddy@81 | 791 t.Error("Got blank access token back") |
| paddy@81 | 792 } |
| paddy@81 | 793 if resp.RefreshToken == "" { |
| paddy@81 | 794 t.Error("Got blank refresh token back") |
| paddy@81 | 795 } |
| paddy@81 | 796 if resp.TokenType == "" { |
| paddy@81 | 797 t.Error("Got blank token type back") |
| paddy@81 | 798 } |
| paddy@81 | 799 if resp.ExpiresIn == 0 { |
| paddy@81 | 800 t.Error("Got blank expires in back") |
| paddy@81 | 801 } |
| paddy@87 | 802 tokens, err := context.GetTokensByProfileID(authCode.ProfileID, 1, 0) |
| paddy@81 | 803 if err != nil { |
| paddy@81 | 804 t.Error("Error retrieving token:", err) |
| paddy@81 | 805 } |
| paddy@81 | 806 if len(tokens) != 1 { |
| paddy@85 | 807 t.Fatalf("Expected %d tokens, got %d", 1, len(tokens)) |
| paddy@81 | 808 } |
| paddy@81 | 809 if tokens[0].AccessToken != resp.AccessToken { |
| paddy@81 | 810 t.Errorf(`Expected access token to be "%s", got "%s"`, tokens[0].AccessToken, resp.AccessToken) |
| paddy@81 | 811 } |
| paddy@81 | 812 if tokens[0].RefreshToken != resp.RefreshToken { |
| paddy@81 | 813 t.Errorf(`Expected refresh token to be "%s", got "%s"`, tokens[0].RefreshToken, resp.RefreshToken) |
| paddy@81 | 814 } |
| paddy@81 | 815 if tokens[0].ExpiresIn != resp.ExpiresIn { |
| paddy@81 | 816 t.Errorf(`Expected expires in to be %d, got %d`, tokens[0].ExpiresIn, resp.ExpiresIn) |
| paddy@81 | 817 } |
| paddy@81 | 818 if tokens[0].TokenType != resp.TokenType { |
| paddy@81 | 819 t.Errorf(`Expected token type to be %s, got %s`, tokens[0].TokenType, resp.TokenType) |
| paddy@81 | 820 } |
| paddy@81 | 821 } |