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
|
||||
|
||||
import (
|
||||
"log/slog"
|
||||
"net/http"
|
||||
"net/mail"
|
||||
"strings"
|
||||
|
@ -13,9 +14,9 @@ func ValidEmail(email string) bool {
|
|||
func ValidPass(pass string) bool {
|
||||
return len(pass) >= 8 // TODO: Obviously, we *might* want something more sophisticated here
|
||||
}
|
||||
func TakenEmail(email string) bool {
|
||||
// TODO: Implement
|
||||
return false
|
||||
func EmailTaken(email string) bool {
|
||||
// TODO: Implement properly
|
||||
return EmailExists(email)
|
||||
}
|
||||
func Register(w http.ResponseWriter, r *http.Request) {
|
||||
email, password, ok := r.BasicAuth()
|
||||
|
@ -23,13 +24,39 @@ func Register(w http.ResponseWriter, r *http.Request) {
|
|||
if !ok {
|
||||
email = strings.TrimSpace(email)
|
||||
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?
|
||||
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
|
||||
w.Header().Set("WWW-Authenticate", `Basic realm="restricted", charset="UTF-8"`)
|
||||
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
|
||||
|
||||
require (
|
||||
github.com/golang-jwt/jwt/v5 v5.2.1
|
||||
github.com/google/uuid v1.6.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/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
|
||||
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 (
|
||||
"fmt"
|
||||
"net/http"
|
||||
// "net/http"
|
||||
)
|
||||
|
||||
func main() {
|
||||
fmt.Println("Test")
|
||||
|
||||
router := http.NewServeMux()
|
||||
CreateKey()
|
||||
|
||||
router.HandleFunc("POST /todos", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Println("create a todo")
|
||||
})
|
||||
// router := http.NewServeMux()
|
||||
|
||||
router.HandleFunc("GET /todos", func(w http.ResponseWriter, r *http.Request) {
|
||||
fmt.Println("get all todos")
|
||||
})
|
||||
// router.HandleFunc("POST /todos", func(w http.ResponseWriter, r *http.Request) {
|
||||
// fmt.Println("create a todo")
|
||||
// })
|
||||
|
||||
router.HandleFunc("PATCH /todos/{id}", func(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.PathValue("id")
|
||||
fmt.Println("update a todo by id", id)
|
||||
})
|
||||
// // router.HandleFunc("GET /public-key", func(w http.ResponseWriter, r *http.Request) {
|
||||
// // w.WriteHeader(http.StatusOK)
|
||||
// // w.Write()
|
||||
// // })
|
||||
|
||||
router.HandleFunc("DELETE /todos/{id}", func(w http.ResponseWriter, r *http.Request) {
|
||||
id := r.PathValue("id")
|
||||
fmt.Println("delete a todo by id", id)
|
||||
})
|
||||
// router.HandleFunc("PATCH /todos/{id}", func(w http.ResponseWriter, r *http.Request) {
|
||||
// id := r.PathValue("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
|
||||
}
|
||||
|
||||
func CreateUser(User) {
|
||||
|
||||
// TODO: Implement
|
||||
func ByEmail(email string) (User, bool) {
|
||||
return UserByEmail(email)
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue