ai cooking
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

refactor more into auth make enable mocks still work

+96 -49
+5 -31
cmd/careme/web.go
··· 36 36 return fmt.Errorf("failed to create cache: %w", err) 37 37 } 38 38 39 - authClient, err := auth.NewClient(cfg.Clerk.SecretKey) 39 + authClient, err := auth.NewFromConfig(cfg) 40 40 if err != nil { 41 - return fmt.Errorf("failed to create clerk client: %w", err) 41 + return fmt.Errorf("failed to create auth client: %w", err) 42 42 } 43 + 44 + mux := http.NewServeMux() 45 + authClient.Register(mux) 43 46 44 47 userStorage := users.NewStorage(cache) 45 48 ··· 47 50 if err != nil { 48 51 return fmt.Errorf("failed to create recipe generator: %w", err) 49 52 } 50 - mux := http.NewServeMux() 51 53 mux.HandleFunc("/static/tailwind.css", func(w http.ResponseWriter, r *http.Request) { 52 54 w.Header().Set("Content-Type", "text/css; charset=utf-8") 53 55 w.Header().Set("Cache-Control", "public, max-age=31536000, immutable") ··· 110 112 slog.ErrorContext(ctx, "home template execute error", "error", err) 111 113 http.Error(w, "template error", http.StatusInternalServerError) 112 114 } 113 - }) 114 - 115 - //TODO move signin/up/auth/establish/logout to auth package 116 - mux.HandleFunc("/sign-in", func(w http.ResponseWriter, r *http.Request) { 117 - http.Redirect(w, r, cfg.Clerk.Signin(), http.StatusSeeOther) 118 - }) 119 - mux.HandleFunc("/sign-up", func(w http.ResponseWriter, r *http.Request) { 120 - http.Redirect(w, r, cfg.Clerk.Signup(), http.StatusSeeOther) 121 - }) 122 - mux.HandleFunc("/auth/establish", func(w http.ResponseWriter, r *http.Request) { 123 - if cfg.Clerk.PublishableKey == "" { 124 - http.Error(w, "clerk publishable key missing", http.StatusInternalServerError) 125 - return 126 - } 127 - w.Header().Set("Content-Type", "text/html; charset=utf-8") 128 - data := struct { 129 - PublishableKey string 130 - }{ 131 - PublishableKey: cfg.Clerk.PublishableKey, 132 - } 133 - if err := templates.AuthEstablish.Execute(w, data); err != nil { 134 - slog.ErrorContext(r.Context(), "auth establish template execute error", "error", err) 135 - http.Error(w, "template error", http.StatusInternalServerError) 136 - } 137 - }) 138 - 139 - mux.HandleFunc("/logout", func(w http.ResponseWriter, r *http.Request) { 140 - authClient.Logout(w, r) 141 115 }) 142 116 143 117 mux.HandleFunc("/ready", func(w http.ResponseWriter, r *http.Request) {
+66 -18
internal/auth/clerk.go
··· 1 1 package auth 2 2 3 3 import ( 4 + "careme/internal/config" 5 + "careme/internal/templates" 4 6 "context" 5 7 "errors" 6 8 "fmt" ··· 10 12 11 13 "github.com/clerk/clerk-sdk-go/v2" 12 14 clerkhttp "github.com/clerk/clerk-sdk-go/v2/http" 15 + "github.com/clerk/clerk-sdk-go/v2/jwks" 13 16 "github.com/clerk/clerk-sdk-go/v2/session" 14 17 "github.com/clerk/clerk-sdk-go/v2/user" 15 18 ) ··· 18 21 ErrNoSession = errors.New("no valid session found") 19 22 ) 20 23 24 + type AuthClient interface { 25 + GetUserEmail(ctx context.Context, clerkUserID string) (string, error) 26 + GetUserIDFromRequest(r *http.Request) (string, error) 27 + WithAuthHTTP(handler http.Handler) http.Handler 28 + Register(mux *http.ServeMux) 29 + } 30 + 21 31 // Client wraps Clerk SDK functionality 22 32 // todo private 23 33 type clerkClient struct { 24 - secretKey string 25 - } 26 - 27 - type AuthClient interface { 28 - GetUserEmail(ctx context.Context, clerkUserID string) (string, error) 29 - GetUserIDFromRequest(r *http.Request) (string, error) 34 + cfg *config.Config 35 + userClient *user.Client 36 + sessionClient *session.Client 37 + jwksClient *jwks.Client 30 38 } 31 39 32 40 var _ AuthClient = (*clerkClient)(nil) 33 41 34 42 // NewClient creates a new Clerk client wrapper 35 - func NewClient(secretKey string) (*clerkClient, error) { 36 - if secretKey == "" { 43 + func NewClient(cfg *config.Config) (*clerkClient, error) { 44 + if cfg.Clerk.SecretKey == "" { 37 45 return nil, fmt.Errorf("clerk secret key is required") 38 46 } 39 47 40 - // Set the global Clerk secret key 41 - //TODO use a local client instead? GLOBALS BAD 42 - clerk.SetKey(secretKey) 48 + clientConfig := &clerk.ClientConfig{ 49 + BackendConfig: clerk.BackendConfig{ 50 + Key: clerk.String(cfg.Clerk.SecretKey), 51 + }, 52 + } 43 53 44 54 return &clerkClient{ 45 - secretKey: secretKey, 55 + userClient: user.NewClient(clientConfig), 56 + sessionClient: session.NewClient(clientConfig), 57 + jwksClient: jwks.NewClient(clientConfig), 58 + cfg: cfg, 46 59 }, nil 47 60 } 48 61 49 62 func (c *clerkClient) GetUserEmail(ctx context.Context, clerkUserID string) (string, error) { 50 - clerkUser, err := user.Get(ctx, clerkUserID) 63 + 64 + //todo can we pull this right off of claims? not woth bothering? 65 + clerkUser, err := c.userClient.Get(ctx, clerkUserID) 51 66 if err != nil { 52 67 return "", fmt.Errorf("failed to fetch clerk user: %w", err) 53 68 } ··· 94 109 slog.Info("authorization failure, purging cookies and redirecting") 95 110 // Clear any existing Clerk cookies by setting them to expired 96 111 clearCookie(w, "__session") 112 + clearCookie(w, "__clerk_db_jwt") // common in dev flows 113 + clearCookie(w, "__client") // if present in your setup 97 114 http.Redirect(w, r, r.RequestURI, http.StatusFound) 98 115 })) 99 116 ··· 105 122 return "" 106 123 }) 107 124 108 - wrapped := clerkhttp.WithHeaderAuthorization(purgeAndRedirect, useSessionCookie)(handler) 125 + wrapped := clerkhttp.WithHeaderAuthorization( 126 + purgeAndRedirect, 127 + useSessionCookie, 128 + clerkhttp.JWKSClient(c.jwksClient), 129 + )(handler) 109 130 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 110 131 wrapped.ServeHTTP(w, r) 111 132 }) 112 133 } 113 134 114 - func (c *clerkClient) Logout(w http.ResponseWriter, r *http.Request) { 135 + func (c *clerkClient) logout(w http.ResponseWriter, r *http.Request) { 115 136 claims, ok := clerk.SessionClaimsFromContext(r.Context()) 116 137 if ok && claims.SessionID != "" { 117 138 // Revoke the active Clerk session (sid claim). 118 - _, _ = session.Revoke(r.Context(), &session.RevokeParams{ID: claims.SessionID}) 139 + _, _ = c.sessionClient.Revoke(r.Context(), &session.RevokeParams{ID: claims.SessionID}) 119 140 } 120 141 121 142 // Clear app-domain cookies that can re-bootstrap auth. ··· 140 161 }) 141 162 } 142 163 143 - /* Toss this in if you're confused :) 144 - func debugAuth(next http.Handler) http.Handler { 164 + func (c *clerkClient) Register(mux *http.ServeMux) { 165 + mux.HandleFunc("/logout", c.logout) 166 + mux.HandleFunc("/sign-in", func(w http.ResponseWriter, r *http.Request) { 167 + http.Redirect(w, r, c.cfg.Clerk.Signin(), http.StatusSeeOther) 168 + }) 169 + mux.HandleFunc("/sign-up", func(w http.ResponseWriter, r *http.Request) { 170 + http.Redirect(w, r, c.cfg.Clerk.Signup(), http.StatusSeeOther) 171 + }) 172 + mux.HandleFunc("/auth/establish", func(w http.ResponseWriter, r *http.Request) { 173 + if c.cfg.Clerk.PublishableKey == "" { 174 + http.Error(w, "clerk publishable key missing", http.StatusInternalServerError) 175 + return 176 + } 177 + w.Header().Set("Content-Type", "text/html; charset=utf-8") 178 + data := struct { 179 + PublishableKey string 180 + }{ 181 + PublishableKey: c.cfg.Clerk.PublishableKey, 182 + } 183 + if err := templates.AuthEstablish.Execute(w, data); err != nil { 184 + slog.ErrorContext(r.Context(), "auth establish template execute error", "error", err) 185 + http.Error(w, "template error", http.StatusInternalServerError) 186 + } 187 + }) 188 + } 189 + 190 + // Toss this in if you're confused :) 191 + /* 192 + func DebugAuth(next http.Handler) http.Handler { 145 193 return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { 146 194 q := r.URL.Query() 147 195 hasDB := q.Has("__clerk_db_jwt")
+12
internal/auth/factory.go
··· 1 + package auth 2 + 3 + import "careme/internal/config" 4 + 5 + // NewFromConfig creates an AuthClient based on config settings. 6 + func NewFromConfig(cfg *config.Config) (AuthClient, error) { 7 + if cfg.Mocks.Enable { 8 + return Mock(cfg), nil 9 + } 10 + 11 + return NewClient(cfg) 12 + }
+12
internal/auth/mock.go
··· 32 32 func (c *mockClient) GetUserIDFromRequest(r *http.Request) (string, error) { 33 33 return "mock-clerk-user-id", nil 34 34 } 35 + 36 + func (c *mockClient) WithAuthHTTP(handler http.Handler) http.Handler { 37 + return handler 38 + } 39 + 40 + func (c *mockClient) logout(w http.ResponseWriter, r *http.Request) { 41 + http.Error(w, "not implemented in mock auth client", http.StatusNotImplemented) 42 + } 43 + 44 + func (c *mockClient) Register(mux *http.ServeMux) { 45 + mux.HandleFunc("/logout", c.logout) 46 + }
+1
internal/config/config.go
··· 40 40 41 41 var locahostredirect = "?redirect_url=http://localhost:8080/auth/establish" 42 42 43 + // move to auth pacakage? 43 44 func (c *ClerkConfig) Signin() string { 44 45 url := fmt.Sprintf("https://%s/sign-in", c.Domain) 45 46 if !c.Prod {