rednafi / rednafi.com

Musings & rants on software

Home Page:https://rednafi.com/

Geek Repo:Geek Repo

Github PK Tool:Github PK Tool

Crossing the CORS crossroad

rednafi opened this issue · comments

  • Briefly explain the CORS workflow
  • Maybe add a simple mermaid sequence diagram to explain it
  • Write a quick server that allows requests only from a list of allowed origins
  • Demonstrate how to make preflight options requests with curl to verify that the server only allows requests from certain origins
package main

import (
	"encoding/json"
	"fmt"
	"net/http"
)

// Person struct to parse the input JSON
type Person struct {
	Name string `json:"name"`
}

// helloNameHandler responds with "Hello {name}".
func helloNameHandler(w http.ResponseWriter, r *http.Request) {
	if r.Method != "POST" {
		http.Error(w, "Only POST method is allowed", http.StatusMethodNotAllowed)
		return
	}

	var p Person
	if err := json.NewDecoder(r.Body).Decode(&p); err != nil {
		http.Error(w, err.Error(), http.StatusBadRequest)
		return
	}

	response := fmt.Sprintf("Hello %s", p.Name)
	w.Header().Set("Content-Type", "application/json")
	json.NewEncoder(w).Encode(map[string]string{"message": response})
}

// corsMiddleware adds CORS headers to the response
func corsMiddleware(next http.Handler) http.Handler {
	return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
		allowedOrigins := map[string]bool{
			"http://allowed-origin-1.com": true,
			"http://allowed-origin-2.com": true,
		}

		origin := r.Header.Get("Origin")
		if _, ok := allowedOrigins[origin]; ok {
			w.Header().Set("Access-Control-Allow-Origin", origin)
		} else {
			// Optional: Handle not allowed origin, e.g., by returning an error
			http.Error(w, "Origin not allowed", http.StatusForbidden)
			return
		}

		w.Header().Set("Access-Control-Allow-Methods", "POST, OPTIONS")
		w.Header().Set("Access-Control-Allow-Headers", "Content-Type")

		// Handle preflight request
		if r.Method == "OPTIONS" {
			w.WriteHeader(http.StatusOK)
			return
		}

		next.ServeHTTP(w, r)
	})
}

func main() {
	mux := http.NewServeMux()
	mux.Handle("/hello", corsMiddleware(http.HandlerFunc(helloNameHandler)))

	fmt.Println("Server is running on http://localhost:7676")
	http.ListenAndServe(":7676", mux)
}

Run the server with go run main.go.

Make a preflight request with the correct header:

curl -X OPTIONS http://localhost:7676/hello -d '{"name": "Thing"}' -i -H 'Origin: http://allowed-origin-1.com'
HTTP/1.1 200 OK
Access-Control-Allow-Headers: Content-Type
Access-Control-Allow-Methods: POST, OPTIONS
Access-Control-Allow-Origin: http://allowed-origin-1.com
Date: Tue, 12 Mar 2024 16:08:26 GMT
Content-Length: 0

Make a preflight request with incorrect header:

curl -X OPTIONS http://localhost:7676/hello -d '{"name": "Thing"}' -i -H 'Origin: http://notallowed.com' 
HTTP/1.1 403 Forbidden
Content-Type: text/plain; charset=utf-8
X-Content-Type-Options: nosniff
Date: Tue, 12 Mar 2024 16:11:57 GMT
Content-Length: 19

Origin not allowed