Add encryption experiments
This commit is contained in:
parent
bf5acf23b0
commit
94851e6104
10 changed files with 200 additions and 25 deletions
2
.gitignore
vendored
Normal file
2
.gitignore
vendored
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
pye-auth
|
||||||
|
key
|
5
Makefile
Normal file
5
Makefile
Normal file
|
@ -0,0 +1,5 @@
|
||||||
|
build:
|
||||||
|
go build
|
||||||
|
|
||||||
|
run:
|
||||||
|
go build && ./pye-auth
|
34
README.md
34
README.md
|
@ -1,3 +1,33 @@
|
||||||
# PYE
|
# PYE Auth
|
||||||
|
|
||||||
**Mission**: Science compels us to create a microservice!
|
**Mission**: Science compels us to create a microservice!
|
||||||
|
|
||||||
|
This is the repository for my **JWT auth microservice assignment**
|
||||||
|
with(out) blazingly fast cloud-native web3 memory-safe blockchain reactive AI
|
||||||
|
(insert a dozen more buzzwords of your choosing) technologies.
|
||||||
|
|
||||||
|
This should be done by **October 17th 2024**, or at the very least,
|
||||||
|
in a shape that proves I am somewhat competent.
|
||||||
|
|
||||||
|
## Course of action
|
||||||
|
|
||||||
|
How I currently see this going
|
||||||
|
|
||||||
|
1. Make an HTTP Basic Auth -> JWT -> Open key API
|
||||||
|
2. Create simple frontend (really stretching the definition) to test it
|
||||||
|
3. Ask myself and others - "Is this a microservice?"
|
||||||
|
If the answer is yes, rejoice.
|
||||||
|
If the answer is no, rejoice for a different reason.
|
||||||
|
4. Once it's technically solid-ish, polish ever-so-slightly
|
||||||
|
|
||||||
|
## "Technology stack"
|
||||||
|
|
||||||
|
The technology I *intend* on using
|
||||||
|
|
||||||
|
1. **Data storage - SQLite**.
|
||||||
|
Definitely want to avoid a full-sized DB because they're oversized for most
|
||||||
|
projects. To be honest, even **JSON** would do for this.
|
||||||
|
In fact, this might just be the way to go for the proof-of-concept, hm...
|
||||||
|
2. **Frontend - template/html module**. Duh, I am anti-bloat.
|
||||||
|
3. **HTTP routing - Chi**.
|
||||||
|
I'd use `net/http`, but a deadline of 1 week means speed is everything.
|
35
auth.go
35
auth.go
|
@ -1,6 +1,7 @@
|
||||||
package main
|
package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
|
"log/slog"
|
||||||
"net/http"
|
"net/http"
|
||||||
"net/mail"
|
"net/mail"
|
||||||
"strings"
|
"strings"
|
||||||
|
@ -13,9 +14,9 @@ func ValidEmail(email string) bool {
|
||||||
func ValidPass(pass string) bool {
|
func ValidPass(pass string) bool {
|
||||||
return len(pass) >= 8 // TODO: Obviously, we *might* want something more sophisticated here
|
return len(pass) >= 8 // TODO: Obviously, we *might* want something more sophisticated here
|
||||||
}
|
}
|
||||||
func TakenEmail(email string) bool {
|
func EmailTaken(email string) bool {
|
||||||
// TODO: Implement
|
// TODO: Implement properly
|
||||||
return false
|
return EmailExists(email)
|
||||||
}
|
}
|
||||||
func Register(w http.ResponseWriter, r *http.Request) {
|
func Register(w http.ResponseWriter, r *http.Request) {
|
||||||
email, password, ok := r.BasicAuth()
|
email, password, ok := r.BasicAuth()
|
||||||
|
@ -23,13 +24,39 @@ func Register(w http.ResponseWriter, r *http.Request) {
|
||||||
if !ok {
|
if !ok {
|
||||||
email = strings.TrimSpace(email)
|
email = strings.TrimSpace(email)
|
||||||
password = strings.TrimSpace(password)
|
password = strings.TrimSpace(password)
|
||||||
if !(ValidEmail(email) || ValidPass(password) || TakenEmail(email)) {
|
if !(ValidEmail(email) || ValidPass(password) || EmailTaken(email)) {
|
||||||
// TODO: Provide descriptive error and check if 400 is best code?
|
// TODO: Provide descriptive error and check if 400 is best code?
|
||||||
http.Error(w, "Invalid auth credentials", http.StatusBadRequest)
|
http.Error(w, "Invalid auth credentials", http.StatusBadRequest)
|
||||||
|
return
|
||||||
}
|
}
|
||||||
|
user, err := NewUser(email, password)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Error creating a new user", "error", err)
|
||||||
|
}
|
||||||
|
AddUser(user)
|
||||||
}
|
}
|
||||||
|
|
||||||
// No email and password was provided
|
// No email and password was provided
|
||||||
w.Header().Set("WWW-Authenticate", `Basic realm="restricted", charset="UTF-8"`)
|
w.Header().Set("WWW-Authenticate", `Basic realm="restricted", charset="UTF-8"`)
|
||||||
http.Error(w, "This API requires authorization", http.StatusUnauthorized)
|
http.Error(w, "This API requires authorization", http.StatusUnauthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func Login(w http.ResponseWriter, r *http.Request) {
|
||||||
|
email, password, ok := r.BasicAuth()
|
||||||
|
|
||||||
|
if !ok {
|
||||||
|
email = strings.TrimSpace(email)
|
||||||
|
password = strings.TrimSpace(password)
|
||||||
|
user, ok := ByEmail(email)
|
||||||
|
if !ok || !user.PasswordFits(password) {
|
||||||
|
http.Error(w, "You did something wrong", http.StatusUnauthorized)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
w.WriteHeader(http.StatusOK)
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
// No email and password was provided
|
||||||
|
w.Header().Set("WWW-Authenticate", `Basic realm="restricted", charset="UTF-8"`)
|
||||||
|
http.Error(w, "This API requires authorization", http.StatusUnauthorized)
|
||||||
|
}
|
1
go.mod
1
go.mod
|
@ -3,6 +3,7 @@ module pye-auth
|
||||||
go 1.22
|
go 1.22
|
||||||
|
|
||||||
require (
|
require (
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||||
github.com/google/uuid v1.6.0
|
github.com/google/uuid v1.6.0
|
||||||
golang.org/x/crypto v0.28.0
|
golang.org/x/crypto v0.28.0
|
||||||
)
|
)
|
||||||
|
|
2
go.sum
2
go.sum
|
@ -1,3 +1,5 @@
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.2.1 h1:OuVbFODueb089Lh128TAcimifWaLhJwVflnrgM17wHk=
|
||||||
|
github.com/golang-jwt/jwt/v5 v5.2.1/go.mod h1:pqrtFR0X4osieyHYxtmOUWsAWrfe1Q5UVIyoH402zdk=
|
||||||
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
github.com/google/uuid v1.6.0 h1:NIvaJDMOsjHA8n1jAhLSgzrAzy1Hgr+hNrb57e+94F0=
|
||||||
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
github.com/google/uuid v1.6.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||||
|
|
44
jwt.go
Normal file
44
jwt.go
Normal file
|
@ -0,0 +1,44 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/ecdsa"
|
||||||
|
"crypto/elliptic"
|
||||||
|
"crypto/rand"
|
||||||
|
"crypto/x509"
|
||||||
|
"log/slog"
|
||||||
|
|
||||||
|
// "github.com/golang-jwt/jwt/v5"
|
||||||
|
)
|
||||||
|
|
||||||
|
// var (
|
||||||
|
// key *ecdsa.PrivateKey
|
||||||
|
// t *jwt.Token
|
||||||
|
// s string
|
||||||
|
// key string
|
||||||
|
// )
|
||||||
|
|
||||||
|
func CreateKey() {
|
||||||
|
// TODO: Is this a secure key?
|
||||||
|
k, err := ecdsa.GenerateKey(elliptic.P521(), rand.Reader)
|
||||||
|
if err != nil {
|
||||||
|
slog.Error("Error generating key", "error", err)
|
||||||
|
}
|
||||||
|
km, _ := x509.MarshalECPrivateKey(k)
|
||||||
|
slog.Info("Key", "key", km)
|
||||||
|
}
|
||||||
|
|
||||||
|
// func CreateJWT(usr User) string {
|
||||||
|
|
||||||
|
// t := jwt.NewWithClaims(jwt.SigningMethodES256,
|
||||||
|
// jwt.MapClaims{
|
||||||
|
// "iss": "my-auth-server",
|
||||||
|
// "sub": "john",
|
||||||
|
// "foo": 2,
|
||||||
|
// })
|
||||||
|
// s, err := t.SignedString(key)
|
||||||
|
// if err != nil {
|
||||||
|
// slog.Error("Error creating JWT", "error", err)
|
||||||
|
// // TODO: Something
|
||||||
|
// }
|
||||||
|
// return s
|
||||||
|
// }
|
37
main.go
37
main.go
|
@ -2,31 +2,34 @@ package main
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"net/http"
|
// "net/http"
|
||||||
)
|
)
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
fmt.Println("Test")
|
fmt.Println("Test")
|
||||||
|
|
||||||
router := http.NewServeMux()
|
CreateKey()
|
||||||
|
|
||||||
router.HandleFunc("POST /todos", func(w http.ResponseWriter, r *http.Request) {
|
// router := http.NewServeMux()
|
||||||
fmt.Println("create a todo")
|
|
||||||
})
|
|
||||||
|
|
||||||
router.HandleFunc("GET /todos", func(w http.ResponseWriter, r *http.Request) {
|
// router.HandleFunc("POST /todos", func(w http.ResponseWriter, r *http.Request) {
|
||||||
fmt.Println("get all todos")
|
// fmt.Println("create a todo")
|
||||||
})
|
// })
|
||||||
|
|
||||||
router.HandleFunc("PATCH /todos/{id}", func(w http.ResponseWriter, r *http.Request) {
|
// // router.HandleFunc("GET /public-key", func(w http.ResponseWriter, r *http.Request) {
|
||||||
id := r.PathValue("id")
|
// // w.WriteHeader(http.StatusOK)
|
||||||
fmt.Println("update a todo by id", id)
|
// // w.Write()
|
||||||
})
|
// // })
|
||||||
|
|
||||||
router.HandleFunc("DELETE /todos/{id}", func(w http.ResponseWriter, r *http.Request) {
|
// router.HandleFunc("PATCH /todos/{id}", func(w http.ResponseWriter, r *http.Request) {
|
||||||
id := r.PathValue("id")
|
// id := r.PathValue("id")
|
||||||
fmt.Println("delete a todo by id", id)
|
// fmt.Println("update a todo by id", id)
|
||||||
})
|
// })
|
||||||
|
|
||||||
http.ListenAndServe(":7102", router)
|
// router.HandleFunc("DELETE /todos/{id}", func(w http.ResponseWriter, r *http.Request) {
|
||||||
|
// id := r.PathValue("id")
|
||||||
|
// fmt.Println("delete a todo by id", id)
|
||||||
|
// })
|
||||||
|
|
||||||
|
// http.ListenAndServe(":7102", router)
|
||||||
}
|
}
|
||||||
|
|
60
pseudo_db.go
Normal file
60
pseudo_db.go
Normal file
|
@ -0,0 +1,60 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"encoding/json"
|
||||||
|
"log"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// So SQLite seems to hate my Mac.
|
||||||
|
// And I'd rather deal with something easily tinker-able in PoC stage
|
||||||
|
// So.................
|
||||||
|
// JSON.
|
||||||
|
//
|
||||||
|
// TODO: Kill this, preferably with fire.
|
||||||
|
|
||||||
|
func ReadUsers() []User {
|
||||||
|
data, err := os.ReadFile("./data.json")
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
var users []User
|
||||||
|
err = json.Unmarshal(data, &users)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
return users
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddUser(user User) {
|
||||||
|
users := ReadUsers()
|
||||||
|
users = append(users, user)
|
||||||
|
data, err := json.Marshal(users)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
err = os.WriteFile("./data.json", data, 0644)
|
||||||
|
if err != nil {
|
||||||
|
log.Fatal(err)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func EmailExists(email string) bool {
|
||||||
|
users := ReadUsers()
|
||||||
|
for i := 0; i < len(users); i++ {
|
||||||
|
if users[i].email == email {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
func UserByEmail(email string) (User, bool) {
|
||||||
|
users := ReadUsers()
|
||||||
|
for i := 0; i < len(users); i++ {
|
||||||
|
if users[i].email == email {
|
||||||
|
return users[i], true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return User{}, false
|
||||||
|
}
|
5
user.go
5
user.go
|
@ -24,6 +24,7 @@ func NewUser(email, password string) (User, error) {
|
||||||
return User{uuid.New(), email, hash}, nil
|
return User{uuid.New(), email, hash}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func CreateUser(User) {
|
// TODO: Implement
|
||||||
|
func ByEmail(email string) (User, bool) {
|
||||||
|
return UserByEmail(email)
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue