mirror of
https://github.com/zoriya/Kyoo.git
synced 2025-05-24 02:02:36 -04:00
Add /jwt route
This commit is contained in:
parent
95da0184a0
commit
e197062f64
@ -4,6 +4,7 @@ import (
|
|||||||
"context"
|
"context"
|
||||||
"crypto/rand"
|
"crypto/rand"
|
||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
|
"time"
|
||||||
|
|
||||||
"github.com/golang-jwt/jwt/v5"
|
"github.com/golang-jwt/jwt/v5"
|
||||||
"github.com/zoriya/kyoo/keibi/dbc"
|
"github.com/zoriya/kyoo/keibi/dbc"
|
||||||
@ -13,6 +14,7 @@ type Configuration struct {
|
|||||||
JwtSecret []byte
|
JwtSecret []byte
|
||||||
Issuer string
|
Issuer string
|
||||||
DefaultClaims jwt.MapClaims
|
DefaultClaims jwt.MapClaims
|
||||||
|
ExpirationDelay time.Duration
|
||||||
}
|
}
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
@ -126,6 +126,10 @@ type Handler struct {
|
|||||||
|
|
||||||
// @host kyoo.zoriya.dev
|
// @host kyoo.zoriya.dev
|
||||||
// @BasePath /auth
|
// @BasePath /auth
|
||||||
|
|
||||||
|
// @securityDefinitions.apiKey Token
|
||||||
|
// @in header
|
||||||
|
// @name Authorization
|
||||||
func main() {
|
func main() {
|
||||||
e := echo.New()
|
e := echo.New()
|
||||||
e.Use(middleware.Logger())
|
e.Use(middleware.Logger())
|
||||||
@ -151,6 +155,7 @@ func main() {
|
|||||||
e.GET("/users", h.ListUsers)
|
e.GET("/users", h.ListUsers)
|
||||||
e.POST("/users", h.Register)
|
e.POST("/users", h.Register)
|
||||||
|
|
||||||
|
e.GET("/jwt", h.CreateJwt)
|
||||||
e.POST("/session", h.Login)
|
e.POST("/session", h.Login)
|
||||||
|
|
||||||
e.GET("/swagger/*", echoSwagger.WrapHandler)
|
e.GET("/swagger/*", echoSwagger.WrapHandler)
|
||||||
|
@ -7,6 +7,7 @@ import (
|
|||||||
"encoding/base64"
|
"encoding/base64"
|
||||||
"maps"
|
"maps"
|
||||||
"net/http"
|
"net/http"
|
||||||
|
"strings"
|
||||||
"time"
|
"time"
|
||||||
|
|
||||||
"github.com/alexedwards/argon2id"
|
"github.com/alexedwards/argon2id"
|
||||||
@ -17,7 +18,7 @@ import (
|
|||||||
|
|
||||||
type LoginDto struct {
|
type LoginDto struct {
|
||||||
// Either the email or the username.
|
// Either the email or the username.
|
||||||
Login string `json:"login" validate:"required"`
|
Login string `json:"login" validate:"required"`
|
||||||
// Password of the account.
|
// Password of the account.
|
||||||
Password string `json:"password" validate:"required"`
|
Password string `json:"password" validate:"required"`
|
||||||
}
|
}
|
||||||
@ -82,7 +83,7 @@ func (h *Handler) createSession(c echo.Context, user *User) error {
|
|||||||
|
|
||||||
session, err := h.db.CreateSession(ctx, dbc.CreateSessionParams{
|
session, err := h.db.CreateSession(ctx, dbc.CreateSessionParams{
|
||||||
Token: base64.StdEncoding.EncodeToString(id),
|
Token: base64.StdEncoding.EncodeToString(id),
|
||||||
UserID: user.Id,
|
UserId: user.Id,
|
||||||
Device: device,
|
Device: device,
|
||||||
})
|
})
|
||||||
if err != nil {
|
if err != nil {
|
||||||
@ -91,20 +92,43 @@ func (h *Handler) createSession(c echo.Context, user *User) error {
|
|||||||
return c.JSON(201, session)
|
return c.JSON(201, session)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Jwt struct {
|
||||||
|
// The jwt token you can use for all authorized call to either keibi or other services.
|
||||||
|
Token string `json:"token"`
|
||||||
|
}
|
||||||
|
|
||||||
// @Summary Get JWT
|
// @Summary Get JWT
|
||||||
// @Description Convert a session token to a short lived JWT.
|
// @Description Convert a session token to a short lived JWT.
|
||||||
// @Tags sessions
|
// @Tags sessions
|
||||||
// @Accept json
|
// @Accept json
|
||||||
// @Produce json
|
// @Produce json
|
||||||
// @Param user body LoginDto false "Account informations"
|
// @Security Token
|
||||||
// @Success 200 {object} dbc.Session
|
// @Success 200 {object} Jwt
|
||||||
// @Failure 400 {object} problem.Problem "Invalid login body"
|
// @Failure 401 {object} problem.Problem "Missing session token"
|
||||||
// @Failure 400 {object} problem.Problem "Invalid password"
|
// @Failure 403 {object} problem.Problem "Invalid session token (or expired)"
|
||||||
// @Failure 404 {object} problem.Problem "Account does not exists"
|
|
||||||
// @Router /jwt [get]
|
// @Router /jwt [get]
|
||||||
func (h *Handler) CreateJwt(c echo.Context, user *User) error {
|
func (h *Handler) CreateJwt(c echo.Context) error {
|
||||||
claims := maps.Clone(user.Claims)
|
auth := c.Request().Header.Get("Authorization")
|
||||||
claims["sub"] = user.Id.String()
|
if !strings.HasPrefix(auth, "Bearer ") {
|
||||||
|
return echo.NewHTTPError(http.StatusUnauthorized, "Missing session token")
|
||||||
|
}
|
||||||
|
token := auth[len("Bearer "):]
|
||||||
|
|
||||||
|
session, err := h.db.GetUserFromToken(context.Background(), token)
|
||||||
|
if err != nil {
|
||||||
|
return echo.NewHTTPError(http.StatusForbidden, "Invalid token")
|
||||||
|
}
|
||||||
|
if session.LastUsed.Add(h.config.ExpirationDelay).Compare(time.Now().UTC()) < 0 {
|
||||||
|
return echo.NewHTTPError(http.StatusForbidden, "Token has expired")
|
||||||
|
}
|
||||||
|
|
||||||
|
go func() {
|
||||||
|
h.db.TouchSession(context.Background(), session.Id)
|
||||||
|
h.db.TouchUser(context.Background(), session.User.Id)
|
||||||
|
}()
|
||||||
|
|
||||||
|
claims := maps.Clone(session.User.Claims)
|
||||||
|
claims["sub"] = session.User.Id.String()
|
||||||
claims["iss"] = h.config.Issuer
|
claims["iss"] = h.config.Issuer
|
||||||
claims["exp"] = &jwt.NumericDate{
|
claims["exp"] = &jwt.NumericDate{
|
||||||
Time: time.Now().UTC().Add(time.Hour),
|
Time: time.Now().UTC().Add(time.Hour),
|
||||||
@ -112,12 +136,12 @@ func (h *Handler) CreateJwt(c echo.Context, user *User) error {
|
|||||||
claims["iss"] = &jwt.NumericDate{
|
claims["iss"] = &jwt.NumericDate{
|
||||||
Time: time.Now().UTC(),
|
Time: time.Now().UTC(),
|
||||||
}
|
}
|
||||||
token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
jwt := jwt.NewWithClaims(jwt.SigningMethodHS256, claims)
|
||||||
t, err := token.SignedString(h.config.JwtSecret)
|
t, err := jwt.SignedString(h.config.JwtSecret)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
return c.JSON(http.StatusOK, echo.Map{
|
return c.JSON(http.StatusOK, Jwt{
|
||||||
"token": t,
|
Token: t,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
-- name: GetUserFromToken :one
|
-- name: GetUserFromToken :one
|
||||||
select
|
select
|
||||||
u.*
|
s.id, s.last_used, sqlc.embed(u)
|
||||||
from
|
from
|
||||||
users as u
|
users as u
|
||||||
left join sessions as s on u.id = s.user_id
|
inner join sessions as s on u.id = s.user_id
|
||||||
where
|
where
|
||||||
s.token = $1
|
s.token = $1
|
||||||
limit 1;
|
limit 1;
|
||||||
|
@ -38,6 +38,14 @@ where
|
|||||||
or username = sqlc.arg(login)
|
or username = sqlc.arg(login)
|
||||||
limit 1;
|
limit 1;
|
||||||
|
|
||||||
|
-- name: TouchUser :exec
|
||||||
|
update
|
||||||
|
users
|
||||||
|
set
|
||||||
|
last_used = now()::timestamptz
|
||||||
|
where
|
||||||
|
id = $1;
|
||||||
|
|
||||||
-- name: CreateUser :one
|
-- name: CreateUser :one
|
||||||
insert into users(username, email, password, claims)
|
insert into users(username, email, password, claims)
|
||||||
values ($1, $2, $3, $4)
|
values ($1, $2, $3, $4)
|
||||||
|
@ -10,11 +10,18 @@ sql:
|
|||||||
out: "dbc"
|
out: "dbc"
|
||||||
emit_pointers_for_null_types: true
|
emit_pointers_for_null_types: true
|
||||||
emit_json_tags: true
|
emit_json_tags: true
|
||||||
|
initialisms: []
|
||||||
overrides:
|
overrides:
|
||||||
- db_type: "timestamptz"
|
- db_type: "timestamptz"
|
||||||
go_type:
|
go_type:
|
||||||
import: "time"
|
import: "time"
|
||||||
type: "Time"
|
type: "Time"
|
||||||
|
- db_type: "timestamptz"
|
||||||
|
nullable: true
|
||||||
|
go_type:
|
||||||
|
import: "time"
|
||||||
|
type: "Time"
|
||||||
|
pointer: true
|
||||||
- db_type: "uuid"
|
- db_type: "uuid"
|
||||||
go_type:
|
go_type:
|
||||||
import: "github.com/google/uuid"
|
import: "github.com/google/uuid"
|
||||||
|
@ -49,7 +49,7 @@ type RegisterDto struct {
|
|||||||
|
|
||||||
func MapDbUser(user *dbc.User) User {
|
func MapDbUser(user *dbc.User) User {
|
||||||
return User{
|
return User{
|
||||||
Id: user.ID,
|
Id: user.Id,
|
||||||
Username: user.Username,
|
Username: user.Username,
|
||||||
Email: user.Email,
|
Email: user.Email,
|
||||||
CreatedDate: user.CreatedDate,
|
CreatedDate: user.CreatedDate,
|
||||||
@ -84,7 +84,7 @@ func (h *Handler) ListUsers(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
users, err = h.db.GetAllUsersAfter(ctx, dbc.GetAllUsersAfterParams{
|
users, err = h.db.GetAllUsersAfter(ctx, dbc.GetAllUsersAfterParams{
|
||||||
Limit: limit,
|
Limit: limit,
|
||||||
AfterID: uid,
|
AfterId: uid,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user