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