mirror of
https://github.com/therootcompany/golib.git
synced 2026-04-24 12:48:00 +00:00
Principal identity is the subject (who), not the credential instance (which token). The hashID suffix was an internal cache fingerprint that leaked into the public ID. Callers that need to distinguish individual token instances must use a separate mechanism. TSV serialization in ToRecord() still writes Name~hashID when hashID is set so the credential file round-trips correctly.
csvauth
Simple, non-scalable credentials stored in a tab-separated file.
(logical successor to envauth)
- Login Credentials
- Save recoverable (aes or plain) or salted hashed passwords (pbkdf2 or bcrypt)
- Great in http middleware, authorizing login or api requests
- Stored by username (or token hash)
- Service Accounts
- Store API keys for services like SMTP and S3
- Great for contacting other services
- Stored by purpose
Also useful for generating pbkdf2 or bcrypt hashes for manual entry in a real database.
Can be adapted to pull from a Google Sheets URL (CSV format).
# create login credentials
csvauth store 'bot@example.com'
# create login token
csvauth store --token 'bot@example.com'
# store service account
csvauth store --purpose 'postmark_smtp_notifier' 'admin@example.com'
credentials.tsv:
purpose name algo salt derived roles extra
ntfy_sh mytopic-1234 plain mytopic-1234
s3_files account1 aes xxxxxxxxxxxx xxxxxxxxxxxxxxxx
login johndoe pbkdf2 1000 16 SHA-256 5cLjzprCHP3WmMbzfqVaew k-elXFa4B_P4-iZ-Rr9GnA admin
login janedoe bcrypt $2a$12$Xbe3OnIapGXUv9eF3k3cSu7sazeZSJquUwGzaovJxb9XQcN54/rte {"foo": "bar"}
f, err := os.Open("./credentials.tsv")
defer func() { _ = f.Close() }()
auth, err := csvauth.Load(f)
// ...
credential, err := auth.Authenticate(usernameOrEmpty, passwordOrToken)
if err != nil {
return err
}
// ...
account := auth.LoadServiceAccount("account-mailer")
req.SetBasicAuth(account.Name, account.Secret())
Login Credentials: Basic Auth & Bearer Token
-
Use
csvauth store [options] <username>to create new login credentials.go run ./cmd/csvauth/ store --helpgo run ./cmd/csvauth/ store 'john.doe@example.com' # choose your own algorithm go run ./cmd/csvauth/ store --algorithm aes-128-gcm 'johndoe' go run ./cmd/csvauth/ store --algorithm plain 'johndoe' go run ./cmd/csvauth/ store --algorithm 'pbkdf2 1000 16 SHA-256' 'johndoe' go run ./cmd/csvauth/ store --algorithm 'bcrypt 12' 'john.doe@example.com' # choose your own password go run ./cmd/csvauth/ store --ask-password 'john.doe@example.com' go run ./cmd/csvauth/ store --password-file ./password.txt 'johndoe' # add extra credential data go run ./cmd/csvauth/ store --roles 'admin' --extra '{"foo":"bar"}' 'jimbob' -
Use
github.com/therootcompany/golib/auth/csvauthto verify credentialspackage main import ( "net/http" "os" "github.com/therootcompany/golib/auth/csvauth" ) var auth csvauth.Auth func main() { f, _ := os.Open("./credentials.tsv") defer func() { _ = f.Close() }() auth, _ = csvauth.Load(f) // ... } // Example of checking for checking username (or token signifier) and password // (or token) in just about every common way func handleRequest(w http.ResponseWriter, r *http.Request) { name, secret, ok := r.BasicAuth() if !ok { secret, ok = strings.CutPrefix(r.Header.Get("Authorization"), "Bearer ") if !ok { secret = r.Header.Get("X-API-Key") if secret == "" { secret = r.URL.Query().Get("access_token") if secret == "" { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } } } } credential, err := auth.Authenticate(name, secret); if err != nil { http.Error(w, "Unauthorized", http.StatusUnauthorized) return } // ... }
Service Account
-
Use
csvauth store --purpose <account> [options] <username>to store API credentialsgo run ./cmd/csvauth/ store --helpgo run ./cmd/csvauth/ store --purpose ntfy_sh_admins 'acme-admins-1234abcd' -
Use
github.com/therootcompany/golib/auth/csvauthto verify credentialspackage main import ( "bytes" "net/http" "os" "github.com/therootcompany/golib/auth/csvauth" ) func main() { f, _ := os.Open("./credentials.tsv") defer func() { _ = f.Close() }() auth, _ := csvauth.Load(f) // ... credential := auth.LoadServiceAccount("ntfy_sh_admins") req, _ := http.NewRequest("POST", "https://ntfy.sh/"+credential.Secret(), bytes.NewBuffer(message)) // ... }