auth

Paddy 2015-05-12 Parent:73e12d5a1124 Child:b7e685839a1b

167:0ff23f3a4ede Go to Latest

auth/authcode_test.go

Implement an endpoint for token information. Implement an endpoint that allows us to look up information on a token. We strip the refresh token before the response is sent to avoid leaking the response token.

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