ch16.2
This commit is contained in:
@@ -56,3 +56,8 @@ func (app *application) rateLimitExceededResponse(w http.ResponseWriter, r *http
|
||||
message := "rate limit exceeded"
|
||||
app.errorResponse(w, r, http.StatusTooManyRequests, message)
|
||||
}
|
||||
|
||||
func (app *application) invalidCredentialsResponse(w http.ResponseWriter, r *http.Request) {
|
||||
message := "invalid authentication credentials"
|
||||
app.errorResponse(w, r, http.StatusUnauthorized, message)
|
||||
}
|
||||
|
||||
@@ -23,5 +23,7 @@ func (app *application) routes() http.Handler {
|
||||
router.HandlerFunc(http.MethodPost, "/v1/users", app.registerUserHandler)
|
||||
router.HandlerFunc(http.MethodPut, "/v1/users/activated", app.activateUserHandler)
|
||||
|
||||
router.HandlerFunc(http.MethodPost, "/v1/tokens/authentication", app.createAuthenticationTokenHandler)
|
||||
|
||||
return app.recoverPanic(app.rateLimit(router))
|
||||
}
|
||||
|
||||
66
cmd/api/tokens.go
Normal file
66
cmd/api/tokens.go
Normal file
@@ -0,0 +1,66 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"net/http"
|
||||
"time"
|
||||
|
||||
"gitea.local.lab/Lbenedar/greenlight/internal/data"
|
||||
"gitea.local.lab/Lbenedar/greenlight/internal/validator"
|
||||
)
|
||||
|
||||
func (app *application) createAuthenticationTokenHandler(w http.ResponseWriter, r *http.Request) {
|
||||
var input struct {
|
||||
Email string `json:"email"`
|
||||
Password string `json:"password"`
|
||||
}
|
||||
|
||||
err := app.readJSON(w, r, &input)
|
||||
if err != nil {
|
||||
app.badRequestResponse(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
v := validator.New()
|
||||
|
||||
data.ValidateEmail(v, input.Email)
|
||||
data.ValidatePasswordPlaintext(v, input.Password)
|
||||
|
||||
if !v.Valid() {
|
||||
app.failedValidationResponse(w, r, v.Errors)
|
||||
return
|
||||
}
|
||||
|
||||
user, err := app.models.Users.GetByEmail(input.Email)
|
||||
if err != nil {
|
||||
switch {
|
||||
case errors.Is(err, data.ErrRecordNotFound):
|
||||
app.invalidCredentialsResponse(w, r)
|
||||
default:
|
||||
app.serverErrorResponse(w, r, err)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
match, err := user.Password.Matches(input.Password)
|
||||
if err != nil {
|
||||
app.serverErrorResponse(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
if !match {
|
||||
app.invalidCredentialsResponse(w, r)
|
||||
return
|
||||
}
|
||||
|
||||
token, err := app.models.Tokens.New(user.ID, 24*time.Hour, data.ScopeAuthentication)
|
||||
if err != nil {
|
||||
app.serverErrorResponse(w, r, err)
|
||||
return
|
||||
}
|
||||
|
||||
err = app.writeJSON(w, http.StatusCreated, envelope{"authentication_token": token}, nil)
|
||||
if err != nil {
|
||||
app.serverErrorResponse(w, r, err)
|
||||
}
|
||||
}
|
||||
@@ -12,15 +12,16 @@ import (
|
||||
)
|
||||
|
||||
const (
|
||||
ScopeActivation = "activation"
|
||||
ScopeActivation = "activation"
|
||||
ScopeAuthentication = "authentication"
|
||||
)
|
||||
|
||||
type Token struct {
|
||||
Plaintext string
|
||||
Hash []byte
|
||||
UserID int64
|
||||
Expiry time.Time
|
||||
Scope string
|
||||
Plaintext string `json:"token"`
|
||||
Hash []byte `json:"-"`
|
||||
UserID int64 `json:"-"`
|
||||
Expiry time.Time `json:"expiry"`
|
||||
Scope string `json:"-"`
|
||||
}
|
||||
|
||||
func generateToken(userID int64, ttl time.Duration, scope string) (*Token, error) {
|
||||
@@ -66,7 +67,7 @@ func (m TokenModel) New(userID int64, ttl time.Duration, scope string) (*Token,
|
||||
|
||||
func (m TokenModel) Insert(token *Token) error {
|
||||
query := `
|
||||
INSERT INTO tokens (hash, user_id, expirt, scope)
|
||||
INSERT INTO tokens (hash, user_id, expiry, scope)
|
||||
VALUES ($1, $2, $3, $4)`
|
||||
|
||||
args := []any{token.Hash, token.UserID, token.Expiry, token.Scope}
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
CREATE TABLE IF NOT EXISTS tokens {
|
||||
CREATE TABLE IF NOT EXISTS tokens (
|
||||
hash bytea PRIMARY KEY,
|
||||
user_id bigint NOT NULL REFERENCES users ON DELETE CASCADE,
|
||||
expiry timestamp(0) with time zone NOT NULL,
|
||||
scope text NOT NULL
|
||||
};
|
||||
);
|
||||
Reference in New Issue
Block a user