feat: Added chapter 6 first auth attempt
This commit is contained in:
parent
9603509fcd
commit
e72fd2ee86
1
go.mod
1
go.mod
|
@ -6,4 +6,5 @@ require (
|
|||
github.com/google/uuid v1.6.0 // indirect
|
||||
github.com/joho/godotenv v1.5.1 // indirect
|
||||
github.com/lib/pq v1.10.9 // indirect
|
||||
golang.org/x/crypto v0.28.0 // indirect
|
||||
)
|
||||
|
|
2
go.sum
2
go.sum
|
@ -4,3 +4,5 @@ github.com/joho/godotenv v1.5.1 h1:7eLL/+HRGLY0ldzfGMeQkb7vMd0as4CfYvUVzLqw0N0=
|
|||
github.com/joho/godotenv v1.5.1/go.mod h1:f4LDr5Voq0i2e/R5DDNOoa2zzDfwtkZa6DnEwAbqwq4=
|
||||
github.com/lib/pq v1.10.9 h1:YXG7RB+JIjhP29X+OtkiDnYaXQwpS4JEWq7dtCCRUEw=
|
||||
github.com/lib/pq v1.10.9/go.mod h1:AlVN5x4E4T544tWzH6hKfbfQvm3HdbOxrmggDNAPY9o=
|
||||
golang.org/x/crypto v0.28.0 h1:GBDwsMXVQi34v5CCYUm2jkJvu4cbtru2U4TN2PSyQnw=
|
||||
golang.org/x/crypto v0.28.0/go.mod h1:rmgy+3RHxRZMyY0jjAJShp2zgEdOqj2AO7U0pYmeQ7U=
|
||||
|
|
12
internal/auth/auth.go
Normal file
12
internal/auth/auth.go
Normal file
|
@ -0,0 +1,12 @@
|
|||
package auth
|
||||
|
||||
import "golang.org/x/crypto/bcrypt"
|
||||
|
||||
func HashPassword(password string) (string, error) {
|
||||
hashedPassword, err := bcrypt.GenerateFromPassword([]byte(password), 10)
|
||||
return string(hashedPassword), err
|
||||
}
|
||||
|
||||
func CheckPasswordHash(password, hash string) error {
|
||||
return bcrypt.CompareHashAndPassword([]byte(hash), []byte(password))
|
||||
}
|
|
@ -23,4 +23,5 @@ type User struct {
|
|||
CreatedAt time.Time
|
||||
UpdatedAt time.Time
|
||||
Email string
|
||||
HashedPassword string
|
||||
}
|
||||
|
|
|
@ -10,31 +10,33 @@ import (
|
|||
)
|
||||
|
||||
const createUser = `-- name: CreateUser :one
|
||||
INSERT INTO users (id, created_at, updated_at, email)
|
||||
INSERT INTO users (id, created_at, updated_at, email, hashed_password)
|
||||
VALUES (
|
||||
gen_random_uuid(),
|
||||
NOW(),
|
||||
NOW(),
|
||||
$1
|
||||
$1,
|
||||
$2
|
||||
)
|
||||
RETURNING id, created_at, updated_at, email
|
||||
RETURNING id, created_at, updated_at, email, hashed_password
|
||||
`
|
||||
|
||||
func (q *Queries) CreateUser(ctx context.Context, email string) (User, error) {
|
||||
row := q.db.QueryRowContext(ctx, createUser, email)
|
||||
func (q *Queries) CreateUser(ctx context.Context, email string, hashed_password string) (User, error) {
|
||||
row := q.db.QueryRowContext(ctx, createUser, email, hashed_password)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Email,
|
||||
&i.HashedPassword,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const deleteUser = `-- name: DeleteUser :one
|
||||
DELETE FROM users
|
||||
RETURNING id, created_at, updated_at, email
|
||||
RETURNING id, created_at, updated_at, email, hashed_password
|
||||
`
|
||||
|
||||
func (q *Queries) DeleteUser(ctx context.Context) (User, error) {
|
||||
|
@ -45,6 +47,25 @@ func (q *Queries) DeleteUser(ctx context.Context) (User, error) {
|
|||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Email,
|
||||
&i.HashedPassword,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
||||
const getUserByEmail = `-- name: GetUserByEmail :one
|
||||
SELECT id, created_at, updated_at, email, hashed_password FROM users
|
||||
WHERE users.email = $1
|
||||
`
|
||||
|
||||
func (q *Queries) GetUserByEmail(ctx context.Context, email string) (User, error) {
|
||||
row := q.db.QueryRowContext(ctx, getUserByEmail, email)
|
||||
var i User
|
||||
err := row.Scan(
|
||||
&i.ID,
|
||||
&i.CreatedAt,
|
||||
&i.UpdatedAt,
|
||||
&i.Email,
|
||||
&i.HashedPassword,
|
||||
)
|
||||
return i, err
|
||||
}
|
||||
|
|
51
login.go
Normal file
51
login.go
Normal file
|
@ -0,0 +1,51 @@
|
|||
package main
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"log"
|
||||
"net/http"
|
||||
|
||||
"github.com/finchrelia/chirpy-server/internal/auth"
|
||||
)
|
||||
|
||||
func (cfg *apiConfig) Login(w http.ResponseWriter, r *http.Request) {
|
||||
type params struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
p := params{}
|
||||
err := decoder.Decode(&p)
|
||||
if err != nil {
|
||||
log.Printf("Incorrect email or password")
|
||||
w.WriteHeader(401)
|
||||
return
|
||||
}
|
||||
loggedUser, err := cfg.DB.GetUserByEmail(r.Context(), p.Email)
|
||||
if err != nil {
|
||||
log.Printf("Error retrieving user: %s", err)
|
||||
}
|
||||
|
||||
err = auth.CheckPasswordHash(p.Password, loggedUser.HashedPassword)
|
||||
if err != nil {
|
||||
log.Printf("Incorrect email or password")
|
||||
w.WriteHeader(401)
|
||||
return
|
||||
}
|
||||
|
||||
data, err := json.Marshal(User{
|
||||
ID: loggedUser.ID,
|
||||
CreatedAt: loggedUser.CreatedAt,
|
||||
UpdatedAt: loggedUser.UpdatedAt,
|
||||
Email: loggedUser.Email,
|
||||
})
|
||||
if err != nil {
|
||||
log.Printf("Error marshalling JSON: %s", err)
|
||||
w.WriteHeader(500)
|
||||
return
|
||||
}
|
||||
w.Header().Set("Content-Type", "application/json")
|
||||
w.WriteHeader(200)
|
||||
w.Write(data)
|
||||
}
|
1
main.go
1
main.go
|
@ -51,6 +51,7 @@ func main() {
|
|||
mux.HandleFunc("POST /api/chirps", apiCfg.chirpsCreate)
|
||||
mux.HandleFunc("POST /api/users", apiCfg.createUsers)
|
||||
mux.HandleFunc("GET /api/chirps/{chirpID}", apiCfg.getChirp)
|
||||
mux.HandleFunc("POST /api/login", apiCfg.Login)
|
||||
|
||||
server := &http.Server{
|
||||
Addr: ":8080",
|
||||
|
|
|
@ -11,3 +11,7 @@ RETURNING *;
|
|||
-- name: DeleteUser :one
|
||||
DELETE FROM users
|
||||
RETURNING *;
|
||||
|
||||
-- name: GetUserByEmail :one
|
||||
SELECT * FROM users
|
||||
WHERE users.email = $1;
|
7
sql/schema/003_users_auth.sql
Normal file
7
sql/schema/003_users_auth.sql
Normal file
|
@ -0,0 +1,7 @@
|
|||
-- +goose Up
|
||||
ALTER TABLE users
|
||||
ADD COLUMN hashed_password TEXT DEFAULT 'unset' NOT NULL;
|
||||
|
||||
-- +goose Down
|
||||
ALTER TABLE users
|
||||
DROP COLUMN hashed_password;
|
10
users.go
10
users.go
|
@ -6,6 +6,7 @@ import (
|
|||
"net/http"
|
||||
"time"
|
||||
|
||||
"github.com/finchrelia/chirpy-server/internal/auth"
|
||||
"github.com/google/uuid"
|
||||
)
|
||||
|
||||
|
@ -14,11 +15,13 @@ type User struct {
|
|||
CreatedAt time.Time `json:"created_at"`
|
||||
UpdatedAt time.Time `json:"updated_at"`
|
||||
Email string `json:"email"`
|
||||
HashedPassword string `json:"-"`
|
||||
}
|
||||
|
||||
func (cfg *apiConfig) createUsers(w http.ResponseWriter, r *http.Request) {
|
||||
type parameters struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
decoder := json.NewDecoder(r.Body)
|
||||
|
@ -30,7 +33,12 @@ func (cfg *apiConfig) createUsers(w http.ResponseWriter, r *http.Request) {
|
|||
return
|
||||
}
|
||||
defer r.Body.Close()
|
||||
newDBUser, err := cfg.DB.CreateUser(r.Context(), params.Email)
|
||||
|
||||
hashedPassword, err := auth.HashPassword(params.Password)
|
||||
if err != nil {
|
||||
log.Printf("Error hashing password: %s", err)
|
||||
}
|
||||
newDBUser, err := cfg.DB.CreateUser(r.Context(), params.Email, hashedPassword)
|
||||
if err != nil {
|
||||
log.Printf("Error creating user %s: %s", params.Email, err)
|
||||
w.WriteHeader(500)
|
||||
|
|
Loading…
Reference in New Issue
Block a user