diff --git a/auth/auth.gen.go b/auth/auth.gen.go index 89a2168..b7cd839 100644 --- a/auth/auth.gen.go +++ b/auth/auth.gen.go @@ -13,23 +13,6 @@ import ( strictgin "github.com/oapi-codegen/runtime/strictmiddleware/gin" ) -const ( - BearerAuthScopes = "bearerAuth.Scopes" -) - -// GetImpersonationTokenJSONBody defines parameters for GetImpersonationToken. -type GetImpersonationTokenJSONBody struct { - TgId *int64 `json:"tg_id,omitempty"` - UserId *int64 `json:"user_id,omitempty"` - union json.RawMessage -} - -// GetImpersonationTokenJSONBody0 defines parameters for GetImpersonationToken. -type GetImpersonationTokenJSONBody0 = interface{} - -// GetImpersonationTokenJSONBody1 defines parameters for GetImpersonationToken. -type GetImpersonationTokenJSONBody1 = interface{} - // PostSignInJSONBody defines parameters for PostSignIn. type PostSignInJSONBody struct { Nickname string `json:"nickname"` @@ -42,9 +25,6 @@ type PostSignUpJSONBody struct { Pass string `json:"pass"` } -// GetImpersonationTokenJSONRequestBody defines body for GetImpersonationToken for application/json ContentType. -type GetImpersonationTokenJSONRequestBody GetImpersonationTokenJSONBody - // PostSignInJSONRequestBody defines body for PostSignIn for application/json ContentType. type PostSignInJSONRequestBody PostSignInJSONBody @@ -53,9 +33,6 @@ type PostSignUpJSONRequestBody PostSignUpJSONBody // ServerInterface represents all server handlers. type ServerInterface interface { - // Get service impersontaion token - // (POST /get-impersonation-token) - GetImpersonationToken(c *gin.Context) // Sign in a user and return JWT // (POST /sign-in) PostSignIn(c *gin.Context) @@ -73,21 +50,6 @@ type ServerInterfaceWrapper struct { type MiddlewareFunc func(c *gin.Context) -// GetImpersonationToken operation middleware -func (siw *ServerInterfaceWrapper) GetImpersonationToken(c *gin.Context) { - - c.Set(BearerAuthScopes, []string{}) - - for _, middleware := range siw.HandlerMiddlewares { - middleware(c) - if c.IsAborted() { - return - } - } - - siw.Handler.GetImpersonationToken(c) -} - // PostSignIn operation middleware func (siw *ServerInterfaceWrapper) PostSignIn(c *gin.Context) { @@ -141,41 +103,10 @@ func RegisterHandlersWithOptions(router gin.IRouter, si ServerInterface, options ErrorHandler: errorHandler, } - router.POST(options.BaseURL+"/get-impersonation-token", wrapper.GetImpersonationToken) router.POST(options.BaseURL+"/sign-in", wrapper.PostSignIn) router.POST(options.BaseURL+"/sign-up", wrapper.PostSignUp) } -type UnauthorizedErrorResponse struct { -} - -type GetImpersonationTokenRequestObject struct { - Body *GetImpersonationTokenJSONRequestBody -} - -type GetImpersonationTokenResponseObject interface { - VisitGetImpersonationTokenResponse(w http.ResponseWriter) error -} - -type GetImpersonationToken200JSONResponse struct { - // AccessToken JWT access token - AccessToken string `json:"access_token"` -} - -func (response GetImpersonationToken200JSONResponse) VisitGetImpersonationTokenResponse(w http.ResponseWriter) error { - w.Header().Set("Content-Type", "application/json") - w.WriteHeader(200) - - return json.NewEncoder(w).Encode(response) -} - -type GetImpersonationToken401Response = UnauthorizedErrorResponse - -func (response GetImpersonationToken401Response) VisitGetImpersonationTokenResponse(w http.ResponseWriter) error { - w.WriteHeader(401) - return nil -} - type PostSignInRequestObject struct { Body *PostSignInJSONRequestBody } @@ -196,11 +127,15 @@ func (response PostSignIn200JSONResponse) VisitPostSignInResponse(w http.Respons return json.NewEncoder(w).Encode(response) } -type PostSignIn401Response = UnauthorizedErrorResponse +type PostSignIn401JSONResponse struct { + Error *string `json:"error,omitempty"` +} -func (response PostSignIn401Response) VisitPostSignInResponse(w http.ResponseWriter) error { +func (response PostSignIn401JSONResponse) VisitPostSignInResponse(w http.ResponseWriter) error { + w.Header().Set("Content-Type", "application/json") w.WriteHeader(401) - return nil + + return json.NewEncoder(w).Encode(response) } type PostSignUpRequestObject struct { @@ -224,9 +159,6 @@ func (response PostSignUp200JSONResponse) VisitPostSignUpResponse(w http.Respons // StrictServerInterface represents all server handlers. type StrictServerInterface interface { - // Get service impersontaion token - // (POST /get-impersonation-token) - GetImpersonationToken(ctx context.Context, request GetImpersonationTokenRequestObject) (GetImpersonationTokenResponseObject, error) // Sign in a user and return JWT // (POST /sign-in) PostSignIn(ctx context.Context, request PostSignInRequestObject) (PostSignInResponseObject, error) @@ -247,39 +179,6 @@ type strictHandler struct { middlewares []StrictMiddlewareFunc } -// GetImpersonationToken operation middleware -func (sh *strictHandler) GetImpersonationToken(ctx *gin.Context) { - var request GetImpersonationTokenRequestObject - - var body GetImpersonationTokenJSONRequestBody - 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.GetImpersonationToken(ctx, request.(GetImpersonationTokenRequestObject)) - } - for _, middleware := range sh.middlewares { - handler = middleware(handler, "GetImpersonationToken") - } - - response, err := handler(ctx, request) - - if err != nil { - ctx.Error(err) - ctx.Status(http.StatusInternalServerError) - } else if validResponse, ok := response.(GetImpersonationTokenResponseObject); ok { - if err := validResponse.VisitGetImpersonationTokenResponse(ctx.Writer); err != nil { - ctx.Error(err) - } - } else if response != nil { - ctx.Error(fmt.Errorf("unexpected response type: %T", response)) - } -} - // PostSignIn operation middleware func (sh *strictHandler) PostSignIn(ctx *gin.Context) { var request PostSignInRequestObject diff --git a/auth/openapi-auth.yaml b/auth/openapi-auth.yaml index 93db937..5f3ebd6 100644 --- a/auth/openapi-auth.yaml +++ b/auth/openapi-auth.yaml @@ -10,7 +10,6 @@ paths: /sign-up: post: summary: Sign up a new user - operationId: postSignUp tags: [Auth] requestBody: required: true @@ -42,7 +41,6 @@ paths: /sign-in: post: summary: Sign in a user and return JWT - operationId: postSignIn tags: [Auth] requestBody: required: true @@ -75,52 +73,88 @@ paths: user_name: type: string "401": - $ref: '#/components/responses/UnauthorizedError' - - /get-impersonation-token: - post: - summary: Get service impersontaion token - operationId: getImpersonationToken - tags: [Auth] - security: - - bearerAuth: [] - requestBody: - required: true - content: - application/json: - schema: - type: object - properties: - user_id: - type: integer - format: int64 - tg_id: - type: integer - format: int64 - oneOf: - - required: ["user_id"] - - required: ["tg_id"] - responses: - "200": - description: Generated impersonation access token + description: Access denied due to invalid credentials content: application/json: schema: type: object - required: - - access_token properties: - access_token: + error: type: string - description: JWT access token - "401": - $ref: '#/components/responses/UnauthorizedError' - -components: - securitySchemes: - bearerAuth: - type: http - scheme: bearer - responses: - UnauthorizedError: - description: Access token is missing or 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 9138fa7..ac55abe 100644 --- a/modules/auth/handlers/handlers.go +++ b/modules/auth/handlers/handlers.go @@ -47,28 +47,10 @@ func CheckPassword(password, hash string) (bool, error) { return argon2id.ComparePasswordAndHash(password, hash) } -func (s Server) generateImpersonationToken(userID string, impersonated_by string) (accessToken string, err error) { - accessClaims := jwt.MapClaims{ - "user_id": userID, - "exp": time.Now().Add(15 * time.Minute).Unix(), - "imp_id": impersonated_by, - } - - at := jwt.NewWithClaims(jwt.SigningMethodHS256, accessClaims) - - accessToken, err = at.SignedString([]byte(s.JwtPrivateKey)) - if err != nil { - return "", err - } - - return accessToken, nil -} - func (s Server) generateTokens(userID string) (accessToken string, refreshToken string, csrfToken string, err error) { accessClaims := jwt.MapClaims{ "user_id": userID, "exp": time.Now().Add(15 * time.Minute).Unix(), - //TODO: add created_at } at := jwt.NewWithClaims(jwt.SigningMethodHS256, accessClaims) accessToken, err = at.SignedString([]byte(s.JwtPrivateKey)) @@ -137,7 +119,10 @@ func (s Server) PostSignIn(ctx context.Context, req auth.PostSignInRequestObject // TODO: return 500 } if !ok { - return auth.PostSignIn401Response{}, nil + err_msg := "invalid credentials" + return auth.PostSignIn401JSONResponse{ + Error: &err_msg, + }, nil } accessToken, refreshToken, csrfToken, err := s.generateTokens(req.Body.Nickname) @@ -159,40 +144,6 @@ func (s Server) PostSignIn(ctx context.Context, req auth.PostSignInRequestObject return result, nil } -func (s Server) GetImpersonationToken(ctx context.Context, req auth.GetImpersonationTokenRequestObject) (auth.GetImpersonationTokenResponseObject, error) { - ginCtx, ok := ctx.Value(gin.ContextKey).(*gin.Context) - if !ok { - log.Print("failed to get gin context") - // TODO: change to 500 - return auth.GetImpersonationToken200JSONResponse{}, fmt.Errorf("failed to get gin.Context from context.Context") - } - - token, err := ExtractBearerToken(ginCtx.Request.Header.Get("Authorization")) - if err != nil { - // TODO: return 500 - log.Errorf("failed to extract bearer token: %v", err) - return auth.GetImpersonationToken401Response{}, err - } - log.Printf("got auth token: %s", token) - - ext_service, err := s.db.GetExternalServiceByToken(context.Background(), &token) - if err != nil { - log.Errorf("failed to get external service by token: %v", err) - return auth.GetImpersonationToken401Response{}, err - // TODO: check err and retyrn 400/500 - } - - // TODO: handle tgid - accessToken, err := s.generateImpersonationToken(fmt.Sprintf("%d", *req.Body.UserId), fmt.Sprintf("%d", ext_service.ID)) - if err != nil { - log.Errorf("failed to generate impersonation token: %v", err) - return auth.GetImpersonationToken401Response{}, err - // TODO: check err and retyrn 400/500 - } - - return auth.GetImpersonationToken200JSONResponse{AccessToken: accessToken}, nil -} - // func (s Server) PostAuthVerifyToken(ctx context.Context, req auth.PostAuthVerifyTokenRequestObject) (auth.PostAuthVerifyTokenResponseObject, error) { // valid := false // var userID *string @@ -285,11 +236,3 @@ func (s Server) GetImpersonationToken(ctx context.Context, req auth.GetImpersona // Error: errStr, // }, nil // } - -func ExtractBearerToken(header string) (string, error) { - const prefix = "Bearer " - if len(header) <= len(prefix) || header[:len(prefix)] != prefix { - return "", fmt.Errorf("invalid bearer token format") - } - return header[len(prefix):], nil -} diff --git a/modules/auth/queries.sql b/modules/auth/queries.sql index 363f07a..828d2af 100644 --- a/modules/auth/queries.sql +++ b/modules/auth/queries.sql @@ -9,7 +9,3 @@ INTO users (passhash, nickname) VALUES (sqlc.arg(passhash), sqlc.arg(nickname)) RETURNING id; --- name: GetExternalServiceByToken :one -SELECT * -FROM external_services -WHERE auth_token = sqlc.arg('auth_token'); \ No newline at end of file diff --git a/sql/migrations/000001_init.up.sql b/sql/migrations/000001_init.up.sql index 9bf99dc..d6353d6 100644 --- a/sql/migrations/000001_init.up.sql +++ b/sql/migrations/000001_init.up.sql @@ -33,6 +33,8 @@ CREATE TABLE users ( last_login timestamptz ); + + CREATE TABLE studios ( id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, studio_name text NOT NULL UNIQUE, @@ -106,8 +108,7 @@ CREATE TABLE signals ( CREATE TABLE external_services ( id bigint GENERATED ALWAYS AS IDENTITY PRIMARY KEY, - name text UNIQUE NOT NULL, - auth_token text + name text UNIQUE NOT NULL ); CREATE TABLE external_ids ( diff --git a/sql/models.go b/sql/models.go index 1395a19..b1ea282 100644 --- a/sql/models.go +++ b/sql/models.go @@ -193,9 +193,8 @@ type ExternalID struct { } type ExternalService struct { - ID int64 `json:"id"` - Name string `json:"name"` - AuthToken *string `json:"auth_token"` + ID int64 `json:"id"` + Name string `json:"name"` } type Image struct { diff --git a/sql/queries.sql.go b/sql/queries.sql.go index e12619e..0c17599 100644 --- a/sql/queries.sql.go +++ b/sql/queries.sql.go @@ -74,19 +74,6 @@ func (q *Queries) DeleteUserTitle(ctx context.Context, arg DeleteUserTitleParams return i, err } -const getExternalServiceByToken = `-- name: GetExternalServiceByToken :one -SELECT id, name, auth_token -FROM external_services -WHERE auth_token = $1 -` - -func (q *Queries) GetExternalServiceByToken(ctx context.Context, authToken *string) (ExternalService, error) { - row := q.db.QueryRow(ctx, getExternalServiceByToken, authToken) - var i ExternalService - err := row.Scan(&i.ID, &i.Name, &i.AuthToken) - return i, err -} - const getImageByID = `-- name: GetImageByID :one SELECT id, storage_type, image_path FROM images