this repo has no description

clean up auth code

Changed files
+142 -68
legit
+6 -17
legit/routes/auth.go
··· 7 7 8 8 comatproto "github.com/bluesky-social/indigo/api/atproto" 9 9 "github.com/bluesky-social/indigo/xrpc" 10 + rauth "github.com/icyphox/bild/legit/routes/auth" 10 11 ) 11 12 12 13 const ( ··· 19 20 auth, ok := session.Values["authenticated"].(bool) 20 21 21 22 if !ok || !auth { 22 - http.Error(w, "Forbidden: You are not logged in", http.StatusForbidden) 23 + http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) 23 24 return 24 25 } 25 26 ··· 43 44 }, 44 45 } 45 46 atSession, err := comatproto.ServerRefreshSession(r.Context(), &client) 46 - 47 47 if err != nil { 48 48 log.Println(err) 49 - http.Error(w, "Internal Server Error", http.StatusInternalServerError) 49 + h.Write500(w) 50 50 return 51 51 } 52 52 53 - clientSession, _ := h.s.Get(r, "bild-session") 54 - clientSession.Values["handle"] = atSession.Handle 55 - clientSession.Values["did"] = atSession.Did 56 - clientSession.Values["accessJwt"] = atSession.AccessJwt 57 - clientSession.Values["refreshJwt"] = atSession.RefreshJwt 58 - clientSession.Values["expiry"] = time.Now().Add(time.Hour).String() 59 - clientSession.Values["pds"] = pdsUrl 60 - clientSession.Values["authenticated"] = true 61 - 62 - err = clientSession.Save(r, w) 63 - 53 + err = h.auth.StoreSession(r, w, nil, &rauth.AtSessionRefresh{ServerRefreshSession_Output: *atSession, PDSEndpoint: pdsUrl}) 64 54 if err != nil { 65 - log.Printf("failed to store session for did: %s\n", atSession.Did) 66 - log.Println(err) 67 - http.Error(w, "Internal Server Error", http.StatusInternalServerError) 55 + log.Printf("failed to store session for did: %s\n: %s", atSession.Did, err) 56 + h.Write500(w) 68 57 return 69 58 } 70 59
+98
legit/routes/auth/auth.go
··· 1 + package auth 2 + 3 + import ( 4 + "context" 5 + "fmt" 6 + "net/http" 7 + "time" 8 + 9 + comatproto "github.com/bluesky-social/indigo/api/atproto" 10 + "github.com/bluesky-social/indigo/atproto/identity" 11 + "github.com/bluesky-social/indigo/atproto/syntax" 12 + "github.com/bluesky-social/indigo/xrpc" 13 + "github.com/gorilla/sessions" 14 + ) 15 + 16 + type Auth struct { 17 + s sessions.Store 18 + } 19 + 20 + func NewAuth(store sessions.Store) *Auth { 21 + return &Auth{store} 22 + } 23 + 24 + func resolveIdent(ctx context.Context, arg string) (*identity.Identity, error) { 25 + id, err := syntax.ParseAtIdentifier(arg) 26 + if err != nil { 27 + return nil, err 28 + } 29 + 30 + dir := identity.DefaultDirectory() 31 + return dir.Lookup(ctx, *id) 32 + } 33 + 34 + func (a *Auth) CreateInitialSession(w http.ResponseWriter, r *http.Request, username, appPassword string) (AtSessionCreate, error) { 35 + ctx := r.Context() 36 + resolved, err := resolveIdent(ctx, username) 37 + if err != nil { 38 + return AtSessionCreate{}, fmt.Errorf("invalid handle: %s", err) 39 + } 40 + 41 + pdsUrl := resolved.PDSEndpoint() 42 + client := xrpc.Client{ 43 + Host: pdsUrl, 44 + } 45 + 46 + atSession, err := comatproto.ServerCreateSession(ctx, &client, &comatproto.ServerCreateSession_Input{ 47 + Identifier: resolved.DID.String(), 48 + Password: appPassword, 49 + }) 50 + if err != nil { 51 + return AtSessionCreate{}, fmt.Errorf("invalid app password") 52 + } 53 + 54 + return AtSessionCreate{ 55 + ServerCreateSession_Output: *atSession, 56 + PDSEndpoint: pdsUrl, 57 + }, nil 58 + } 59 + 60 + func (a *Auth) StoreSession(r *http.Request, w http.ResponseWriter, atSessionCreate *AtSessionCreate, atSessionRefresh *AtSessionRefresh) error { 61 + if atSessionCreate != nil { 62 + atSession := atSessionCreate 63 + 64 + clientSession, _ := a.s.Get(r, "bild-session") 65 + clientSession.Values["handle"] = atSession.Handle 66 + clientSession.Values["did"] = atSession.Did 67 + clientSession.Values["accessJwt"] = atSession.AccessJwt 68 + clientSession.Values["refreshJwt"] = atSession.RefreshJwt 69 + clientSession.Values["expiry"] = time.Now().Add(time.Hour).String() 70 + clientSession.Values["pds"] = atSession.PDSEndpoint 71 + clientSession.Values["authenticated"] = true 72 + 73 + return clientSession.Save(r, w) 74 + } else { 75 + atSession := atSessionRefresh 76 + 77 + clientSession, _ := a.s.Get(r, "bild-session") 78 + clientSession.Values["handle"] = atSession.Handle 79 + clientSession.Values["did"] = atSession.Did 80 + clientSession.Values["accessJwt"] = atSession.AccessJwt 81 + clientSession.Values["refreshJwt"] = atSession.RefreshJwt 82 + clientSession.Values["expiry"] = time.Now().Add(time.Hour).String() 83 + clientSession.Values["pds"] = atSession.PDSEndpoint 84 + clientSession.Values["authenticated"] = true 85 + 86 + return clientSession.Save(r, w) 87 + } 88 + } 89 + 90 + func (a *Auth) GetSessionUser(r *http.Request) (*identity.Identity, error) { 91 + session, _ := a.s.Get(r, "bild-session") 92 + did, ok := session.Values["did"].(string) 93 + if !ok { 94 + return nil, fmt.Errorf("user is not authenticated") 95 + } 96 + 97 + return resolveIdent(r.Context(), did) 98 + }
+15
legit/routes/auth/types.go
··· 1 + package auth 2 + 3 + import ( 4 + comatproto "github.com/bluesky-social/indigo/api/atproto" 5 + ) 6 + 7 + type AtSessionCreate struct { 8 + comatproto.ServerCreateSession_Output 9 + PDSEndpoint string 10 + } 11 + 12 + type AtSessionRefresh struct { 13 + comatproto.ServerRefreshSession_Output 14 + PDSEndpoint string 15 + }
+8 -4
legit/routes/handler.go
··· 11 11 "github.com/gorilla/sessions" 12 12 "github.com/icyphox/bild/legit/config" 13 13 "github.com/icyphox/bild/legit/db" 14 + "github.com/icyphox/bild/legit/routes/auth" 14 15 "github.com/icyphox/bild/legit/routes/tmpl" 15 16 ) 16 17 ··· 44 45 return nil, fmt.Errorf("failed to load templates: %w", err) 45 46 } 46 47 48 + auth := auth.NewAuth(s) 49 + 47 50 db, err := db.Setup(c.Server.DBPath) 48 51 if err != nil { 49 52 return nil, fmt.Errorf("failed to setup db: %w", err) 50 53 } 51 54 52 55 h := Handle{ 53 - c: c, 54 - t: t, 55 - s: s, 56 - db: db, 56 + c: c, 57 + t: t, 58 + s: s, 59 + db: db, 60 + auth: auth, 57 61 } 58 62 59 63 r.Get("/login", h.Login)
+15 -47
legit/routes/routes.go
··· 2 2 3 3 import ( 4 4 "compress/gzip" 5 - "context" 6 5 "errors" 7 6 "fmt" 8 7 "html/template" ··· 15 14 "strings" 16 15 "time" 17 16 18 - comatproto "github.com/bluesky-social/indigo/api/atproto" 19 - "github.com/bluesky-social/indigo/atproto/identity" 20 - "github.com/bluesky-social/indigo/atproto/syntax" 21 - "github.com/bluesky-social/indigo/xrpc" 22 17 "github.com/dustin/go-humanize" 23 18 "github.com/go-chi/chi/v5" 24 19 "github.com/go-git/go-git/v5/plumbing" ··· 26 21 "github.com/icyphox/bild/legit/config" 27 22 "github.com/icyphox/bild/legit/db" 28 23 "github.com/icyphox/bild/legit/git" 24 + "github.com/icyphox/bild/legit/routes/auth" 29 25 "github.com/russross/blackfriday/v2" 30 26 "golang.org/x/crypto/ssh" 31 27 ) 32 28 33 29 type Handle struct { 34 - c *config.Config 35 - t *template.Template 36 - s *sessions.CookieStore 37 - db *db.DB 30 + c *config.Config 31 + t *template.Template 32 + s *sessions.CookieStore 33 + db *db.DB 34 + auth *auth.Auth 38 35 } 39 36 40 37 func (h *Handle) Index(w http.ResponseWriter, r *http.Request) { ··· 440 437 http.ServeFile(w, r, f) 441 438 } 442 439 443 - func resolveIdent(ctx context.Context, arg string) (*identity.Identity, error) { 444 - id, err := syntax.ParseAtIdentifier(arg) 445 - if err != nil { 446 - return nil, err 447 - } 448 - 449 - dir := identity.DefaultDirectory() 450 - return dir.Lookup(ctx, *id) 451 - } 452 - 453 440 func (h *Handle) Login(w http.ResponseWriter, r *http.Request) { 454 441 switch r.Method { 455 442 case http.MethodGet: ··· 458 445 return 459 446 } 460 447 case http.MethodPost: 461 - ctx := r.Context() 462 448 username := r.FormValue("username") 463 449 appPassword := r.FormValue("app_password") 464 450 465 - resolved, err := resolveIdent(ctx, username) 451 + atSession, err := h.auth.CreateInitialSession(w, r, username, appPassword) 466 452 if err != nil { 467 - http.Error(w, "invalid `handle`", http.StatusBadRequest) 453 + h.WriteOOBNotice(w, "login", "Invalid username or app password.") 454 + log.Printf("creating initial session: %s", err) 468 455 return 469 456 } 470 457 471 - pdsUrl := resolved.PDSEndpoint() 472 - client := xrpc.Client{ 473 - Host: pdsUrl, 474 - } 475 - 476 - atSession, err := comatproto.ServerCreateSession(ctx, &client, &comatproto.ServerCreateSession_Input{ 477 - Identifier: resolved.DID.String(), 478 - Password: appPassword, 479 - }) 480 - 481 - clientSession, _ := h.s.Get(r, "bild-session") 482 - clientSession.Values["handle"] = atSession.Handle 483 - clientSession.Values["did"] = atSession.Did 484 - clientSession.Values["accessJwt"] = atSession.AccessJwt 485 - clientSession.Values["refreshJwt"] = atSession.RefreshJwt 486 - clientSession.Values["expiry"] = time.Now().Add(time.Hour).String() 487 - clientSession.Values["pds"] = pdsUrl 488 - clientSession.Values["authenticated"] = true 489 - 490 - err = clientSession.Save(r, w) 491 - 458 + err = h.auth.StoreSession(r, w, &atSession, nil) 492 459 if err != nil { 493 - log.Printf("failed to store session for did: %s\n", atSession.Did) 494 - log.Println(err) 460 + h.WriteOOBNotice(w, "login", "Failed to store session.") 461 + log.Printf("storing session: %s", err) 495 462 return 496 463 } 497 464 498 465 log.Printf("successfully saved session for %s (%s)", atSession.Handle, atSession.Did) 499 - http.Redirect(w, r, "/@"+atSession.Handle, 302) 466 + w.Header().Set("HX-Redirect", "/") 467 + w.WriteHeader(http.StatusOK) 500 468 } 501 469 } 502 470 ··· 508 476 case http.MethodGet: 509 477 keys, err := h.db.GetPublicKeys(did) 510 478 if err != nil { 479 + h.WriteOOBNotice(w, "keys", "Failed to list keys. Try again later.") 511 480 log.Println(err) 512 - http.Error(w, "invalid `did`", http.StatusBadRequest) 513 481 return 514 482 } 515 483