Compare commits

...

2 Commits

9 changed files with 50 additions and 50 deletions

View File

@ -60,7 +60,7 @@ const docTemplate = `{
}
}
},
"/v1/articles/by/sourceid": {
"/v1/articles/by/sourceId": {
"get": {
"security": [
{
@ -1035,7 +1035,7 @@ const docTemplate = `{
}
}
},
"/v1/users/refreshToken": {
"/v1/users/refresh/token": {
"post": {
"security": [
{
@ -1350,9 +1350,6 @@ const docTemplate = `{
"refreshToken": {
"type": "string"
},
"sessionToken": {
"type": "string"
},
"token": {
"type": "string"
},

View File

@ -51,7 +51,7 @@
}
}
},
"/v1/articles/by/sourceid": {
"/v1/articles/by/sourceId": {
"get": {
"security": [
{
@ -1026,7 +1026,7 @@
}
}
},
"/v1/users/refreshToken": {
"/v1/users/refresh/token": {
"post": {
"security": [
{
@ -1341,9 +1341,6 @@
"refreshToken": {
"type": "string"
},
"sessionToken": {
"type": "string"
},
"token": {
"type": "string"
},

View File

@ -84,8 +84,6 @@ definitions:
type: string
refreshToken:
type: string
sessionToken:
type: string
token:
type: string
type:
@ -221,7 +219,7 @@ paths:
summary: Returns an article and source based on defined ID.
tags:
- Articles
/v1/articles/by/sourceid:
/v1/articles/by/sourceId:
get:
parameters:
- description: source id
@ -776,7 +774,7 @@ paths:
summary: Revokes the current session token and replaces it with a new one.
tags:
- Users
/v1/users/refreshToken:
/v1/users/refresh/token:
post:
parameters:
- description: body

View File

@ -1,6 +1,5 @@
package domain
type BaseResponse struct {
Message string `json:"message"`
}
@ -10,7 +9,6 @@ type LoginResponse struct {
Token string `json:"token"`
Type string `json:"type"`
RefreshToken string `json:"refreshToken"`
SessionToken string `json:"sessionToken"`
}
type ArticleResponse struct {
@ -36,4 +34,4 @@ type DiscordWebhookResponse struct {
type SourcesResponse struct {
BaseResponse
Payload []SourceDto `json:"payload"`
}
}

View File

@ -135,7 +135,7 @@ func (s *Handler) getArticleDetails(c echo.Context) error {
// @Param page query int false "Page to query"
// @Produce application/json
// @Tags Articles
// @Router /v1/articles/by/sourceid [get]
// @Router /v1/articles/by/sourceId [get]
// @Success 200 {object} domain.ArticleResponse "OK"
// @Failure 400 {object} domain.BaseResponse
// @Failure 500 {object} domain.BaseResponse

View File

@ -110,7 +110,8 @@ func NewServer(ctx context.Context, configs services.Configs, conn *sql.DB) *Han
users.Use(echojwt.WithConfig(jwtConfig))
users.POST("/scopes/add", s.AddScopes)
users.POST("/scopes/remove", s.RemoveScopes)
users.POST("/refreshToken", s.RefreshJwtToken)
users.POST("/refresh/token", s.RefreshJwtToken)
users.POST("/refresh/sessionToken", s.NewSessionToken)
s.Router = router
return s
@ -161,18 +162,24 @@ func (s *Handler) ValidateJwtToken(c echo.Context, requiredScope string) (JwtTok
err = token.hasExpired()
if err != nil {
return JwtToken{}, errors.New(ErrJwtExpired)
//s.WriteMessage(c, ErrJwtExpired, http.StatusUnauthorized)
}
err = token.hasScope(requiredScope)
if err != nil {
return JwtToken{}, errors.New(ErrJwtScopeMissing)
//s.WriteMessage(c, ErrJwtScopeMissing, http.StatusUnauthorized)
}
if token.Iss != s.config.ServerAddress {
return JwtToken{}, errors.New(ErrJwtInvalidIssuer)
//s.WriteMessage(c, ErrJwtInvalidIssuer, http.StatusUnauthorized)
}
user, err := s.repo.Users.GetUser(c.Request().Context(), token.UserName)
if err != nil {
return JwtToken{}, errors.New("user record not found")
}
if user.SessionToken != token.SessionToken {
return JwtToken{}, errors.New("invalid session token")
}
return token, nil

View File

@ -19,12 +19,13 @@ const (
)
type JwtToken struct {
Exp time.Time `json:"exp"`
Iss string `json:"iss"`
Authorized bool `json:"authorized"`
UserName string `json:"username"`
UserId int64 `json:"userId"`
Scopes []string `json:"scopes"`
Exp time.Time `json:"exp"`
Iss string `json:"iss"`
Authorized bool `json:"authorized"`
UserName string `json:"username"`
UserId int64 `json:"userId"`
Scopes []string `json:"scopes"`
SessionToken string `json:"sessionToken"`
jwt.RegisteredClaims
}

View File

@ -49,7 +49,7 @@ func (h *Handler) AuthRegister(c echo.Context) error {
return h.WriteError(c, err, http.StatusInternalServerError)
}
_, err = h.repo.Users.Create(c.Request().Context(), username, password, "", domain.ScopeArticleRead)
_, err = h.repo.Users.Create(c.Request().Context(), username, password, domain.ScopeArticleRead)
if err != nil {
return h.InternalServerErrorResponse(c, err.Error())
}
@ -92,12 +92,8 @@ func (h *Handler) AuthLogin(c echo.Context) error {
// TODO think about moving this down some?
expiresAt := time.Now().Add(time.Hour * 48)
userScopes := strings.Split(user.Scopes, ",")
sessionToken, err := uuid.NewV7()
if err != nil {
return h.InternalServerErrorResponse(c, err.Error())
}
jwt, err := h.generateJwtWithExp(username, h.config.ServerAddress, sessionToken.String(), userScopes, user.ID, expiresAt)
jwt, err := h.generateJwtWithExp(username, h.config.ServerAddress, user.SessionToken, userScopes, user.ID, expiresAt)
if err != nil {
return h.InternalServerErrorResponse(c, err.Error())
}
@ -114,7 +110,6 @@ func (h *Handler) AuthLogin(c echo.Context) error {
Token: jwt,
Type: "Bearer",
RefreshToken: refresh,
SessionToken: sessionToken.String(),
})
}
@ -152,7 +147,7 @@ func (h *Handler) createAdminToken(c echo.Context, password string) error {
// This will take collect some information about the requested refresh, validate and then return a new jwt token if approved.
// Register
// @Summary Generates a new token
// @Router /v1/users/refreshToken [post]
// @Router /v1/users/refresh/token [post]
// @Param request body domain.RefreshTokenRequest true "body"
// @Tags Users
// @Success 200 {object} domain.LoginResponse
@ -183,7 +178,7 @@ func (h *Handler) RefreshJwtToken(c echo.Context) error {
}
userScopes := strings.Split(user.Scopes, ",")
jwt, err := h.generateJwtWithExp(request.Username, h.config.ServerAddress, "", userScopes, user.ID, time.Now().Add(time.Hour*48))
jwt, err := h.generateJwtWithExp(request.Username, h.config.ServerAddress, user.SessionToken, userScopes, user.ID, time.Now().Add(time.Hour*48))
if err != nil {
return h.InternalServerErrorResponse(c, err.Error())
}
@ -281,16 +276,18 @@ func (h *Handler) RemoveScopes(c echo.Context) error {
// @Failure 400 {object} domain.BaseResponse
// @Failure 500 {object} domain.BaseResponse
// @Security Bearer
func (h *Handler) LogoutEverywhere(c echo.Context) error {
func (h *Handler) NewSessionToken(c echo.Context) error {
token, err := h.getJwtTokenFromContext(c)
if err != nil {
return h.WriteError(c, err, http.StatusUnauthorized)
}
err = token.IsValid(domain.ScopeAll)
_, err = h.repo.Users.NewSessionToken(c.Request().Context(), token.UserName)
if err != nil {
return h.WriteError(c, err, http.StatusUnauthorized)
return h.WriteError(c, err, http.StatusInternalServerError)
}
return c.String(http.StatusInternalServerError, "Not Implemented")
return c.JSON(http.StatusInternalServerError, domain.BaseResponse{
Message: "OK",
})
}

View File

@ -27,8 +27,8 @@ type UserServices interface {
GetUser(ctx context.Context, username string) (entity.UserEntity, error)
AddScopes(ctx context.Context, username string, scopes []string) error
RemoveScopes(ctx context.Context, username string, scopes []string) error
Create(ctx context.Context, name, password, sessionToken, scope string) (entity.UserEntity, error)
NewSessionToken(ctx context.Context, name string) error
Create(ctx context.Context, name, password, scope string) (entity.UserEntity, error)
NewSessionToken(ctx context.Context, name string) (string, error)
CheckPasswordForRequirements(password string) error
}
@ -128,32 +128,37 @@ func (us UserService) doesScopeExist(scopes []string, target string) bool {
return false
}
func (us UserService) Create(ctx context.Context, name, password, sessionToken, scope string) (entity.UserEntity, error) {
func (us UserService) Create(ctx context.Context, name, password, scope string) (entity.UserEntity, error) {
err := us.CheckPasswordForRequirements(password)
if err != nil {
return entity.UserEntity{}, err
}
us.repo.Create(ctx, name, password, sessionToken, domain.ScopeArticleRead)
token, err := uuid.NewV7()
if err != nil {
return entity.UserEntity{}, err
}
us.repo.Create(ctx, name, password, token.String(), domain.ScopeArticleRead)
return entity.UserEntity{}, nil
}
func (us UserService) NewSessionToken(ctx context.Context, name string) error {
func (us UserService) NewSessionToken(ctx context.Context, name string) (string, error) {
token, err := uuid.NewV7()
if err != nil {
return err
return "", err
}
rows, err := us.repo.UpdateSessionToken(ctx, name, token.String())
if err != nil {
return err
return "", err
}
if rows != 1 {
return fmt.Errorf("UserService.NewSessionToken %w", err)
return "", fmt.Errorf("UserService.NewSessionToken %w", err)
}
return nil
return token.String(), nil
}
func (us UserService) CheckPasswordForRequirements(password string) error {