rastrillo / native Public

Clone
git clone https://amadan.net/rastrillo/native

Plain git — no account needed to clone.

Download

Download this file

1package nativeauth
2
3import (
4 "encoding/json"
5 "github.com/carlosframework/rastrillo/db"
6 "github.com/carlosframework/rastrillo/migrate"
7 "github.com/carlosframework/rastrillo/sessions"
8 "net/http"
9 "net/http/httptest"
10 "path/filepath"
11 "strings"
12 "testing"
13 "time"
14)
15
16func TestCallbackIsFixedAndContainsNoSession(t *testing.T) {
17 d, err := db.Open(filepath.Join(t.TempDir(), "sessions.db"), nil)
18 if err != nil {
19 t.Fatal(err)
20 }
21 defer d.Close()
22 if _, err = migrate.Apply(t.Context(), d, sessions.Schema); err != nil {
23 t.Fatal(err)
24 }
25 s, err := sessions.New(sessions.Config{DB: d.Writer(), Origin: "https://app.test"})
26 if err != nil {
27 t.Fatal(err)
28 }
29 a := New(s, "https://app.test", "Docs", s.Require)
30 for _, callback := range []string{"https://evil.test", "works.oficina.sheets://signin", "works.oficina.docs://signin?state=evil", "works.oficina.docs://signin/", "javascript:alert(1)"} {
31 data, _ := json.Marshal(map[string]string{"challenge": token(), "callback": callback})
32 req := httptest.NewRequest("POST", "https://app.test/native/request", strings.NewReader(string(data)))
33 req.Header.Set("Content-Type", "application/json")
34 req.Header.Set("Origin", "https://app.test")
35 w := httptest.NewRecorder()
36 a.ServeHTTP(w, req)
37 if w.Code != 400 {
38 t.Fatalf("callback %q status%d", callback, w.Code)
39 }
40 }
41 for _, callback := range []string{"", "works.oficina.docs://signin"} {
42 id := token()
43 a.pending[id] = &request{challenge: token(), callback: callback, expires: time.Now().Add(time.Minute)}
44 cookie, err := s.Mint(sessions.Session{Subject: "alice"})
45 if err != nil {
46 t.Fatal(err)
47 }
48 req := httptest.NewRequest("POST", "https://app.test/native/authorize?id="+id, nil)
49 req.Header.Set("Origin", "https://app.test")
50 req.AddCookie(&http.Cookie{Name: s.CookieName(), Value: cookie})
51 w := httptest.NewRecorder()
52 s.Middleware(a).ServeHTTP(w, req)
53 if w.Code != 200 {
54 t.Fatal(w.Code)
55 }
56 body := w.Body.String()
57 if strings.Contains(body, cookie) || strings.Contains(body, "#ZgotmplZ") {
58 t.Fatal("unsafe or broken callback")
59 }
60 if callback != "" && !strings.Contains(body, `href="works.oficina.docs://signin?state=`+id+`"`) {
61 t.Fatal("missing bound return link")
62 }
63 if callback == "" && strings.Contains(body, `http-equiv="refresh"`) {
64 t.Fatal("legacy flow redirected")
65 }
66 }
67}
68