this repo has no description

clean up auth code

Changed files
+142 -68
legit
+6 -17
legit/routes/auth.go
··· 7 8 comatproto "github.com/bluesky-social/indigo/api/atproto" 9 "github.com/bluesky-social/indigo/xrpc" 10 ) 11 12 const ( ··· 19 auth, ok := session.Values["authenticated"].(bool) 20 21 if !ok || !auth { 22 - http.Error(w, "Forbidden: You are not logged in", http.StatusForbidden) 23 return 24 } 25 ··· 43 }, 44 } 45 atSession, err := comatproto.ServerRefreshSession(r.Context(), &client) 46 - 47 if err != nil { 48 log.Println(err) 49 - http.Error(w, "Internal Server Error", http.StatusInternalServerError) 50 return 51 } 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 - 64 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) 68 return 69 } 70
··· 7 8 comatproto "github.com/bluesky-social/indigo/api/atproto" 9 "github.com/bluesky-social/indigo/xrpc" 10 + rauth "github.com/icyphox/bild/legit/routes/auth" 11 ) 12 13 const ( ··· 20 auth, ok := session.Values["authenticated"].(bool) 21 22 if !ok || !auth { 23 + http.Redirect(w, r, "/login", http.StatusTemporaryRedirect) 24 return 25 } 26 ··· 44 }, 45 } 46 atSession, err := comatproto.ServerRefreshSession(r.Context(), &client) 47 if err != nil { 48 log.Println(err) 49 + h.Write500(w) 50 return 51 } 52 53 + err = h.auth.StoreSession(r, w, nil, &rauth.AtSessionRefresh{ServerRefreshSession_Output: *atSession, PDSEndpoint: pdsUrl}) 54 if err != nil { 55 + log.Printf("failed to store session for did: %s\n: %s", atSession.Did, err) 56 + h.Write500(w) 57 return 58 } 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 "github.com/gorilla/sessions" 12 "github.com/icyphox/bild/legit/config" 13 "github.com/icyphox/bild/legit/db" 14 "github.com/icyphox/bild/legit/routes/tmpl" 15 ) 16 ··· 44 return nil, fmt.Errorf("failed to load templates: %w", err) 45 } 46 47 db, err := db.Setup(c.Server.DBPath) 48 if err != nil { 49 return nil, fmt.Errorf("failed to setup db: %w", err) 50 } 51 52 h := Handle{ 53 - c: c, 54 - t: t, 55 - s: s, 56 - db: db, 57 } 58 59 r.Get("/login", h.Login)
··· 11 "github.com/gorilla/sessions" 12 "github.com/icyphox/bild/legit/config" 13 "github.com/icyphox/bild/legit/db" 14 + "github.com/icyphox/bild/legit/routes/auth" 15 "github.com/icyphox/bild/legit/routes/tmpl" 16 ) 17 ··· 45 return nil, fmt.Errorf("failed to load templates: %w", err) 46 } 47 48 + auth := auth.NewAuth(s) 49 + 50 db, err := db.Setup(c.Server.DBPath) 51 if err != nil { 52 return nil, fmt.Errorf("failed to setup db: %w", err) 53 } 54 55 h := Handle{ 56 + c: c, 57 + t: t, 58 + s: s, 59 + db: db, 60 + auth: auth, 61 } 62 63 r.Get("/login", h.Login)
+15 -47
legit/routes/routes.go
··· 2 3 import ( 4 "compress/gzip" 5 - "context" 6 "errors" 7 "fmt" 8 "html/template" ··· 15 "strings" 16 "time" 17 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 "github.com/dustin/go-humanize" 23 "github.com/go-chi/chi/v5" 24 "github.com/go-git/go-git/v5/plumbing" ··· 26 "github.com/icyphox/bild/legit/config" 27 "github.com/icyphox/bild/legit/db" 28 "github.com/icyphox/bild/legit/git" 29 "github.com/russross/blackfriday/v2" 30 "golang.org/x/crypto/ssh" 31 ) 32 33 type Handle struct { 34 - c *config.Config 35 - t *template.Template 36 - s *sessions.CookieStore 37 - db *db.DB 38 } 39 40 func (h *Handle) Index(w http.ResponseWriter, r *http.Request) { ··· 440 http.ServeFile(w, r, f) 441 } 442 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 func (h *Handle) Login(w http.ResponseWriter, r *http.Request) { 454 switch r.Method { 455 case http.MethodGet: ··· 458 return 459 } 460 case http.MethodPost: 461 - ctx := r.Context() 462 username := r.FormValue("username") 463 appPassword := r.FormValue("app_password") 464 465 - resolved, err := resolveIdent(ctx, username) 466 if err != nil { 467 - http.Error(w, "invalid `handle`", http.StatusBadRequest) 468 return 469 } 470 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 - 492 if err != nil { 493 - log.Printf("failed to store session for did: %s\n", atSession.Did) 494 - log.Println(err) 495 return 496 } 497 498 log.Printf("successfully saved session for %s (%s)", atSession.Handle, atSession.Did) 499 - http.Redirect(w, r, "/@"+atSession.Handle, 302) 500 } 501 } 502 ··· 508 case http.MethodGet: 509 keys, err := h.db.GetPublicKeys(did) 510 if err != nil { 511 log.Println(err) 512 - http.Error(w, "invalid `did`", http.StatusBadRequest) 513 return 514 } 515
··· 2 3 import ( 4 "compress/gzip" 5 "errors" 6 "fmt" 7 "html/template" ··· 14 "strings" 15 "time" 16 17 "github.com/dustin/go-humanize" 18 "github.com/go-chi/chi/v5" 19 "github.com/go-git/go-git/v5/plumbing" ··· 21 "github.com/icyphox/bild/legit/config" 22 "github.com/icyphox/bild/legit/db" 23 "github.com/icyphox/bild/legit/git" 24 + "github.com/icyphox/bild/legit/routes/auth" 25 "github.com/russross/blackfriday/v2" 26 "golang.org/x/crypto/ssh" 27 ) 28 29 type Handle struct { 30 + c *config.Config 31 + t *template.Template 32 + s *sessions.CookieStore 33 + db *db.DB 34 + auth *auth.Auth 35 } 36 37 func (h *Handle) Index(w http.ResponseWriter, r *http.Request) { ··· 437 http.ServeFile(w, r, f) 438 } 439 440 func (h *Handle) Login(w http.ResponseWriter, r *http.Request) { 441 switch r.Method { 442 case http.MethodGet: ··· 445 return 446 } 447 case http.MethodPost: 448 username := r.FormValue("username") 449 appPassword := r.FormValue("app_password") 450 451 + atSession, err := h.auth.CreateInitialSession(w, r, username, appPassword) 452 if err != nil { 453 + h.WriteOOBNotice(w, "login", "Invalid username or app password.") 454 + log.Printf("creating initial session: %s", err) 455 return 456 } 457 458 + err = h.auth.StoreSession(r, w, &atSession, nil) 459 if err != nil { 460 + h.WriteOOBNotice(w, "login", "Failed to store session.") 461 + log.Printf("storing session: %s", err) 462 return 463 } 464 465 log.Printf("successfully saved session for %s (%s)", atSession.Handle, atSession.Did) 466 + w.Header().Set("HX-Redirect", "/") 467 + w.WriteHeader(http.StatusOK) 468 } 469 } 470 ··· 476 case http.MethodGet: 477 keys, err := h.db.GetPublicKeys(did) 478 if err != nil { 479 + h.WriteOOBNotice(w, "keys", "Failed to list keys. Try again later.") 480 log.Println(err) 481 return 482 } 483