Files
marcusDenslow 6097b8c863 rework: added shipping validation at address creation
the last implementation just accepted any input for the shipping
validation and then only validated the address on payment. this is now
changed to validation upon creation upon finishing the shipping address
form.

when a user completes the shipping form, a client-side check is first
made to just simply check if there are any empty fields, if this check
passes it gets check server-side by shippo. If the address is not found
it will return an address not found error, and the user will not be able
to continue to the payment form. The invalid address will also not be
saved to the database.

if the check pass, we simply save the address to the database with an
ID. this ID is then passe in when doing the payment, instead of
validating at the payment phase.

this implementation currently only uses Shippo, which i have come to
realize does NOT support shipping in countries outside of the US (i
should have read the friendly manual before using Shippo). This means
that a huge rewrite is due.

i will focus on adding address validation for Norway first since thats
where i live. i plan on using Bring's api for this validation. i will
still keep Shippo for US based address verification even though this
project is focused fon nordic shipping - just for a rainy day if some
poor soul decides to order from AMERICA
2026-02-26 16:32:48 +01:00

70 lines
1.8 KiB
Go

package middleware
import (
"context"
"net/http"
"strings"
"terminalShop/pkg/auth"
"terminalShop/pkg/utils"
)
type contextKey string
const userIDKey contextKey = "userID"
func Auth(jwtManager *auth.JWTManager) func(http.Handler) http.Handler {
// Auth extracts and validates JWT tokens on all requests.
// If a valid token is present, store the userID in context.
// If no token or an invalid token, continue as public (no userID).
// Protected routes use RequireAuth to enforce valid authentication.
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
authHeader := r.Header.Get("Authorization")
if authHeader == "" {
// No token - public request
next.ServeHTTP(w, r)
return
}
// Expect "Bearer <token>"
parts := strings.SplitN(authHeader, " ", 2)
if len(parts) != 2 || !strings.EqualFold(parts[0], "Bearer") {
// Malformed header - continue as public request
next.ServeHTTP(w, r)
return
}
claims, err := jwtManager.ValidateToken(parts[1])
if err != nil {
// Invalid or expired token - continue as public request.
// RequireAuth will reject on protected routes.
next.ServeHTTP(w, r)
return
}
ctx := context.WithValue(r.Context(), userIDKey, claims.UserID)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
// Reject requests without a valid user context.
// User on protected route groups.
func RequireAuth(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
if UserIDFromContext(r.Context()) == 0 {
utils.RespondError(w, http.StatusUnauthorized, "UNAUTHORIZED", "authentication required", nil)
return
}
next.ServeHTTP(w, r)
})
}
func UserIDFromContext(ctx context.Context) uint {
if id, ok := ctx.Value(userIDKey).(uint); ok {
return id
}
return 0
}