From bbe57e07d59ed06bb8cfdae815b570c99c3886ef Mon Sep 17 00:00:00 2001 From: nihonium Date: Sat, 15 Nov 2025 02:53:25 +0300 Subject: [PATCH 1/2] feat: initial auth service support --- Dockerfiles/Dockerfile_auth | 6 + auth/auth.gen.go | 329 ++++++++++++++++++++++++++++++ auth/auth/auth.gen.go | 329 ++++++++++++++++++++++++++++++ auth/oapi-auth-codegen.yaml | 6 + auth/openapi-auth.yaml | 112 ++++++++++ go.mod | 3 +- go.sum | 2 + modules/auth/handlers/handlers.go | 108 ++++++++++ modules/auth/main.go | 38 ++++ modules/auth/types.go | 6 + 10 files changed, 938 insertions(+), 1 deletion(-) create mode 100644 Dockerfiles/Dockerfile_auth create mode 100644 auth/auth.gen.go create mode 100644 auth/auth/auth.gen.go create mode 100644 auth/oapi-auth-codegen.yaml create mode 100644 auth/openapi-auth.yaml create mode 100644 modules/auth/handlers/handlers.go create mode 100644 modules/auth/main.go create mode 100644 modules/auth/types.go diff --git a/Dockerfiles/Dockerfile_auth b/Dockerfiles/Dockerfile_auth new file mode 100644 index 0000000..5280e86 --- /dev/null +++ b/Dockerfiles/Dockerfile_auth @@ -0,0 +1,6 @@ +FROM ubuntu:22.04 + +WORKDIR /app +COPY --chmod=755 modules/auth/auth /app +EXPOSE 8082 +ENTRYPOINT ["/app/auth"] \ No newline at end of file diff --git a/auth/auth.gen.go b/auth/auth.gen.go new file mode 100644 index 0000000..1f16575 --- /dev/null +++ b/auth/auth.gen.go @@ -0,0 +1,329 @@ +// Package auth provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.5.0 DO NOT EDIT. +package auth + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/gin-gonic/gin" + strictgin "github.com/oapi-codegen/runtime/strictmiddleware/gin" +) + +// PostAuthSignInJSONBody defines parameters for PostAuthSignIn. +type PostAuthSignInJSONBody struct { + Nickname string `json:"nickname"` + Pass string `json:"pass"` +} + +// PostAuthSignUpJSONBody defines parameters for PostAuthSignUp. +type PostAuthSignUpJSONBody struct { + Nickname string `json:"nickname"` + Pass string `json:"pass"` +} + +// PostAuthVerifyTokenJSONBody defines parameters for PostAuthVerifyToken. +type PostAuthVerifyTokenJSONBody struct { + // Token JWT token to validate + Token string `json:"token"` +} + +// PostAuthSignInJSONRequestBody defines body for PostAuthSignIn for application/json ContentType. +type PostAuthSignInJSONRequestBody PostAuthSignInJSONBody + +// PostAuthSignUpJSONRequestBody defines body for PostAuthSignUp for application/json ContentType. +type PostAuthSignUpJSONRequestBody PostAuthSignUpJSONBody + +// PostAuthVerifyTokenJSONRequestBody defines body for PostAuthVerifyToken for application/json ContentType. +type PostAuthVerifyTokenJSONRequestBody PostAuthVerifyTokenJSONBody + +// ServerInterface represents all server handlers. +type ServerInterface interface { + // Sign in a user and return JWT + // (POST /auth/sign-in) + PostAuthSignIn(c *gin.Context) + // Sign up a new user + // (POST /auth/sign-up) + PostAuthSignUp(c *gin.Context) + // Verify JWT validity + // (POST /auth/verify-token) + PostAuthVerifyToken(c *gin.Context) +} + +// ServerInterfaceWrapper converts contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface + HandlerMiddlewares []MiddlewareFunc + ErrorHandler func(*gin.Context, error, int) +} + +type MiddlewareFunc func(c *gin.Context) + +// PostAuthSignIn operation middleware +func (siw *ServerInterfaceWrapper) PostAuthSignIn(c *gin.Context) { + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.PostAuthSignIn(c) +} + +// PostAuthSignUp operation middleware +func (siw *ServerInterfaceWrapper) PostAuthSignUp(c *gin.Context) { + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.PostAuthSignUp(c) +} + +// PostAuthVerifyToken operation middleware +func (siw *ServerInterfaceWrapper) PostAuthVerifyToken(c *gin.Context) { + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.PostAuthVerifyToken(c) +} + +// GinServerOptions provides options for the Gin server. +type GinServerOptions struct { + BaseURL string + Middlewares []MiddlewareFunc + ErrorHandler func(*gin.Context, error, int) +} + +// RegisterHandlers creates http.Handler with routing matching OpenAPI spec. +func RegisterHandlers(router gin.IRouter, si ServerInterface) { + RegisterHandlersWithOptions(router, si, GinServerOptions{}) +} + +// RegisterHandlersWithOptions creates http.Handler with additional options +func RegisterHandlersWithOptions(router gin.IRouter, si ServerInterface, options GinServerOptions) { + errorHandler := options.ErrorHandler + if errorHandler == nil { + errorHandler = func(c *gin.Context, err error, statusCode int) { + c.JSON(statusCode, gin.H{"msg": err.Error()}) + } + } + + wrapper := ServerInterfaceWrapper{ + Handler: si, + HandlerMiddlewares: options.Middlewares, + ErrorHandler: errorHandler, + } + + router.POST(options.BaseURL+"/auth/sign-in", wrapper.PostAuthSignIn) + router.POST(options.BaseURL+"/auth/sign-up", wrapper.PostAuthSignUp) + router.POST(options.BaseURL+"/auth/verify-token", wrapper.PostAuthVerifyToken) +} + +type PostAuthSignInRequestObject struct { + Body *PostAuthSignInJSONRequestBody +} + +type PostAuthSignInResponseObject interface { + VisitPostAuthSignInResponse(w http.ResponseWriter) error +} + +type PostAuthSignIn200JSONResponse struct { + Error *string `json:"error"` + Success *bool `json:"success,omitempty"` + + // Token JWT token to access protected endpoints + Token *string `json:"token"` + UserId *string `json:"user_id"` +} + +func (response PostAuthSignIn200JSONResponse) VisitPostAuthSignInResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type PostAuthSignUpRequestObject struct { + Body *PostAuthSignUpJSONRequestBody +} + +type PostAuthSignUpResponseObject interface { + VisitPostAuthSignUpResponse(w http.ResponseWriter) error +} + +type PostAuthSignUp200JSONResponse struct { + Error *string `json:"error"` + Success *bool `json:"success,omitempty"` + UserId *string `json:"user_id"` +} + +func (response PostAuthSignUp200JSONResponse) VisitPostAuthSignUpResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type PostAuthVerifyTokenRequestObject struct { + Body *PostAuthVerifyTokenJSONRequestBody +} + +type PostAuthVerifyTokenResponseObject interface { + VisitPostAuthVerifyTokenResponse(w http.ResponseWriter) error +} + +type PostAuthVerifyToken200JSONResponse struct { + // Error Error message if token is invalid + Error *string `json:"error"` + + // UserId User ID extracted from token if valid + UserId *string `json:"user_id"` + + // Valid True if token is valid + Valid *bool `json:"valid,omitempty"` +} + +func (response PostAuthVerifyToken200JSONResponse) VisitPostAuthVerifyTokenResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +// StrictServerInterface represents all server handlers. +type StrictServerInterface interface { + // Sign in a user and return JWT + // (POST /auth/sign-in) + PostAuthSignIn(ctx context.Context, request PostAuthSignInRequestObject) (PostAuthSignInResponseObject, error) + // Sign up a new user + // (POST /auth/sign-up) + PostAuthSignUp(ctx context.Context, request PostAuthSignUpRequestObject) (PostAuthSignUpResponseObject, error) + // Verify JWT validity + // (POST /auth/verify-token) + PostAuthVerifyToken(ctx context.Context, request PostAuthVerifyTokenRequestObject) (PostAuthVerifyTokenResponseObject, error) +} + +type StrictHandlerFunc = strictgin.StrictGinHandlerFunc +type StrictMiddlewareFunc = strictgin.StrictGinMiddlewareFunc + +func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { + return &strictHandler{ssi: ssi, middlewares: middlewares} +} + +type strictHandler struct { + ssi StrictServerInterface + middlewares []StrictMiddlewareFunc +} + +// PostAuthSignIn operation middleware +func (sh *strictHandler) PostAuthSignIn(ctx *gin.Context) { + var request PostAuthSignInRequestObject + + var body PostAuthSignInJSONRequestBody + if err := ctx.ShouldBindJSON(&body); err != nil { + ctx.Status(http.StatusBadRequest) + ctx.Error(err) + return + } + request.Body = &body + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostAuthSignIn(ctx, request.(PostAuthSignInRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostAuthSignIn") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(PostAuthSignInResponseObject); ok { + if err := validResponse.VisitPostAuthSignInResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PostAuthSignUp operation middleware +func (sh *strictHandler) PostAuthSignUp(ctx *gin.Context) { + var request PostAuthSignUpRequestObject + + var body PostAuthSignUpJSONRequestBody + if err := ctx.ShouldBindJSON(&body); err != nil { + ctx.Status(http.StatusBadRequest) + ctx.Error(err) + return + } + request.Body = &body + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostAuthSignUp(ctx, request.(PostAuthSignUpRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostAuthSignUp") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(PostAuthSignUpResponseObject); ok { + if err := validResponse.VisitPostAuthSignUpResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PostAuthVerifyToken operation middleware +func (sh *strictHandler) PostAuthVerifyToken(ctx *gin.Context) { + var request PostAuthVerifyTokenRequestObject + + var body PostAuthVerifyTokenJSONRequestBody + if err := ctx.ShouldBindJSON(&body); err != nil { + ctx.Status(http.StatusBadRequest) + ctx.Error(err) + return + } + request.Body = &body + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostAuthVerifyToken(ctx, request.(PostAuthVerifyTokenRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostAuthVerifyToken") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(PostAuthVerifyTokenResponseObject); ok { + if err := validResponse.VisitPostAuthVerifyTokenResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} diff --git a/auth/auth/auth.gen.go b/auth/auth/auth.gen.go new file mode 100644 index 0000000..12b6622 --- /dev/null +++ b/auth/auth/auth.gen.go @@ -0,0 +1,329 @@ +// Package oapi_auth provides primitives to interact with the openapi HTTP API. +// +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.5.0 DO NOT EDIT. +package oapi_auth + +import ( + "context" + "encoding/json" + "fmt" + "net/http" + + "github.com/gin-gonic/gin" + strictgin "github.com/oapi-codegen/runtime/strictmiddleware/gin" +) + +// PostAuthSignInJSONBody defines parameters for PostAuthSignIn. +type PostAuthSignInJSONBody struct { + Nickname string `json:"nickname"` + Pass string `json:"pass"` +} + +// PostAuthSignUpJSONBody defines parameters for PostAuthSignUp. +type PostAuthSignUpJSONBody struct { + Nickname string `json:"nickname"` + Pass string `json:"pass"` +} + +// PostAuthVerifyTokenJSONBody defines parameters for PostAuthVerifyToken. +type PostAuthVerifyTokenJSONBody struct { + // Token JWT token to validate + Token string `json:"token"` +} + +// PostAuthSignInJSONRequestBody defines body for PostAuthSignIn for application/json ContentType. +type PostAuthSignInJSONRequestBody PostAuthSignInJSONBody + +// PostAuthSignUpJSONRequestBody defines body for PostAuthSignUp for application/json ContentType. +type PostAuthSignUpJSONRequestBody PostAuthSignUpJSONBody + +// PostAuthVerifyTokenJSONRequestBody defines body for PostAuthVerifyToken for application/json ContentType. +type PostAuthVerifyTokenJSONRequestBody PostAuthVerifyTokenJSONBody + +// ServerInterface represents all server handlers. +type ServerInterface interface { + // Sign in a user and return JWT + // (POST /auth/sign-in) + PostAuthSignIn(c *gin.Context) + // Sign up a new user + // (POST /auth/sign-up) + PostAuthSignUp(c *gin.Context) + // Verify JWT validity + // (POST /auth/verify-token) + PostAuthVerifyToken(c *gin.Context) +} + +// ServerInterfaceWrapper converts contexts to parameters. +type ServerInterfaceWrapper struct { + Handler ServerInterface + HandlerMiddlewares []MiddlewareFunc + ErrorHandler func(*gin.Context, error, int) +} + +type MiddlewareFunc func(c *gin.Context) + +// PostAuthSignIn operation middleware +func (siw *ServerInterfaceWrapper) PostAuthSignIn(c *gin.Context) { + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.PostAuthSignIn(c) +} + +// PostAuthSignUp operation middleware +func (siw *ServerInterfaceWrapper) PostAuthSignUp(c *gin.Context) { + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.PostAuthSignUp(c) +} + +// PostAuthVerifyToken operation middleware +func (siw *ServerInterfaceWrapper) PostAuthVerifyToken(c *gin.Context) { + + for _, middleware := range siw.HandlerMiddlewares { + middleware(c) + if c.IsAborted() { + return + } + } + + siw.Handler.PostAuthVerifyToken(c) +} + +// GinServerOptions provides options for the Gin server. +type GinServerOptions struct { + BaseURL string + Middlewares []MiddlewareFunc + ErrorHandler func(*gin.Context, error, int) +} + +// RegisterHandlers creates http.Handler with routing matching OpenAPI spec. +func RegisterHandlers(router gin.IRouter, si ServerInterface) { + RegisterHandlersWithOptions(router, si, GinServerOptions{}) +} + +// RegisterHandlersWithOptions creates http.Handler with additional options +func RegisterHandlersWithOptions(router gin.IRouter, si ServerInterface, options GinServerOptions) { + errorHandler := options.ErrorHandler + if errorHandler == nil { + errorHandler = func(c *gin.Context, err error, statusCode int) { + c.JSON(statusCode, gin.H{"msg": err.Error()}) + } + } + + wrapper := ServerInterfaceWrapper{ + Handler: si, + HandlerMiddlewares: options.Middlewares, + ErrorHandler: errorHandler, + } + + router.POST(options.BaseURL+"/auth/sign-in", wrapper.PostAuthSignIn) + router.POST(options.BaseURL+"/auth/sign-up", wrapper.PostAuthSignUp) + router.POST(options.BaseURL+"/auth/verify-token", wrapper.PostAuthVerifyToken) +} + +type PostAuthSignInRequestObject struct { + Body *PostAuthSignInJSONRequestBody +} + +type PostAuthSignInResponseObject interface { + VisitPostAuthSignInResponse(w http.ResponseWriter) error +} + +type PostAuthSignIn200JSONResponse struct { + Error *string `json:"error"` + Success *bool `json:"success,omitempty"` + + // Token JWT token to access protected endpoints + Token *string `json:"token"` + UserId *string `json:"user_id"` +} + +func (response PostAuthSignIn200JSONResponse) VisitPostAuthSignInResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type PostAuthSignUpRequestObject struct { + Body *PostAuthSignUpJSONRequestBody +} + +type PostAuthSignUpResponseObject interface { + VisitPostAuthSignUpResponse(w http.ResponseWriter) error +} + +type PostAuthSignUp200JSONResponse struct { + Error *string `json:"error"` + Success *bool `json:"success,omitempty"` + UserId *string `json:"user_id"` +} + +func (response PostAuthSignUp200JSONResponse) VisitPostAuthSignUpResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +type PostAuthVerifyTokenRequestObject struct { + Body *PostAuthVerifyTokenJSONRequestBody +} + +type PostAuthVerifyTokenResponseObject interface { + VisitPostAuthVerifyTokenResponse(w http.ResponseWriter) error +} + +type PostAuthVerifyToken200JSONResponse struct { + // Error Error message if token is invalid + Error *string `json:"error"` + + // UserId User ID extracted from token if valid + UserId *string `json:"user_id"` + + // Valid True if token is valid + Valid *bool `json:"valid,omitempty"` +} + +func (response PostAuthVerifyToken200JSONResponse) VisitPostAuthVerifyTokenResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(200) + + return json.NewEncoder(w).Encode(response) +} + +// StrictServerInterface represents all server handlers. +type StrictServerInterface interface { + // Sign in a user and return JWT + // (POST /auth/sign-in) + PostAuthSignIn(ctx context.Context, request PostAuthSignInRequestObject) (PostAuthSignInResponseObject, error) + // Sign up a new user + // (POST /auth/sign-up) + PostAuthSignUp(ctx context.Context, request PostAuthSignUpRequestObject) (PostAuthSignUpResponseObject, error) + // Verify JWT validity + // (POST /auth/verify-token) + PostAuthVerifyToken(ctx context.Context, request PostAuthVerifyTokenRequestObject) (PostAuthVerifyTokenResponseObject, error) +} + +type StrictHandlerFunc = strictgin.StrictGinHandlerFunc +type StrictMiddlewareFunc = strictgin.StrictGinMiddlewareFunc + +func NewStrictHandler(ssi StrictServerInterface, middlewares []StrictMiddlewareFunc) ServerInterface { + return &strictHandler{ssi: ssi, middlewares: middlewares} +} + +type strictHandler struct { + ssi StrictServerInterface + middlewares []StrictMiddlewareFunc +} + +// PostAuthSignIn operation middleware +func (sh *strictHandler) PostAuthSignIn(ctx *gin.Context) { + var request PostAuthSignInRequestObject + + var body PostAuthSignInJSONRequestBody + if err := ctx.ShouldBindJSON(&body); err != nil { + ctx.Status(http.StatusBadRequest) + ctx.Error(err) + return + } + request.Body = &body + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostAuthSignIn(ctx, request.(PostAuthSignInRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostAuthSignIn") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(PostAuthSignInResponseObject); ok { + if err := validResponse.VisitPostAuthSignInResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PostAuthSignUp operation middleware +func (sh *strictHandler) PostAuthSignUp(ctx *gin.Context) { + var request PostAuthSignUpRequestObject + + var body PostAuthSignUpJSONRequestBody + if err := ctx.ShouldBindJSON(&body); err != nil { + ctx.Status(http.StatusBadRequest) + ctx.Error(err) + return + } + request.Body = &body + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostAuthSignUp(ctx, request.(PostAuthSignUpRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostAuthSignUp") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(PostAuthSignUpResponseObject); ok { + if err := validResponse.VisitPostAuthSignUpResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} + +// PostAuthVerifyToken operation middleware +func (sh *strictHandler) PostAuthVerifyToken(ctx *gin.Context) { + var request PostAuthVerifyTokenRequestObject + + var body PostAuthVerifyTokenJSONRequestBody + if err := ctx.ShouldBindJSON(&body); err != nil { + ctx.Status(http.StatusBadRequest) + ctx.Error(err) + return + } + request.Body = &body + + handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { + return sh.ssi.PostAuthVerifyToken(ctx, request.(PostAuthVerifyTokenRequestObject)) + } + for _, middleware := range sh.middlewares { + handler = middleware(handler, "PostAuthVerifyToken") + } + + response, err := handler(ctx, request) + + if err != nil { + ctx.Error(err) + ctx.Status(http.StatusInternalServerError) + } else if validResponse, ok := response.(PostAuthVerifyTokenResponseObject); ok { + if err := validResponse.VisitPostAuthVerifyTokenResponse(ctx.Writer); err != nil { + ctx.Error(err) + } + } else if response != nil { + ctx.Error(fmt.Errorf("unexpected response type: %T", response)) + } +} diff --git a/auth/oapi-auth-codegen.yaml b/auth/oapi-auth-codegen.yaml new file mode 100644 index 0000000..6792391 --- /dev/null +++ b/auth/oapi-auth-codegen.yaml @@ -0,0 +1,6 @@ +package: auth +generate: + strict-server: true + gin-server: true + models: true +output: auth/auth.gen.go \ No newline at end of file diff --git a/auth/openapi-auth.yaml b/auth/openapi-auth.yaml new file mode 100644 index 0000000..7ffc60e --- /dev/null +++ b/auth/openapi-auth.yaml @@ -0,0 +1,112 @@ +openapi: 3.1.0 +info: + title: Auth Service + version: 1.0.0 + +paths: + /auth/sign-up: + post: + summary: Sign up a new user + tags: [Auth] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [nickname, pass] + properties: + nickname: + type: string + pass: + type: string + format: password + responses: + "200": + description: Sign-up result + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + error: + type: string + nullable: true + user_id: + type: string + nullable: true + + /auth/sign-in: + post: + summary: Sign in a user and return JWT + tags: [Auth] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [nickname, pass] + properties: + nickname: + type: string + pass: + type: string + format: password + responses: + "200": + description: Sign-in result with JWT + content: + application/json: + schema: + type: object + properties: + success: + type: boolean + error: + type: string + nullable: true + user_id: + type: string + nullable: true + token: + type: string + description: JWT token to access protected endpoints + nullable: true + + /auth/verify-token: + post: + summary: Verify JWT validity + tags: [Auth] + requestBody: + required: true + content: + application/json: + schema: + type: object + required: [token] + properties: + token: + type: string + description: JWT token to validate + responses: + "200": + description: Token validation result + content: + application/json: + schema: + type: object + properties: + valid: + type: boolean + description: True if token is valid + user_id: + type: string + nullable: true + description: User ID extracted from token if valid + error: + type: string + nullable: true + description: Error message if token is invalid \ No newline at end of file diff --git a/go.mod b/go.mod index b7a66f2..4089c02 100644 --- a/go.mod +++ b/go.mod @@ -5,10 +5,10 @@ go 1.25.0 require ( github.com/gin-contrib/cors v1.7.6 github.com/gin-gonic/gin v1.11.0 + github.com/golang-jwt/jwt/v5 v5.3.0 github.com/jackc/pgx/v5 v5.7.6 github.com/oapi-codegen/runtime v1.1.2 github.com/pelletier/go-toml/v2 v2.2.4 - golang.org/x/crypto v0.40.0 ) require ( @@ -38,6 +38,7 @@ require ( github.com/ugorji/go/codec v1.3.0 // indirect go.uber.org/mock v0.5.0 // indirect golang.org/x/arch v0.20.0 // indirect + golang.org/x/crypto v0.40.0 // indirect golang.org/x/mod v0.25.0 // indirect golang.org/x/net v0.42.0 // indirect golang.org/x/sync v0.16.0 // indirect diff --git a/go.sum b/go.sum index 1af1a7c..d8c4265 100644 --- a/go.sum +++ b/go.sum @@ -31,6 +31,8 @@ github.com/goccy/go-json v0.10.5 h1:Fq85nIqj+gXn/S5ahsiTlK3TmC85qgirsdTP/+DeaC4= github.com/goccy/go-json v0.10.5/go.mod h1:oq7eo15ShAhp70Anwd5lgX2pLfOS3QCiwU/PULtXL6M= github.com/goccy/go-yaml v1.18.0 h1:8W7wMFS12Pcas7KU+VVkaiCng+kG8QiFeFwzFb+rwuw= github.com/goccy/go-yaml v1.18.0/go.mod h1:XBurs7gK8ATbW4ZPGKgcbrY1Br56PdM69F7LkFRi1kA= +github.com/golang-jwt/jwt/v5 v5.3.0 h1:pv4AsKCKKZuqlgs5sUmn4x8UlGa0kEVt/puTpKx9vvo= +github.com/golang-jwt/jwt/v5 v5.3.0/go.mod h1:fxCRLWMO43lRc8nhHWY6LGqRcf+1gQWArsqaEUEa5bE= github.com/google/go-cmp v0.7.0 h1:wk8382ETsv4JYUZwIsn6YpYiWiBsYLSJiTsyBybVuN8= github.com/google/go-cmp v0.7.0/go.mod h1:pXiqmnSA92OHEEa9HXL2W4E7lf9JzCmGVUdgjX3N/iU= github.com/google/gofuzz v1.0.0/go.mod h1:dBl0BpW6vV/+mYPU4Po3pmUjxk6FQPldtuIdl/M65Eg= diff --git a/modules/auth/handlers/handlers.go b/modules/auth/handlers/handlers.go new file mode 100644 index 0000000..ca72192 --- /dev/null +++ b/modules/auth/handlers/handlers.go @@ -0,0 +1,108 @@ +package handlers + +import ( + "context" + "fmt" + auth "nyanimedb/auth" + sqlc "nyanimedb/sql" + "strconv" + "time" + + "github.com/golang-jwt/jwt/v5" +) + +var secretKey = []byte("my_secret_key") + +func generateToken(userID string) (string, error) { + claims := jwt.MapClaims{ + "user_id": userID, + "exp": time.Now().Add(time.Hour * 24).Unix(), + } + + token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) + return token.SignedString(secretKey) +} + +var UserDb = make(map[string]string) //TEMP + +type Server struct { + db *sqlc.Queries +} + +func NewServer(db *sqlc.Queries) Server { + return Server{db: db} +} + +func parseInt64(s string) (int32, error) { + i, err := strconv.ParseInt(s, 10, 64) + return int32(i), err +} + +func (s Server) PostAuthSignIn(ctx context.Context, req auth.PostAuthSignInRequestObject) (auth.PostAuthSignInResponseObject, error) { + err := "" + success := true + t, _ := generateToken(req.Body.Nickname) + + UserDb[req.Body.Nickname] = req.Body.Pass + + return auth.PostAuthSignIn200JSONResponse{ + Error: &err, + Success: &success, + UserId: &req.Body.Nickname, + Token: &t, + }, nil +} + +func (s Server) PostAuthSignUp(ctx context.Context, req auth.PostAuthSignUpRequestObject) (auth.PostAuthSignUpResponseObject, error) { + err := "" + success := true + UserDb[req.Body.Nickname] = req.Body.Pass + + return auth.PostAuthSignUp200JSONResponse{ + Error: &err, + Success: &success, + UserId: &req.Body.Nickname, + }, nil +} + +func (s Server) PostAuthVerifyToken(ctx context.Context, req auth.PostAuthVerifyTokenRequestObject) (auth.PostAuthVerifyTokenResponseObject, error) { + valid := false + var userID *string + var errStr *string + + token, err := jwt.Parse(req.Body.Token, func(t *jwt.Token) (interface{}, error) { + if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { + return nil, fmt.Errorf("unexpected signing method") + } + return secretKey, nil + }) + + if err != nil { + e := err.Error() + errStr = &e + return auth.PostAuthVerifyToken200JSONResponse{ + Valid: &valid, + UserId: userID, + Error: errStr, + }, nil + } + + if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { + if uid, ok := claims["user_id"].(string); ok { + valid = true + userID = &uid + } else { + e := "user_id not found in token" + errStr = &e + } + } else { + e := "invalid token claims" + errStr = &e + } + + return auth.PostAuthVerifyToken200JSONResponse{ + Valid: &valid, + UserId: userID, + Error: errStr, + }, nil +} diff --git a/modules/auth/main.go b/modules/auth/main.go new file mode 100644 index 0000000..c001e8b --- /dev/null +++ b/modules/auth/main.go @@ -0,0 +1,38 @@ +package main + +import ( + "time" + + auth "nyanimedb/auth" + handlers "nyanimedb/modules/auth/handlers" + sqlc "nyanimedb/sql" + + "github.com/gin-contrib/cors" + "github.com/gin-gonic/gin" +) + +var AppConfig Config + +func main() { + r := gin.Default() + + var queries *sqlc.Queries = nil + + server := handlers.NewServer(queries) + + r.Use(cors.New(cors.Config{ + AllowOrigins: []string{"*"}, // allow all origins, change to specific domains in production + AllowMethods: []string{"GET", "POST", "PUT", "DELETE"}, + AllowHeaders: []string{"Origin", "Content-Type", "Accept"}, + ExposeHeaders: []string{"Content-Length"}, + AllowCredentials: true, + MaxAge: 12 * time.Hour, + })) + + auth.RegisterHandlers(r, auth.NewStrictHandler( + server, + []auth.StrictMiddlewareFunc{}, + )) + + r.Run(":8082") +} diff --git a/modules/auth/types.go b/modules/auth/types.go new file mode 100644 index 0000000..038b179 --- /dev/null +++ b/modules/auth/types.go @@ -0,0 +1,6 @@ +package main + +type Config struct { + JwtPrivateKey string + LogLevel string `toml:"LogLevel" env:"LOG_LEVEL"` +} From e64e770783883cd7f2fee60b83f9ad2dd41e254c Mon Sep 17 00:00:00 2001 From: nihonium Date: Sun, 23 Nov 2025 03:32:58 +0300 Subject: [PATCH 2/2] feat: use SetCookie for access and refresh tokens --- auth/auth.gen.go | 104 ++------------- auth/openapi-auth.yaml | 121 +++++++++++++----- modules/auth/handlers/handlers.go | 203 +++++++++++++++++++++--------- 3 files changed, 246 insertions(+), 182 deletions(-) diff --git a/auth/auth.gen.go b/auth/auth.gen.go index 1f16575..adb2b06 100644 --- a/auth/auth.gen.go +++ b/auth/auth.gen.go @@ -25,21 +25,12 @@ type PostAuthSignUpJSONBody struct { Pass string `json:"pass"` } -// PostAuthVerifyTokenJSONBody defines parameters for PostAuthVerifyToken. -type PostAuthVerifyTokenJSONBody struct { - // Token JWT token to validate - Token string `json:"token"` -} - // PostAuthSignInJSONRequestBody defines body for PostAuthSignIn for application/json ContentType. type PostAuthSignInJSONRequestBody PostAuthSignInJSONBody // PostAuthSignUpJSONRequestBody defines body for PostAuthSignUp for application/json ContentType. type PostAuthSignUpJSONRequestBody PostAuthSignUpJSONBody -// PostAuthVerifyTokenJSONRequestBody defines body for PostAuthVerifyToken for application/json ContentType. -type PostAuthVerifyTokenJSONRequestBody PostAuthVerifyTokenJSONBody - // ServerInterface represents all server handlers. type ServerInterface interface { // Sign in a user and return JWT @@ -48,9 +39,6 @@ type ServerInterface interface { // Sign up a new user // (POST /auth/sign-up) PostAuthSignUp(c *gin.Context) - // Verify JWT validity - // (POST /auth/verify-token) - PostAuthVerifyToken(c *gin.Context) } // ServerInterfaceWrapper converts contexts to parameters. @@ -88,19 +76,6 @@ func (siw *ServerInterfaceWrapper) PostAuthSignUp(c *gin.Context) { siw.Handler.PostAuthSignUp(c) } -// PostAuthVerifyToken operation middleware -func (siw *ServerInterfaceWrapper) PostAuthVerifyToken(c *gin.Context) { - - for _, middleware := range siw.HandlerMiddlewares { - middleware(c) - if c.IsAborted() { - return - } - } - - siw.Handler.PostAuthVerifyToken(c) -} - // GinServerOptions provides options for the Gin server. type GinServerOptions struct { BaseURL string @@ -130,7 +105,6 @@ func RegisterHandlersWithOptions(router gin.IRouter, si ServerInterface, options router.POST(options.BaseURL+"/auth/sign-in", wrapper.PostAuthSignIn) router.POST(options.BaseURL+"/auth/sign-up", wrapper.PostAuthSignUp) - router.POST(options.BaseURL+"/auth/verify-token", wrapper.PostAuthVerifyToken) } type PostAuthSignInRequestObject struct { @@ -144,10 +118,7 @@ type PostAuthSignInResponseObject interface { type PostAuthSignIn200JSONResponse struct { Error *string `json:"error"` Success *bool `json:"success,omitempty"` - - // Token JWT token to access protected endpoints - Token *string `json:"token"` - UserId *string `json:"user_id"` + UserId *string `json:"user_id"` } func (response PostAuthSignIn200JSONResponse) VisitPostAuthSignInResponse(w http.ResponseWriter) error { @@ -157,6 +128,17 @@ func (response PostAuthSignIn200JSONResponse) VisitPostAuthSignInResponse(w http return json.NewEncoder(w).Encode(response) } +type PostAuthSignIn401JSONResponse struct { + Error *string `json:"error,omitempty"` +} + +func (response PostAuthSignIn401JSONResponse) VisitPostAuthSignInResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") + w.WriteHeader(401) + + return json.NewEncoder(w).Encode(response) +} + type PostAuthSignUpRequestObject struct { Body *PostAuthSignUpJSONRequestBody } @@ -178,32 +160,6 @@ func (response PostAuthSignUp200JSONResponse) VisitPostAuthSignUpResponse(w http return json.NewEncoder(w).Encode(response) } -type PostAuthVerifyTokenRequestObject struct { - Body *PostAuthVerifyTokenJSONRequestBody -} - -type PostAuthVerifyTokenResponseObject interface { - VisitPostAuthVerifyTokenResponse(w http.ResponseWriter) error -} - -type PostAuthVerifyToken200JSONResponse struct { - // Error Error message if token is invalid - Error *string `json:"error"` - - // UserId User ID extracted from token if valid - UserId *string `json:"user_id"` - - // Valid True if token is valid - Valid *bool `json:"valid,omitempty"` -} - -func (response PostAuthVerifyToken200JSONResponse) VisitPostAuthVerifyTokenResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(200) - - return json.NewEncoder(w).Encode(response) -} - // StrictServerInterface represents all server handlers. type StrictServerInterface interface { // Sign in a user and return JWT @@ -212,9 +168,6 @@ type StrictServerInterface interface { // Sign up a new user // (POST /auth/sign-up) PostAuthSignUp(ctx context.Context, request PostAuthSignUpRequestObject) (PostAuthSignUpResponseObject, error) - // Verify JWT validity - // (POST /auth/verify-token) - PostAuthVerifyToken(ctx context.Context, request PostAuthVerifyTokenRequestObject) (PostAuthVerifyTokenResponseObject, error) } type StrictHandlerFunc = strictgin.StrictGinHandlerFunc @@ -294,36 +247,3 @@ func (sh *strictHandler) PostAuthSignUp(ctx *gin.Context) { ctx.Error(fmt.Errorf("unexpected response type: %T", response)) } } - -// PostAuthVerifyToken operation middleware -func (sh *strictHandler) PostAuthVerifyToken(ctx *gin.Context) { - var request PostAuthVerifyTokenRequestObject - - var body PostAuthVerifyTokenJSONRequestBody - if err := ctx.ShouldBindJSON(&body); err != nil { - ctx.Status(http.StatusBadRequest) - ctx.Error(err) - return - } - request.Body = &body - - handler := func(ctx *gin.Context, request interface{}) (interface{}, error) { - return sh.ssi.PostAuthVerifyToken(ctx, request.(PostAuthVerifyTokenRequestObject)) - } - for _, middleware := range sh.middlewares { - handler = middleware(handler, "PostAuthVerifyToken") - } - - response, err := handler(ctx, request) - - if err != nil { - ctx.Error(err) - ctx.Status(http.StatusInternalServerError) - } else if validResponse, ok := response.(PostAuthVerifyTokenResponseObject); ok { - if err := validResponse.VisitPostAuthVerifyTokenResponse(ctx.Writer); err != nil { - ctx.Error(err) - } - } else if response != nil { - ctx.Error(fmt.Errorf("unexpected response type: %T", response)) - } -} diff --git a/auth/openapi-auth.yaml b/auth/openapi-auth.yaml index 7ffc60e..b9ce76f 100644 --- a/auth/openapi-auth.yaml +++ b/auth/openapi-auth.yaml @@ -1,4 +1,4 @@ -openapi: 3.1.0 +openapi: 3.1.1 info: title: Auth Service version: 1.0.0 @@ -58,6 +58,14 @@ paths: responses: "200": description: Sign-in result with JWT + # headers: + # Set-Cookie: + # schema: + # type: array + # items: + # type: string + # explode: true + # style: simple content: application/json: schema: @@ -71,42 +79,89 @@ paths: user_id: type: string nullable: true - token: - type: string - description: JWT token to access protected endpoints - nullable: true - - /auth/verify-token: - post: - summary: Verify JWT validity - tags: [Auth] - requestBody: - required: true - content: - application/json: - schema: - type: object - required: [token] - properties: - token: - type: string - description: JWT token to validate - responses: - "200": - description: Token validation result + "401": + description: Access denied due to invalid credentials content: application/json: schema: type: object properties: - valid: - type: boolean - description: True if token is valid - user_id: - type: string - nullable: true - description: User ID extracted from token if valid error: type: string - nullable: true - description: Error message if token is invalid \ No newline at end of file + example: "Access denied" + # /auth/verify-token: + # post: + # summary: Verify JWT validity + # tags: [Auth] + # requestBody: + # required: true + # content: + # application/json: + # schema: + # type: object + # required: [token] + # properties: + # token: + # type: string + # description: JWT token to validate + # responses: + # "200": + # description: Token validation result + # content: + # application/json: + # schema: + # type: object + # properties: + # valid: + # type: boolean + # description: True if token is valid + # user_id: + # type: string + # nullable: true + # description: User ID extracted from token if valid + # error: + # type: string + # nullable: true + # description: Error message if token is invalid + # /auth/refresh-token: + # post: + # summary: Refresh JWT using a refresh token + # tags: [Auth] + # requestBody: + # required: true + # content: + # application/json: + # schema: + # type: object + # required: [refresh_token] + # properties: + # refresh_token: + # type: string + # description: JWT refresh token obtained from sign-in + # responses: + # "200": + # description: New access (and optionally refresh) token + # content: + # application/json: + # schema: + # type: object + # properties: + # valid: + # type: boolean + # description: True if refresh token was valid + # user_id: + # type: string + # nullable: true + # description: User ID extracted from refresh token + # access_token: + # type: string + # description: New access token + # nullable: true + # refresh_token: + # type: string + # description: New refresh token (optional) + # nullable: true + # error: + # type: string + # nullable: true + # description: Error message if refresh token is invalid diff --git a/modules/auth/handlers/handlers.go b/modules/auth/handlers/handlers.go index ca72192..9b9b0d3 100644 --- a/modules/auth/handlers/handlers.go +++ b/modules/auth/handlers/handlers.go @@ -3,27 +3,21 @@ package handlers import ( "context" "fmt" + "log" + "net/http" auth "nyanimedb/auth" sqlc "nyanimedb/sql" "strconv" "time" + "github.com/gin-gonic/gin" "github.com/golang-jwt/jwt/v5" ) -var secretKey = []byte("my_secret_key") +var accessSecret = []byte("my_access_secret_key") +var refreshSecret = []byte("my_refresh_secret_key") -func generateToken(userID string) (string, error) { - claims := jwt.MapClaims{ - "user_id": userID, - "exp": time.Now().Add(time.Hour * 24).Unix(), - } - - token := jwt.NewWithClaims(jwt.SigningMethodHS256, claims) - return token.SignedString(secretKey) -} - -var UserDb = make(map[string]string) //TEMP +var UserDb = make(map[string]string) // TEMP: stores passwords type Server struct { db *sqlc.Queries @@ -38,19 +32,28 @@ func parseInt64(s string) (int32, error) { return int32(i), err } -func (s Server) PostAuthSignIn(ctx context.Context, req auth.PostAuthSignInRequestObject) (auth.PostAuthSignInResponseObject, error) { - err := "" - success := true - t, _ := generateToken(req.Body.Nickname) +func generateTokens(userID string) (accessToken string, refreshToken string, err error) { + accessClaims := jwt.MapClaims{ + "user_id": userID, + "exp": time.Now().Add(15 * time.Minute).Unix(), + } + at := jwt.NewWithClaims(jwt.SigningMethodHS256, accessClaims) + accessToken, err = at.SignedString(accessSecret) + if err != nil { + return "", "", err + } - UserDb[req.Body.Nickname] = req.Body.Pass + refreshClaims := jwt.MapClaims{ + "user_id": userID, + "exp": time.Now().Add(7 * 24 * time.Hour).Unix(), + } + rt := jwt.NewWithClaims(jwt.SigningMethodHS256, refreshClaims) + refreshToken, err = rt.SignedString(refreshSecret) + if err != nil { + return "", "", err + } - return auth.PostAuthSignIn200JSONResponse{ - Error: &err, - Success: &success, - UserId: &req.Body.Nickname, - Token: &t, - }, nil + return accessToken, refreshToken, nil } func (s Server) PostAuthSignUp(ctx context.Context, req auth.PostAuthSignUpRequestObject) (auth.PostAuthSignUpResponseObject, error) { @@ -65,44 +68,130 @@ func (s Server) PostAuthSignUp(ctx context.Context, req auth.PostAuthSignUpReque }, nil } -func (s Server) PostAuthVerifyToken(ctx context.Context, req auth.PostAuthVerifyTokenRequestObject) (auth.PostAuthVerifyTokenResponseObject, error) { - valid := false - var userID *string - var errStr *string +func (s Server) PostAuthSignIn(ctx context.Context, req auth.PostAuthSignInRequestObject) (auth.PostAuthSignInResponseObject, error) { + // ctx.SetCookie("122") + ginCtx, ok := ctx.Value(gin.ContextKey).(*gin.Context) + if !ok { + log.Print("failed to get gin context") + // TODO: change to 500 + return auth.PostAuthSignIn200JSONResponse{}, fmt.Errorf("failed to get gin.Context from context.Context") + } - token, err := jwt.Parse(req.Body.Token, func(t *jwt.Token) (interface{}, error) { - if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { - return nil, fmt.Errorf("unexpected signing method") - } - return secretKey, nil - }) + err := "" + success := true - if err != nil { - e := err.Error() - errStr = &e - return auth.PostAuthVerifyToken200JSONResponse{ - Valid: &valid, - UserId: userID, - Error: errStr, + pass, ok := UserDb[req.Body.Nickname] + if !ok || pass != req.Body.Pass { + e := "invalid credentials" + return auth.PostAuthSignIn401JSONResponse{ + Error: &e, }, nil } - if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { - if uid, ok := claims["user_id"].(string); ok { - valid = true - userID = &uid - } else { - e := "user_id not found in token" - errStr = &e - } - } else { - e := "invalid token claims" - errStr = &e - } + accessToken, refreshToken, _ := generateTokens(req.Body.Nickname) - return auth.PostAuthVerifyToken200JSONResponse{ - Valid: &valid, - UserId: userID, - Error: errStr, - }, nil + ginCtx.SetSameSite(http.SameSiteStrictMode) + ginCtx.SetCookie("access_token", accessToken, 604800, "/auth", "", true, true) + ginCtx.SetCookie("refresh_token", refreshToken, 604800, "/api", "", true, true) + + // Return access token; refresh token can be returned in response or HttpOnly cookie + result := auth.PostAuthSignIn200JSONResponse{ + Error: &err, + Success: &success, + UserId: &req.Body.Nickname, + } + return result, nil } + +// func (s Server) PostAuthVerifyToken(ctx context.Context, req auth.PostAuthVerifyTokenRequestObject) (auth.PostAuthVerifyTokenResponseObject, error) { +// valid := false +// var userID *string +// var errStr *string + +// token, err := jwt.Parse(req.Body.Token, func(t *jwt.Token) (interface{}, error) { +// if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { +// return nil, fmt.Errorf("unexpected signing method") +// } +// return accessSecret, nil +// }) + +// if err != nil { +// e := err.Error() +// errStr = &e +// return auth.PostAuthVerifyToken200JSONResponse{ +// Valid: &valid, +// UserId: userID, +// Error: errStr, +// }, nil +// } + +// if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { +// if uid, ok := claims["user_id"].(string); ok { +// valid = true +// userID = &uid +// } else { +// e := "user_id not found in token" +// errStr = &e +// } +// } else { +// e := "invalid token claims" +// errStr = &e +// } + +// return auth.PostAuthVerifyToken200JSONResponse{ +// Valid: &valid, +// UserId: userID, +// Error: errStr, +// }, nil +// } + +// func (s Server) PostAuthRefreshToken(ctx context.Context, req auth.PostAuthRefreshTokenRequestObject) (auth.PostAuthRefreshTokenResponseObject, error) { +// valid := false +// var userID *string +// var errStr *string + +// token, err := jwt.Parse(req.Body.Token, func(t *jwt.Token) (interface{}, error) { +// if _, ok := t.Method.(*jwt.SigningMethodHMAC); !ok { +// return nil, fmt.Errorf("unexpected signing method") +// } +// return refreshSecret, nil +// }) + +// if err != nil { +// e := err.Error() +// errStr = &e +// return auth.PostAuthVerifyToken200JSONResponse{ +// Valid: &valid, +// UserId: userID, +// Error: errStr, +// }, nil +// } + +// if claims, ok := token.Claims.(jwt.MapClaims); ok && token.Valid { +// if uid, ok := claims["user_id"].(string); ok { +// // Refresh token is valid, generate new tokens +// newAccessToken, newRefreshToken, _ := generateTokens(uid) +// valid = true +// userID = &uid +// return auth.PostAuthVerifyToken200JSONResponse{ +// Valid: &valid, +// UserId: userID, +// Error: nil, +// Token: &newAccessToken, // return new access token +// // optionally return newRefreshToken as well +// }, nil +// } else { +// e := "user_id not found in refresh token" +// errStr = &e +// } +// } else { +// e := "invalid refresh token claims" +// errStr = &e +// } + +// return auth.PostAuthVerifyToken200JSONResponse{ +// Valid: &valid, +// UserId: userID, +// Error: errStr, +// }, nil +// }