Compare commits
No commits in common. "877a6a9619d4f9d92c1cc08f67324affd6e57356" and "47058dd866b5f0a33e80edf831739156dcf3ab07" have entirely different histories.
877a6a9619
...
47058dd866
@ -60,7 +60,7 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/v1/articles/by/sourceId": {
|
"/v1/articles/by/sourceid": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
@ -1035,7 +1035,7 @@ const docTemplate = `{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/v1/users/refresh/token": {
|
"/v1/users/refreshToken": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
@ -1350,6 +1350,9 @@ const docTemplate = `{
|
|||||||
"refreshToken": {
|
"refreshToken": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"sessionToken": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"token": {
|
"token": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
@ -51,7 +51,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/v1/articles/by/sourceId": {
|
"/v1/articles/by/sourceid": {
|
||||||
"get": {
|
"get": {
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
@ -1026,7 +1026,7 @@
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
"/v1/users/refresh/token": {
|
"/v1/users/refreshToken": {
|
||||||
"post": {
|
"post": {
|
||||||
"security": [
|
"security": [
|
||||||
{
|
{
|
||||||
@ -1341,6 +1341,9 @@
|
|||||||
"refreshToken": {
|
"refreshToken": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
"sessionToken": {
|
||||||
|
"type": "string"
|
||||||
|
},
|
||||||
"token": {
|
"token": {
|
||||||
"type": "string"
|
"type": "string"
|
||||||
},
|
},
|
||||||
|
@ -84,6 +84,8 @@ definitions:
|
|||||||
type: string
|
type: string
|
||||||
refreshToken:
|
refreshToken:
|
||||||
type: string
|
type: string
|
||||||
|
sessionToken:
|
||||||
|
type: string
|
||||||
token:
|
token:
|
||||||
type: string
|
type: string
|
||||||
type:
|
type:
|
||||||
@ -219,7 +221,7 @@ paths:
|
|||||||
summary: Returns an article and source based on defined ID.
|
summary: Returns an article and source based on defined ID.
|
||||||
tags:
|
tags:
|
||||||
- Articles
|
- Articles
|
||||||
/v1/articles/by/sourceId:
|
/v1/articles/by/sourceid:
|
||||||
get:
|
get:
|
||||||
parameters:
|
parameters:
|
||||||
- description: source id
|
- description: source id
|
||||||
@ -774,7 +776,7 @@ paths:
|
|||||||
summary: Revokes the current session token and replaces it with a new one.
|
summary: Revokes the current session token and replaces it with a new one.
|
||||||
tags:
|
tags:
|
||||||
- Users
|
- Users
|
||||||
/v1/users/refresh/token:
|
/v1/users/refreshToken:
|
||||||
post:
|
post:
|
||||||
parameters:
|
parameters:
|
||||||
- description: body
|
- description: body
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package domain
|
package domain
|
||||||
|
|
||||||
|
|
||||||
type BaseResponse struct {
|
type BaseResponse struct {
|
||||||
Message string `json:"message"`
|
Message string `json:"message"`
|
||||||
}
|
}
|
||||||
@ -9,6 +10,7 @@ type LoginResponse struct {
|
|||||||
Token string `json:"token"`
|
Token string `json:"token"`
|
||||||
Type string `json:"type"`
|
Type string `json:"type"`
|
||||||
RefreshToken string `json:"refreshToken"`
|
RefreshToken string `json:"refreshToken"`
|
||||||
|
SessionToken string `json:"sessionToken"`
|
||||||
}
|
}
|
||||||
|
|
||||||
type ArticleResponse struct {
|
type ArticleResponse struct {
|
||||||
@ -34,4 +36,4 @@ type DiscordWebhookResponse struct {
|
|||||||
type SourcesResponse struct {
|
type SourcesResponse struct {
|
||||||
BaseResponse
|
BaseResponse
|
||||||
Payload []SourceDto `json:"payload"`
|
Payload []SourceDto `json:"payload"`
|
||||||
}
|
}
|
@ -135,7 +135,7 @@ func (s *Handler) getArticleDetails(c echo.Context) error {
|
|||||||
// @Param page query int false "Page to query"
|
// @Param page query int false "Page to query"
|
||||||
// @Produce application/json
|
// @Produce application/json
|
||||||
// @Tags Articles
|
// @Tags Articles
|
||||||
// @Router /v1/articles/by/sourceId [get]
|
// @Router /v1/articles/by/sourceid [get]
|
||||||
// @Success 200 {object} domain.ArticleResponse "OK"
|
// @Success 200 {object} domain.ArticleResponse "OK"
|
||||||
// @Failure 400 {object} domain.BaseResponse
|
// @Failure 400 {object} domain.BaseResponse
|
||||||
// @Failure 500 {object} domain.BaseResponse
|
// @Failure 500 {object} domain.BaseResponse
|
||||||
|
@ -110,8 +110,7 @@ func NewServer(ctx context.Context, configs services.Configs, conn *sql.DB) *Han
|
|||||||
users.Use(echojwt.WithConfig(jwtConfig))
|
users.Use(echojwt.WithConfig(jwtConfig))
|
||||||
users.POST("/scopes/add", s.AddScopes)
|
users.POST("/scopes/add", s.AddScopes)
|
||||||
users.POST("/scopes/remove", s.RemoveScopes)
|
users.POST("/scopes/remove", s.RemoveScopes)
|
||||||
users.POST("/refresh/token", s.RefreshJwtToken)
|
users.POST("/refreshToken", s.RefreshJwtToken)
|
||||||
users.POST("/refresh/sessionToken", s.NewSessionToken)
|
|
||||||
|
|
||||||
s.Router = router
|
s.Router = router
|
||||||
return s
|
return s
|
||||||
@ -162,24 +161,18 @@ func (s *Handler) ValidateJwtToken(c echo.Context, requiredScope string) (JwtTok
|
|||||||
err = token.hasExpired()
|
err = token.hasExpired()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return JwtToken{}, errors.New(ErrJwtExpired)
|
return JwtToken{}, errors.New(ErrJwtExpired)
|
||||||
|
//s.WriteMessage(c, ErrJwtExpired, http.StatusUnauthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
err = token.hasScope(requiredScope)
|
err = token.hasScope(requiredScope)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return JwtToken{}, errors.New(ErrJwtScopeMissing)
|
return JwtToken{}, errors.New(ErrJwtScopeMissing)
|
||||||
|
//s.WriteMessage(c, ErrJwtScopeMissing, http.StatusUnauthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
if token.Iss != s.config.ServerAddress {
|
if token.Iss != s.config.ServerAddress {
|
||||||
return JwtToken{}, errors.New(ErrJwtInvalidIssuer)
|
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
|
return token, nil
|
||||||
|
@ -19,13 +19,12 @@ const (
|
|||||||
)
|
)
|
||||||
|
|
||||||
type JwtToken struct {
|
type JwtToken struct {
|
||||||
Exp time.Time `json:"exp"`
|
Exp time.Time `json:"exp"`
|
||||||
Iss string `json:"iss"`
|
Iss string `json:"iss"`
|
||||||
Authorized bool `json:"authorized"`
|
Authorized bool `json:"authorized"`
|
||||||
UserName string `json:"username"`
|
UserName string `json:"username"`
|
||||||
UserId int64 `json:"userId"`
|
UserId int64 `json:"userId"`
|
||||||
Scopes []string `json:"scopes"`
|
Scopes []string `json:"scopes"`
|
||||||
SessionToken string `json:"sessionToken"`
|
|
||||||
jwt.RegisteredClaims
|
jwt.RegisteredClaims
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -49,7 +49,7 @@ func (h *Handler) AuthRegister(c echo.Context) error {
|
|||||||
return h.WriteError(c, err, http.StatusInternalServerError)
|
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 {
|
if err != nil {
|
||||||
return h.InternalServerErrorResponse(c, err.Error())
|
return h.InternalServerErrorResponse(c, err.Error())
|
||||||
}
|
}
|
||||||
@ -92,8 +92,12 @@ func (h *Handler) AuthLogin(c echo.Context) error {
|
|||||||
// TODO think about moving this down some?
|
// TODO think about moving this down some?
|
||||||
expiresAt := time.Now().Add(time.Hour * 48)
|
expiresAt := time.Now().Add(time.Hour * 48)
|
||||||
userScopes := strings.Split(user.Scopes, ",")
|
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, user.SessionToken, userScopes, user.ID, expiresAt)
|
jwt, err := h.generateJwtWithExp(username, h.config.ServerAddress, sessionToken.String(), userScopes, user.ID, expiresAt)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return h.InternalServerErrorResponse(c, err.Error())
|
return h.InternalServerErrorResponse(c, err.Error())
|
||||||
}
|
}
|
||||||
@ -110,6 +114,7 @@ func (h *Handler) AuthLogin(c echo.Context) error {
|
|||||||
Token: jwt,
|
Token: jwt,
|
||||||
Type: "Bearer",
|
Type: "Bearer",
|
||||||
RefreshToken: refresh,
|
RefreshToken: refresh,
|
||||||
|
SessionToken: sessionToken.String(),
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -147,7 +152,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.
|
// This will take collect some information about the requested refresh, validate and then return a new jwt token if approved.
|
||||||
// Register
|
// Register
|
||||||
// @Summary Generates a new token
|
// @Summary Generates a new token
|
||||||
// @Router /v1/users/refresh/token [post]
|
// @Router /v1/users/refreshToken [post]
|
||||||
// @Param request body domain.RefreshTokenRequest true "body"
|
// @Param request body domain.RefreshTokenRequest true "body"
|
||||||
// @Tags Users
|
// @Tags Users
|
||||||
// @Success 200 {object} domain.LoginResponse
|
// @Success 200 {object} domain.LoginResponse
|
||||||
@ -178,7 +183,7 @@ func (h *Handler) RefreshJwtToken(c echo.Context) error {
|
|||||||
}
|
}
|
||||||
userScopes := strings.Split(user.Scopes, ",")
|
userScopes := strings.Split(user.Scopes, ",")
|
||||||
|
|
||||||
jwt, err := h.generateJwtWithExp(request.Username, h.config.ServerAddress, user.SessionToken, userScopes, user.ID, time.Now().Add(time.Hour*48))
|
jwt, err := h.generateJwtWithExp(request.Username, h.config.ServerAddress, "", userScopes, user.ID, time.Now().Add(time.Hour*48))
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return h.InternalServerErrorResponse(c, err.Error())
|
return h.InternalServerErrorResponse(c, err.Error())
|
||||||
}
|
}
|
||||||
@ -276,18 +281,16 @@ func (h *Handler) RemoveScopes(c echo.Context) error {
|
|||||||
// @Failure 400 {object} domain.BaseResponse
|
// @Failure 400 {object} domain.BaseResponse
|
||||||
// @Failure 500 {object} domain.BaseResponse
|
// @Failure 500 {object} domain.BaseResponse
|
||||||
// @Security Bearer
|
// @Security Bearer
|
||||||
func (h *Handler) NewSessionToken(c echo.Context) error {
|
func (h *Handler) LogoutEverywhere(c echo.Context) error {
|
||||||
token, err := h.getJwtTokenFromContext(c)
|
token, err := h.getJwtTokenFromContext(c)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return h.WriteError(c, err, http.StatusUnauthorized)
|
return h.WriteError(c, err, http.StatusUnauthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
_, err = h.repo.Users.NewSessionToken(c.Request().Context(), token.UserName)
|
err = token.IsValid(domain.ScopeAll)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return h.WriteError(c, err, http.StatusInternalServerError)
|
return h.WriteError(c, err, http.StatusUnauthorized)
|
||||||
}
|
}
|
||||||
|
|
||||||
return c.JSON(http.StatusInternalServerError, domain.BaseResponse{
|
return c.String(http.StatusInternalServerError, "Not Implemented")
|
||||||
Message: "OK",
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
@ -27,8 +27,8 @@ type UserServices interface {
|
|||||||
GetUser(ctx context.Context, username string) (entity.UserEntity, error)
|
GetUser(ctx context.Context, username string) (entity.UserEntity, error)
|
||||||
AddScopes(ctx context.Context, username string, scopes []string) error
|
AddScopes(ctx context.Context, username string, scopes []string) error
|
||||||
RemoveScopes(ctx context.Context, username string, scopes []string) error
|
RemoveScopes(ctx context.Context, username string, scopes []string) error
|
||||||
Create(ctx context.Context, name, password, scope string) (entity.UserEntity, error)
|
Create(ctx context.Context, name, password, sessionToken, scope string) (entity.UserEntity, error)
|
||||||
NewSessionToken(ctx context.Context, name string) (string, error)
|
NewSessionToken(ctx context.Context, name string) error
|
||||||
CheckPasswordForRequirements(password string) error
|
CheckPasswordForRequirements(password string) error
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -128,37 +128,32 @@ func (us UserService) doesScopeExist(scopes []string, target string) bool {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func (us UserService) Create(ctx context.Context, name, password, scope string) (entity.UserEntity, error) {
|
func (us UserService) Create(ctx context.Context, name, password, sessionToken, scope string) (entity.UserEntity, error) {
|
||||||
err := us.CheckPasswordForRequirements(password)
|
err := us.CheckPasswordForRequirements(password)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return entity.UserEntity{}, err
|
return entity.UserEntity{}, err
|
||||||
}
|
}
|
||||||
|
|
||||||
token, err := uuid.NewV7()
|
us.repo.Create(ctx, name, password, sessionToken, domain.ScopeArticleRead)
|
||||||
if err != nil {
|
|
||||||
return entity.UserEntity{}, err
|
|
||||||
}
|
|
||||||
|
|
||||||
us.repo.Create(ctx, name, password, token.String(), domain.ScopeArticleRead)
|
|
||||||
return entity.UserEntity{}, nil
|
return entity.UserEntity{}, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (us UserService) NewSessionToken(ctx context.Context, name string) (string, error) {
|
func (us UserService) NewSessionToken(ctx context.Context, name string) error {
|
||||||
token, err := uuid.NewV7()
|
token, err := uuid.NewV7()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
rows, err := us.repo.UpdateSessionToken(ctx, name, token.String())
|
rows, err := us.repo.UpdateSessionToken(ctx, name, token.String())
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
if rows != 1 {
|
if rows != 1 {
|
||||||
return "", fmt.Errorf("UserService.NewSessionToken %w", err)
|
return fmt.Errorf("UserService.NewSessionToken %w", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
return token.String(), nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
func (us UserService) CheckPasswordForRequirements(password string) error {
|
func (us UserService) CheckPasswordForRequirements(password string) error {
|
||||||
|
Loading…
Reference in New Issue
Block a user