Compare commits

...

3 Commits

Author SHA1 Message Date
ade3fa0454 imported env module 2026-01-02 19:03:07 +11:00
516be905a9 imported cookies module 2026-01-02 18:25:38 +11:00
6e632267ea added cookie control to jwt 2026-01-02 18:15:49 +11:00
10 changed files with 266 additions and 0 deletions

19
cookies/delete.go Normal file
View File

@@ -0,0 +1,19 @@
package cookies
import (
"net/http"
"time"
)
// Tell the browser to delete the cookie matching the name provided
// Path must match the original set cookie for it to delete
func DeleteCookie(w http.ResponseWriter, name string, path string) {
http.SetCookie(w, &http.Cookie{
Name: name,
Value: "",
Path: path,
Expires: time.Unix(0, 0), // Expire in the past
MaxAge: -1, // Immediately expire
HttpOnly: true,
})
}

3
cookies/go.mod Normal file
View File

@@ -0,0 +1,3 @@
module git.haelnorr.com/h/golib/cookies
go 1.25.5

36
cookies/pagefrom.go Normal file
View File

@@ -0,0 +1,36 @@
package cookies
import (
"net/http"
"net/url"
)
// Check the value of "pagefrom" cookie, delete the cookie, and return the value
func CheckPageFrom(w http.ResponseWriter, r *http.Request) string {
pageFromCookie, err := r.Cookie("pagefrom")
if err != nil {
return "/"
}
pageFrom := pageFromCookie.Value
DeleteCookie(w, pageFromCookie.Name, pageFromCookie.Path)
return pageFrom
}
// Check the referer of the request, and if it matches the trustedHost, set
// the "pagefrom" cookie as the Path of the referer
func SetPageFrom(w http.ResponseWriter, r *http.Request, trustedHost string) {
referer := r.Referer()
parsedURL, err := url.Parse(referer)
if err != nil {
return
}
var pageFrom string
if parsedURL.Path == "" || parsedURL.Host != trustedHost {
pageFrom = "/"
} else if parsedURL.Path == "/login" || parsedURL.Path == "/register" {
return
} else {
pageFrom = parsedURL.Path
}
SetCookie(w, "pagefrom", "/", pageFrom, 0)
}

23
cookies/set.go Normal file
View File

@@ -0,0 +1,23 @@
package cookies
import (
"net/http"
)
// Set a cookie with the given name, path and value. maxAge directly relates
// to cookie MaxAge (0 for no max age, >0 for TTL in seconds)
func SetCookie(
w http.ResponseWriter,
name string,
path string,
value string,
maxAge int,
) {
http.SetCookie(w, &http.Cookie{
Name: name,
Value: value,
Path: path,
HttpOnly: true,
MaxAge: maxAge,
})
}

35
env/boolean.go vendored Normal file
View File

@@ -0,0 +1,35 @@
package env
import (
"os"
"strings"
)
// Get an environment variable as a boolean, specifying a default value if its
// not set or can't be parsed properly into a bool
func Bool(key string, defaultValue bool) bool {
val, exists := os.LookupEnv(key)
if !exists {
return defaultValue
}
truthy := map[string]bool{
"true": true, "t": true, "yes": true, "y": true, "on": true, "1": true,
"enable": true, "enabled": true, "active": true, "affirmative": true,
}
falsy := map[string]bool{
"false": false, "f": false, "no": false, "n": false, "off": false, "0": false,
"disable": false, "disabled": false, "inactive": false, "negative": false,
}
normalized := strings.TrimSpace(strings.ToLower(val))
if val, ok := truthy[normalized]; ok {
return val
}
if val, ok := falsy[normalized]; ok {
return val
}
return defaultValue
}

23
env/duration.go vendored Normal file
View File

@@ -0,0 +1,23 @@
package env
import (
"os"
"strconv"
"time"
)
// Get an environment variable as a time.Duration, specifying a default value if its
// not set or can't be parsed properly
func Duration(key string, defaultValue time.Duration) time.Duration {
val, exists := os.LookupEnv(key)
if !exists {
return time.Duration(defaultValue)
}
intVal, err := strconv.Atoi(val)
if err != nil {
return time.Duration(defaultValue)
}
return time.Duration(intVal)
}

3
env/go.mod vendored Normal file
View File

@@ -0,0 +1,3 @@
module git.haelnorr.com/h/golib/env
go 1.25.5

37
env/int.go vendored Normal file
View File

@@ -0,0 +1,37 @@
package env
import (
"os"
"strconv"
)
// Get an environment variable as an int, specifying a default value if its
// not set or can't be parsed properly into an int
func Int(key string, defaultValue int) int {
val, exists := os.LookupEnv(key)
if !exists {
return defaultValue
}
intVal, err := strconv.Atoi(val)
if err != nil {
return defaultValue
}
return intVal
}
// Get an environment variable as an int64, specifying a default value if its
// not set or can't be parsed properly into an int64
func Int64(key string, defaultValue int64) int64 {
val, exists := os.LookupEnv(key)
if !exists {
return defaultValue
}
intVal, err := strconv.ParseInt(val, 10, 64)
if err != nil {
return defaultValue
}
return intVal
}

14
env/string.go vendored Normal file
View File

@@ -0,0 +1,14 @@
package env
import (
"os"
)
// Get an environment variable, specifying a default value if its not set
func String(key string, defaultValue string) string {
val, exists := os.LookupEnv(key)
if !exists {
return defaultValue
}
return val
}

73
jwt/cookies.go Normal file
View File

@@ -0,0 +1,73 @@
package jwt
import (
"github.com/pkg/errors"
"net/http"
"time"
)
// Get the value of the access and refresh tokens
func GetTokenCookies(
r *http.Request,
) (acc string, ref string) {
accCookie, accErr := r.Cookie("access")
refCookie, refErr := r.Cookie("refresh")
var (
accStr string = ""
refStr string = ""
)
if accErr == nil {
accStr = accCookie.Value
}
if refErr == nil {
refStr = refCookie.Value
}
return accStr, refStr
}
// Set a token with the provided details
func setToken(
w http.ResponseWriter,
token string,
scope string,
exp int64,
rememberme bool,
useSSL bool,
) {
tokenCookie := &http.Cookie{
Name: scope,
Value: token,
Path: "/",
HttpOnly: true,
SameSite: http.SameSiteLaxMode,
Secure: useSSL,
}
if rememberme {
tokenCookie.Expires = time.Unix(exp, 0)
}
http.SetCookie(w, tokenCookie)
}
// Generate new tokens for the subject and set them as cookies
func SetTokenCookies(
w http.ResponseWriter,
r *http.Request,
tokenGen *TokenGenerator,
subject int,
fresh bool,
rememberMe bool,
useSSL bool,
) error {
at, atexp, err := tokenGen.NewAccess(subject, fresh, rememberMe)
if err != nil {
return errors.Wrap(err, "jwt.GenerateAccessToken")
}
rt, rtexp, err := tokenGen.NewRefresh(subject, rememberMe)
if err != nil {
return errors.Wrap(err, "jwt.GenerateRefreshToken")
}
// Don't set the cookies until we know no errors occured
setToken(w, at, "access", atexp, rememberMe, useSSL)
setToken(w, rt, "refresh", rtexp, rememberMe, useSSL)
return nil
}