auth

Paddy 2015-03-07 Parent:d30a3a12d387 Child:8267e1c8bcd1

141:a8e6122bfc1a Go to Latest

auth/authcode_test.go

Require authentication to update Clients. Require the Client's owner to supply basic authentication when updating a client.

History
1 package auth
3 import (
4 "bytes"
5 "io/ioutil"
6 "net/http"
7 "net/http/httptest"
8 "net/url"
9 "strings"
10 "testing"
11 "time"
13 "code.secondbit.org/uuid.hg"
14 )
16 var authCodeStores = []authorizationCodeStore{NewMemstore()}
18 func compareAuthorizationCodes(authCode1, authCode2 AuthorizationCode) (success bool, field string, authCode1val, authCode2val interface{}) {
19 if authCode1.Code != authCode2.Code {
20 return false, "code", authCode1.Code, authCode2.Code
21 }
22 if !authCode1.Created.Equal(authCode2.Created) {
23 return false, "created", authCode1.Created, authCode2.Created
24 }
25 if authCode1.ExpiresIn != authCode2.ExpiresIn {
26 return false, "expires in", authCode1.ExpiresIn, authCode2.ExpiresIn
27 }
28 if !authCode1.ClientID.Equal(authCode2.ClientID) {
29 return false, "client ID", authCode1.ClientID, authCode2.ClientID
30 }
31 if len(authCode1.Scopes) != len(authCode2.Scopes) {
32 return false, "scopes", authCode1.Scopes, authCode2.Scopes
33 }
34 for pos, scope := range authCode1.Scopes {
35 if scope != authCode2.Scopes[pos] {
36 return false, "scopes", authCode1.Scopes, authCode2.Scopes
37 }
38 }
39 if authCode1.RedirectURI != authCode2.RedirectURI {
40 return false, "redirect URI", authCode1.RedirectURI, authCode2.RedirectURI
41 }
42 if authCode1.State != authCode2.State {
43 return false, "state", authCode1.State, authCode2.State
44 }
45 if !authCode1.ProfileID.Equal(authCode2.ProfileID) {
46 return false, "profile ID", authCode1.ProfileID, authCode2.ProfileID
47 }
48 if authCode1.Used != authCode2.Used {
49 return false, "used", authCode1.Used, authCode2.Used
50 }
51 return true, "", nil, nil
52 }
54 func TestAuthorizationCodeStore(t *testing.T) {
55 t.Parallel()
56 authCode := AuthorizationCode{
57 Code: "code",
58 Created: time.Now(),
59 ExpiresIn: 180,
60 ClientID: uuid.NewID(),
61 Scopes: []string{"scope"},
62 RedirectURI: "redirectURI",
63 State: "state",
64 }
65 for _, store := range authCodeStores {
66 context := Context{authCodes: store}
67 err := context.SaveAuthorizationCode(authCode)
68 if err != nil {
69 t.Errorf("Error saving auth code to %T: %s", store, err)
70 }
71 err = context.SaveAuthorizationCode(authCode)
72 if err != ErrAuthorizationCodeAlreadyExists {
73 t.Errorf("Expected ErrAuthorizationCodeAlreadyExists from %T, got %+v", store, err)
74 }
75 retrieved, err := context.GetAuthorizationCode(authCode.Code)
76 if err != nil {
77 t.Errorf("Error retrieving auth code from %T: %s", store, err)
78 }
79 match, field, expectation, result := compareAuthorizationCodes(authCode, retrieved)
80 if !match {
81 t.Errorf("Expected `%v` in the `%s` field of auth code retrieved from %T, got `%v`", expectation, field, store, result)
82 }
83 err = context.UseAuthorizationCode(authCode.Code)
84 if err != nil {
85 t.Errorf("Error retrieving auth code from %T: %s", store, err)
86 }
87 retrieved, err = context.GetAuthorizationCode(authCode.Code)
88 if err != nil {
89 t.Errorf("Error retrieving auth code from %T: %s", store, err)
90 }
91 authCode.Used = true
92 match, field, expectation, result = compareAuthorizationCodes(authCode, retrieved)
93 if !match {
94 t.Errorf("Expected `%v` in the `%s` field of auth code retrieved from %T, got `%v`", expectation, field, store, result)
95 }
96 err = context.DeleteAuthorizationCode(authCode.Code)
97 if err != nil {
98 t.Errorf("Error removing auth code from %T: %s", store, err)
99 }
100 retrieved, err = context.GetAuthorizationCode(authCode.Code)
101 if err != ErrAuthorizationCodeNotFound {
102 t.Errorf("Expected ErrAuthorizationCodeNotFound from %T, got %+v and %+v", store, retrieved, err)
103 }
104 err = context.DeleteAuthorizationCode(authCode.Code)
105 if err != ErrAuthorizationCodeNotFound {
106 t.Errorf("Expected ErrAuthorizationCodeNotFound from %T, got %+v", store, err)
107 }
108 err = context.UseAuthorizationCode(authCode.Code)
109 if err != ErrAuthorizationCodeNotFound {
110 t.Errorf("Expected ErrAuthorizationCodeNotFound from %T, got %+v", store, err)
111 }
112 }
113 }
115 func TestAuthCodeGrantValidate(t *testing.T) {
116 t.Parallel()
117 store := NewMemstore()
118 testContext := Context{
119 clients: store,
120 authCodes: store,
121 profiles: store,
122 tokens: store,
123 sessions: store,
124 }
125 client := Client{
126 ID: uuid.NewID(),
127 Secret: "super secret!",
128 OwnerID: uuid.NewID(),
129 Name: "My test client",
130 Logo: "https://secondbit.org/logo.png",
131 Website: "https://secondbit.org/",
132 Type: "public",
133 }
134 endpoint := Endpoint{
135 ID: uuid.NewID(),
136 ClientID: client.ID,
137 URI: "https://test.secondbit.org/redirect",
138 Added: time.Now(),
139 }
140 err := testContext.SaveClient(client)
141 if err != nil {
142 t.Fatal("Can't store client:", err)
143 }
144 err = testContext.AddEndpoints(client.ID, []Endpoint{endpoint})
145 if err != nil {
146 t.Fatal("Can't store endpoint:", err)
147 }
148 code := AuthorizationCode{
149 Code: "myauthcode",
150 Created: time.Now(),
151 ExpiresIn: 180,
152 ClientID: uuid.NewID(),
153 Scopes: []string{"scope"},
154 RedirectURI: "redirectURI",
155 State: "state",
156 }
157 err = testContext.SaveAuthorizationCode(code)
158 if err != nil {
159 t.Fatal("Can't add auth code:", err)
160 }
161 code2 := code
162 code2.Code = "otherauthcode"
163 code2.ClientID = client.ID
164 err = testContext.SaveAuthorizationCode(code2)
165 if err != nil {
166 t.Fatal("Can't add second auth code:", err)
167 }
168 req, err := http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
169 if err != nil {
170 t.Fatal("Can't build request:", err)
171 }
172 req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
173 w := httptest.NewRecorder()
174 params := url.Values{}
175 body := bytes.NewBufferString(params.Encode())
176 req.Body = ioutil.NopCloser(body)
177 scope, profileID, valid := authCodeGrantValidate(w, req, testContext)
178 if valid {
179 t.Fatalf("Expected invalid auth code, got scope `%s` and profileID `%s`.", scope, profileID)
180 }
181 if w.Code != http.StatusBadRequest {
182 t.Errorf("Expected status %d, got %d", http.StatusBadRequest, w.Code)
183 }
184 expectedBody := `{"error":"invalid_request"}`
185 if strings.TrimSpace(w.Body.String()) != expectedBody {
186 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String()))
187 }
189 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
190 if err != nil {
191 t.Fatal("Can't build request:", err)
192 }
193 req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
194 w = httptest.NewRecorder()
195 params = url.Values{}
196 params.Set("code", "notmycode")
197 body = bytes.NewBufferString(params.Encode())
198 req.Body = ioutil.NopCloser(body)
199 err = req.ParseForm()
200 if err != nil {
201 t.Log(err)
202 }
203 scope, profileID, valid = authCodeGrantValidate(w, req, testContext)
204 if valid {
205 t.Fatalf("Expected invalid auth code, got scope `%s` and profileID `%s`.", scope, profileID)
206 }
207 if w.Code != http.StatusUnauthorized {
208 t.Errorf("Expected status %d, got %d", http.StatusUnauthorized, w.Code)
209 }
210 expectedBody = `{"error":"invalid_client"}`
211 if expectedBody != strings.TrimSpace(w.Body.String()) {
212 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String()))
213 }
215 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
216 if err != nil {
217 t.Fatal("Can't build request:", err)
218 }
219 req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
220 req.SetBasicAuth(client.ID.String(), client.Secret)
221 w = httptest.NewRecorder()
222 params = url.Values{}
223 params.Set("code", "notmycode")
224 body = bytes.NewBufferString(params.Encode())
225 req.Body = ioutil.NopCloser(body)
226 err = req.ParseForm()
227 if err != nil {
228 t.Log(err)
229 }
230 scope, profileID, valid = authCodeGrantValidate(w, req, testContext)
231 if valid {
232 t.Fatalf("Expected invalid auth code, got scope `%s` and profileID `%s`.", scope, profileID)
233 }
234 if w.Code != http.StatusBadRequest {
235 t.Errorf("Expected status %d, got %d", http.StatusUnauthorized, w.Code)
236 }
237 expectedBody = `{"error":"invalid_grant"}`
238 if expectedBody != strings.TrimSpace(w.Body.String()) {
239 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String()))
240 }
242 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
243 if err != nil {
244 t.Fatal("Can't build request:", err)
245 }
246 req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
247 req.SetBasicAuth(client.ID.String(), client.Secret)
248 w = httptest.NewRecorder()
249 params = url.Values{}
250 params.Set("code", code.Code)
251 params.Set("redirect_uri", "not my redirectURI")
252 body = bytes.NewBufferString(params.Encode())
253 req.Body = ioutil.NopCloser(body)
254 err = req.ParseForm()
255 if err != nil {
256 t.Log(err)
257 }
258 scope, profileID, valid = authCodeGrantValidate(w, req, testContext)
259 if valid {
260 t.Fatalf("Expected invalid auth code, got scope `%s` and profileID `%s`.", scope, profileID)
261 }
262 if w.Code != http.StatusBadRequest {
263 t.Errorf("Expected status %d, got %d", http.StatusUnauthorized, w.Code)
264 }
265 expectedBody = `{"error":"invalid_grant"}`
266 if expectedBody != strings.TrimSpace(w.Body.String()) {
267 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String()))
268 }
270 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
271 if err != nil {
272 t.Fatal("Can't build request:", err)
273 }
274 req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
275 req.SetBasicAuth(client.ID.String(), client.Secret)
276 w = httptest.NewRecorder()
277 params = url.Values{}
278 params.Set("code", code.Code)
279 params.Set("redirect_uri", code.RedirectURI)
280 body = bytes.NewBufferString(params.Encode())
281 req.Body = ioutil.NopCloser(body)
282 err = req.ParseForm()
283 if err != nil {
284 t.Log(err)
285 }
286 scope, profileID, valid = authCodeGrantValidate(w, req, testContext)
287 if valid {
288 t.Fatalf("Expected invalid auth code, got scope `%s` and profileID `%s`.", scope, profileID)
289 }
290 if w.Code != http.StatusBadRequest {
291 t.Errorf("Expected status %d, got %d", http.StatusUnauthorized, w.Code)
292 }
293 expectedBody = `{"error":"invalid_grant"}`
294 if expectedBody != strings.TrimSpace(w.Body.String()) {
295 t.Errorf("Expected body of `%s`, got `%s`", expectedBody, strings.TrimSpace(w.Body.String()))
296 }
298 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
299 if err != nil {
300 t.Fatal("Can't build request:", err)
301 }
302 req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
303 req.SetBasicAuth(client.ID.String(), client.Secret)
304 w = httptest.NewRecorder()
305 params = url.Values{}
306 params.Set("code", code2.Code)
307 params.Set("redirect_uri", code2.RedirectURI)
308 body = bytes.NewBufferString(params.Encode())
309 req.Body = ioutil.NopCloser(body)
310 err = req.ParseForm()
311 if err != nil {
312 t.Log(err)
313 }
314 scope, profileID, valid = authCodeGrantValidate(w, req, testContext)
315 if !valid {
316 t.Fatalf("Expected valid auth code, was not valid.")
317 }
318 }
320 func TestAuthCodeGrantInvalidate(t *testing.T) {
321 t.Parallel()
322 store := NewMemstore()
323 testContext := Context{
324 clients: store,
325 authCodes: store,
326 profiles: store,
327 tokens: store,
328 sessions: store,
329 }
330 code := AuthorizationCode{
331 Code: "myauthcode",
332 Created: time.Now(),
333 ExpiresIn: 180,
334 ClientID: uuid.NewID(),
335 Scopes: []string{"scope"},
336 RedirectURI: "redirectURI",
337 State: "state",
338 }
339 err := testContext.SaveAuthorizationCode(code)
340 if err != nil {
341 t.Fatal("Can't add auth code:", err)
342 }
343 req, err := http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
344 if err != nil {
345 t.Fatal("Can't build request:", err)
346 }
347 err = authCodeGrantInvalidate(req, testContext)
348 if err != ErrAuthorizationCodeNotFound {
349 t.Errorf("Expected `%s`, got `%+v`", ErrAuthorizationCodeNotFound, err)
350 }
351 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
352 if err != nil {
353 t.Fatal("Can't build request:", err)
354 }
355 req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
356 params := url.Values{}
357 params.Set("code", "notmycode")
358 body := bytes.NewBufferString(params.Encode())
359 req.Body = ioutil.NopCloser(body)
360 err = authCodeGrantInvalidate(req, testContext)
361 if err != ErrAuthorizationCodeNotFound {
362 t.Errorf("Expected `%s`, got `%+v`", ErrAuthorizationCodeNotFound, err)
363 }
364 req, err = http.NewRequest("POST", "https://test.auth.secondbit.org/oauth2/grant", nil)
365 if err != nil {
366 t.Fatal("Can't build request:", err)
367 }
368 req.Header.Set("Content-Type", "application/x-www-form-urlencoded")
369 params.Set("code", code.Code)
370 body = bytes.NewBufferString(params.Encode())
371 req.Body = ioutil.NopCloser(body)
372 err = authCodeGrantInvalidate(req, testContext)
373 if err != nil {
374 t.Error("Error invalidating auth code:", err)
375 }
376 authCode, err := testContext.GetAuthorizationCode(code.Code)
377 if err != nil {
378 t.Error("Error retrieving auth code:", err)
379 }
380 if !authCode.Used {
381 t.Error("Expected auth code to be used, was not.")
382 }
383 }