diff options
Diffstat (limited to 'cmd/server/auth.go')
| -rw-r--r-- | cmd/server/auth.go | 108 |
1 files changed, 108 insertions, 0 deletions
diff --git a/cmd/server/auth.go b/cmd/server/auth.go new file mode 100644 index 0000000..caade97 --- /dev/null +++ b/cmd/server/auth.go @@ -0,0 +1,108 @@ +package main + +import ( + "crypto/rand" + "encoding/base64" + "encoding/json" + "fmt" + "net/http" + "os" + "strings" + + "riedstra.dev/mitch/go-website/users" +) + +type Auth struct { + Users []*users.SiteUser `json:"Users"` + // How long are JWTs valid? + LoginHours int `json:"LoginHours"` + // JWT secret + TokenKey string `json:"TokenKey"` + // Are cookies HTTP only? + HTTPOnly bool `json:"HTTPOnly"` + // Do they require HTTPs? + Secure bool `json:"SecureCookie"` + // See https://pkg.go.dev/net/http#SameSite + // You probably want this set to 3 + SameSiteStrict http.SameSite `json:"SameSiteStrict"` +} + +func GenTokenKey() string { + r := make([]byte, 16) // 128 bits + _, err := rand.Read(r) + + if err != nil { + // Not my favorite thing, but I consider this to be a + // critical issue + panic(fmt.Errorf("reading random bytes: %w", err)) + } + + return base64.RawURLEncoding.EncodeToString(r) +} + +func (a *App) ReadAuth(fn string) error { //nolint + auth := &Auth{ + Users: []*users.SiteUser{ + { + Username: "admin", + Password: "admin", + }, + }, + LoginHours: 1, + TokenKey: GenTokenKey(), + HTTPOnly: true, + Secure: true, + SameSiteStrict: http.SameSiteStrictMode, + } + + var dec *json.Decoder + + fh, err := os.Open(fn) + if err != nil { + if strings.Contains(err.Error(), "no such file") { + goto write + } + + return fmt.Errorf("opening %s: %w", fn, err) + } + + dec = json.NewDecoder(fh) + dec.DisallowUnknownFields() + + err = dec.Decode(auth) + if err != nil { + return fmt.Errorf("decoding file %s: %w", fn, err) + } + + err = fh.Close() + if err != nil { + return fmt.Errorf("closing file %s: %w", fn, err) + } + + for _, u := range auth.Users { + err = u.SetPasswordHashIfNecessary() + if err != nil { + return fmt.Errorf("setting password hash for: %s: %w", + u.Username, err) + } + } + +write: + fh, err = os.OpenFile(fn, os.O_RDWR|os.O_CREATE|os.O_TRUNC, 0600) + + if err != nil { + return fmt.Errorf("opening file %s: %w", fn, err) + } + + enc := json.NewEncoder(fh) + enc.SetIndent("", " ") + + err = enc.Encode(auth) + if err != nil { + return fmt.Errorf("encoding to file %s: %w", fn, err) + } + + a.auth = auth + + return nil +} |
