From 8cae07ba58dced8ee657ca18d7ae61e5061768cb Mon Sep 17 00:00:00 2001 From: kaitoyama Date: Sun, 22 Mar 2026 15:32:29 +0900 Subject: [PATCH 1/6] Add verify-email and invoice response schemas --- openapi.yaml | 135 ++++++++++++++++++++++------------ server/gen.go | 196 ++++++++++++++++++++++++++++---------------------- 2 files changed, 199 insertions(+), 132 deletions(-) diff --git a/openapi.yaml b/openapi.yaml index 236a222..ecd53a5 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -6,6 +6,35 @@ info: description: Checkin API paths: + /verify-email: + post: + summary: isct メールアドレスを検証して JWT を発行 + tags: + - Auth + operationId: postVerifyEmail + parameters: + - name: redirect + in: query + description: 検証後に遷移する相対パス + schema: + type: string + requestBody: + required: true + content: + application/json: + schema: + $ref: '#/components/schemas/VerifyEmailRequest' + responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/VerifyEmailResponse' + '400': + $ref: '#/components/responses/BadRequest' + '500': + $ref: '#/components/responses/InternalServerError' /customer: get: summary: Customer を取得 @@ -90,7 +119,7 @@ paths: content: application/json: schema: - $ref: '#/components/schemas/Invoice' + $ref: '#/components/schemas/CreateInvoiceResponse' '500': $ref: '#/components/responses/InternalServerError' /webhook/invoice-paid: @@ -107,15 +136,16 @@ paths: schema: type: string requestBody: + required: true content: application/json: schema: - $ref: '#/components/schemas/Invoice' + $ref: '#/components/schemas/StripeEvent' responses: '204': description: No content - '403': - $ref: '#/components/responses/Forbidden' + '400': + $ref: '#/components/responses/BadRequest' '500': $ref: '#/components/responses/InternalServerError' /list/invoices: @@ -217,47 +247,6 @@ paths: $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' - post: - summary: 管理者を作成 - tags: - - Admin - operationId: postAdmin - requestBody: - content: - application/json: - schema: - $ref: '#/components/schemas/Admin' - responses: - '201': - description: Created - content: - application/json: - schema: - $ref: '#/components/schemas/Admin' - '403': - $ref: '#/components/responses/Forbidden' - '500': - $ref: '#/components/responses/InternalServerError' - delete: - summary: 管理者を削除 - tags: - - Admin - operationId: deleteAdmin - parameters: - - name: id - in: query - description: 管理者の traQ ID - required: true - schema: - type: string - responses: - '204': - description: No content - '403': - $ref: '#/components/responses/Forbidden' - '500': - $ref: '#/components/responses/InternalServerError' - components: parameters: CustomerId: @@ -296,6 +285,8 @@ components: schema: type: string responses: + BadRequest: + description: Bad request Forbidden: description: Forbidden NotFound: @@ -303,6 +294,32 @@ components: InternalServerError: description: Internal server error schemas: + VerifyEmailRequest: + description: Verify email request + type: object + properties: + email: + type: string + description: isct email + required: + - email + VerifyEmailResponse: + description: Verify email response + type: object + properties: + email: + type: string + description: normalized email + token: + type: string + description: JWT token + redirect: + type: string + description: validated redirect path + required: + - email + - token + - redirect Admin: description: 管理者の情報 type: object @@ -362,6 +379,21 @@ components: required: - customer_id - product_id + CreateInvoiceResponse: + description: Invoice creation result + type: object + properties: + invoice_id: + type: string + description: Invoice ID + example: in_1MtHbELkdIwHu7ixl4OzzPMv + payment_url: + type: string + description: Hosted invoice payment URL + example: https://invoice.stripe.com/... + required: + - invoice_id + - payment_url Invoice: description: Invoice の情報 type: object @@ -380,18 +412,22 @@ components: example: in_1MtHbELkdIwHu7ixl4OzzPMv amount_due: type: integer + format: int64 description: 最終的に支払う金額 example: 0 amount_paid: type: integer + format: int64 description: 支払い済み金額 example: 0 amount_remaining: type: integer + format: int64 description: 支払い残高 example: 0 created: type: integer + format: int64 description: 作成日時 (Unix 時間) example: 1680644467 customer: @@ -433,14 +469,17 @@ components: example: cs_test_1234567890 amount_total: type: integer + format: int64 description: 支払い金額 example: 2198 amount_subtotal: type: integer + format: int64 description: 割引・税金を含まない支払い金額 example: 1998 created: type: integer + format: int64 description: 作成日時 (Unix 時間) example: 1679600215 customer: @@ -462,8 +501,14 @@ components: type: string description: Product ID example: prod_NWjs8kKbJWmuuc + StripeEvent: + description: Generic Stripe event payload + type: object + additionalProperties: true tags: + - name: Auth + description: Auth API - name: Customer description: Customer API - name: Invoice diff --git a/server/gen.go b/server/gen.go index 623da29..d720259 100644 --- a/server/gen.go +++ b/server/gen.go @@ -1,6 +1,6 @@ // Package api provides primitives to interact with the openapi HTTP API. // -// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.4.1 DO NOT EDIT. +// Code generated by github.com/oapi-codegen/oapi-codegen/v2 version v2.5.0 DO NOT EDIT. package api import ( @@ -62,6 +62,15 @@ type Admin struct { Id string `json:"id"` } +// CreateInvoiceResponse Invoice creation result +type CreateInvoiceResponse struct { + // InvoiceId Invoice ID + InvoiceId string `json:"invoice_id"` + + // PaymentUrl Hosted invoice payment URL + PaymentUrl string `json:"payment_url"` +} + // Customer Customer の情報 type Customer struct { // Email Email @@ -81,13 +90,13 @@ type Customer struct { type GetCheckoutSessionsResponse struct { Data *[]struct { // AmountSubtotal 割引・税金を含まない支払い金額 - AmountSubtotal *int `json:"amount_subtotal,omitempty"` + AmountSubtotal *int64 `json:"amount_subtotal,omitempty"` // AmountTotal 支払い金額 - AmountTotal *int `json:"amount_total,omitempty"` + AmountTotal *int64 `json:"amount_total,omitempty"` // Created 作成日時 (Unix 時間) - Created *int `json:"created,omitempty"` + Created *int64 `json:"created,omitempty"` // Customer Customer の情報 Customer *Customer `json:"customer,omitempty"` @@ -116,16 +125,16 @@ type GetCheckoutSessionsResponseDataStatus string type Invoice struct { Data *[]struct { // AmountDue 最終的に支払う金額 - AmountDue *int `json:"amount_due,omitempty"` + AmountDue *int64 `json:"amount_due,omitempty"` // AmountPaid 支払い済み金額 - AmountPaid *int `json:"amount_paid,omitempty"` + AmountPaid *int64 `json:"amount_paid,omitempty"` // AmountRemaining 支払い残高 - AmountRemaining *int `json:"amount_remaining,omitempty"` + AmountRemaining *int64 `json:"amount_remaining,omitempty"` // Created 作成日時 (Unix 時間) - Created *int `json:"created,omitempty"` + Created *int64 `json:"created,omitempty"` // Customer Customer の情報 Customer *Customer `json:"customer,omitempty"` @@ -171,6 +180,27 @@ type PostInvoiceRequest struct { ProductId string `json:"product_id"` } +// StripeEvent Generic Stripe event payload +type StripeEvent map[string]interface{} + +// VerifyEmailRequest Verify email request +type VerifyEmailRequest struct { + // Email isct email + Email string `json:"email"` +} + +// VerifyEmailResponse Verify email response +type VerifyEmailResponse struct { + // Email normalized email + Email string `json:"email"` + + // Redirect validated redirect path + Redirect string `json:"redirect"` + + // Token JWT token + Token string `json:"token"` +} + // CustomerId defines model for CustomerId. type CustomerId = string @@ -186,12 +216,6 @@ type StartingAfter = string // SubscriptionId defines model for SubscriptionId. type SubscriptionId = string -// DeleteAdminParams defines parameters for DeleteAdmin. -type DeleteAdminParams struct { - // Id 管理者の traQ ID - Id string `form:"id" json:"id"` -} - // GetCustomerParams defines parameters for GetCustomer. type GetCustomerParams struct { // CustomerId Customer ID @@ -261,15 +285,18 @@ type GetInvoicesParamsStatus string // GetInvoicesParamsCollectionMethod defines parameters for GetInvoices. type GetInvoicesParamsCollectionMethod string +// PostVerifyEmailParams defines parameters for PostVerifyEmail. +type PostVerifyEmailParams struct { + // Redirect 検証後に遷移する相対パス + Redirect *string `form:"redirect,omitempty" json:"redirect,omitempty"` +} + // PostWebhookInvoicePaidParams defines parameters for PostWebhookInvoicePaid. type PostWebhookInvoicePaidParams struct { // StripeSignature Stripe が送信する認証情報 StripeSignature string `json:"Stripe-Signature"` } -// PostAdminJSONRequestBody defines body for PostAdmin for application/json ContentType. -type PostAdminJSONRequestBody = Admin - // PatchCustomerJSONRequestBody defines body for PatchCustomer for application/json ContentType. type PatchCustomerJSONRequestBody = PostCustomerRequest @@ -279,20 +306,17 @@ type PostCustomerJSONRequestBody = PostCustomerRequest // PostInvoiceJSONRequestBody defines body for PostInvoice for application/json ContentType. type PostInvoiceJSONRequestBody = PostInvoiceRequest +// PostVerifyEmailJSONRequestBody defines body for PostVerifyEmail for application/json ContentType. +type PostVerifyEmailJSONRequestBody = VerifyEmailRequest + // PostWebhookInvoicePaidJSONRequestBody defines body for PostWebhookInvoicePaid for application/json ContentType. -type PostWebhookInvoicePaidJSONRequestBody = Invoice +type PostWebhookInvoicePaidJSONRequestBody = StripeEvent // ServerInterface represents all server handlers. type ServerInterface interface { - // 管理者を削除 - // (DELETE /admin) - DeleteAdmin(ctx echo.Context, params DeleteAdminParams) error // 管理者の一覧を取得 // (GET /admin) GetAdmins(ctx echo.Context) error - // 管理者を作成 - // (POST /admin) - PostAdmin(ctx echo.Context) error // Customer を取得 // (GET /customer) GetCustomer(ctx echo.Context, params GetCustomerParams) error @@ -311,6 +335,9 @@ type ServerInterface interface { // 請求書由来の入金一覧を取得 // (GET /list/invoices) GetInvoices(ctx echo.Context, params GetInvoicesParams) error + // isct メールアドレスを検証して JWT を発行 + // (POST /verify-email) + PostVerifyEmail(ctx echo.Context, params PostVerifyEmailParams) error // Webhook の invoice.paid イベントを受け取る // (POST /webhook/invoice-paid) PostWebhookInvoicePaid(ctx echo.Context, params PostWebhookInvoicePaidParams) error @@ -321,24 +348,6 @@ type ServerInterfaceWrapper struct { Handler ServerInterface } -// DeleteAdmin converts echo context to params. -func (w *ServerInterfaceWrapper) DeleteAdmin(ctx echo.Context) error { - var err error - - // Parameter object where we will unmarshal all parameters from the context - var params DeleteAdminParams - // ------------- Required query parameter "id" ------------- - - err = runtime.BindQueryParameter("form", true, true, "id", ctx.QueryParams(), ¶ms.Id) - if err != nil { - return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter id: %s", err)) - } - - // Invoke the callback with all the unmarshaled arguments - err = w.Handler.DeleteAdmin(ctx, params) - return err -} - // GetAdmins converts echo context to params. func (w *ServerInterfaceWrapper) GetAdmins(ctx echo.Context) error { var err error @@ -348,15 +357,6 @@ func (w *ServerInterfaceWrapper) GetAdmins(ctx echo.Context) error { return err } -// PostAdmin converts echo context to params. -func (w *ServerInterfaceWrapper) PostAdmin(ctx echo.Context) error { - var err error - - // Invoke the callback with all the unmarshaled arguments - err = w.Handler.PostAdmin(ctx) - return err -} - // GetCustomer converts echo context to params. func (w *ServerInterfaceWrapper) GetCustomer(ctx echo.Context) error { var err error @@ -536,6 +536,24 @@ func (w *ServerInterfaceWrapper) GetInvoices(ctx echo.Context) error { return err } +// PostVerifyEmail converts echo context to params. +func (w *ServerInterfaceWrapper) PostVerifyEmail(ctx echo.Context) error { + var err error + + // Parameter object where we will unmarshal all parameters from the context + var params PostVerifyEmailParams + // ------------- Optional query parameter "redirect" ------------- + + err = runtime.BindQueryParameter("form", true, false, "redirect", ctx.QueryParams(), ¶ms.Redirect) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter redirect: %s", err)) + } + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.PostVerifyEmail(ctx, params) + return err +} + // PostWebhookInvoicePaid converts echo context to params. func (w *ServerInterfaceWrapper) PostWebhookInvoicePaid(ctx echo.Context) error { var err error @@ -595,15 +613,14 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL Handler: si, } - router.DELETE(baseURL+"/admin", wrapper.DeleteAdmin) router.GET(baseURL+"/admin", wrapper.GetAdmins) - router.POST(baseURL+"/admin", wrapper.PostAdmin) router.GET(baseURL+"/customer", wrapper.GetCustomer) router.PATCH(baseURL+"/customer", wrapper.PatchCustomer) router.POST(baseURL+"/customer", wrapper.PostCustomer) router.POST(baseURL+"/invoice", wrapper.PostInvoice) router.GET(baseURL+"/list/checkout-sessions", wrapper.GetCheckoutSessions) router.GET(baseURL+"/list/invoices", wrapper.GetInvoices) + router.POST(baseURL+"/verify-email", wrapper.PostVerifyEmail) router.POST(baseURL+"/webhook/invoice-paid", wrapper.PostWebhookInvoicePaid) } @@ -611,40 +628,45 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", } // GetSwagger returns the content of the embedded swagger specification file From 40b4e4c72b669e8dd176528da6d872d47090a56a Mon Sep 17 00:00:00 2001 From: kaitoyama Date: Sun, 22 Mar 2026 18:11:06 +0900 Subject: [PATCH 2/6] Harden email verification API flow --- openapi.yaml | 73 +++++++++++++--- server/gen.go | 236 +++++++++++++++++++++++++++++++++++++++----------- 2 files changed, 247 insertions(+), 62 deletions(-) diff --git a/openapi.yaml b/openapi.yaml index ecd53a5..fe192cb 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -8,7 +8,7 @@ info: paths: /verify-email: post: - summary: isct メールアドレスを検証して JWT を発行 + summary: isct メールアドレスの確認メールを送信 tags: - Auth operationId: postVerifyEmail @@ -25,16 +25,48 @@ paths: schema: $ref: '#/components/schemas/VerifyEmailRequest' responses: - '200': - description: OK + '202': + description: Accepted content: application/json: schema: - $ref: '#/components/schemas/VerifyEmailResponse' + $ref: '#/components/schemas/VerifyEmailStartResponse' + '400': + $ref: '#/components/responses/BadRequest' + '500': + $ref: '#/components/responses/InternalServerError' + /verify-email/confirm: + get: + summary: メール確認トークンを検証してログインを完了 + tags: + - Auth + operationId: getVerifyEmailConfirm + parameters: + - name: token + in: query + required: true + description: 確認メールに含まれるワンタイムトークン + schema: + type: string + minLength: 32 + responses: + '303': + description: See Other '400': $ref: '#/components/responses/BadRequest' + '401': + description: Unauthorized '500': $ref: '#/components/responses/InternalServerError' + /csrf: + get: + summary: CSRF トークン用 cookie を発行 + tags: + - Auth + operationId: getCsrf + responses: + '204': + description: No Content /customer: get: summary: Customer を取得 @@ -69,6 +101,8 @@ paths: tags: - Customer operationId: postCustomer + parameters: + - $ref: '#/components/parameters/CsrfToken' requestBody: content: application/json: @@ -88,6 +122,8 @@ paths: tags: - Customer operationId: patchCustomer + parameters: + - $ref: '#/components/parameters/CsrfToken' requestBody: content: application/json: @@ -108,6 +144,8 @@ paths: tags: - Invoice operationId: postInvoice + parameters: + - $ref: '#/components/parameters/CsrfToken' requestBody: content: application/json: @@ -249,6 +287,14 @@ paths: $ref: '#/components/responses/InternalServerError' components: parameters: + CsrfToken: + name: X-CSRF-Token + in: header + description: Double-submit CSRF token + required: true + schema: + type: string + minLength: 16 CustomerId: name: customer_id in: query @@ -293,32 +339,39 @@ components: description: Not found InternalServerError: description: Internal server error + securitySchemes: + cookieAuth: + type: apiKey + in: cookie + name: checkin_session schemas: VerifyEmailRequest: description: Verify email request type: object + additionalProperties: false properties: email: type: string description: isct email + format: email + maxLength: 255 + pattern: (?i)^[^@\s]+@isct\.ac\.jp$ required: - email - VerifyEmailResponse: - description: Verify email response + VerifyEmailStartResponse: + description: Verify email start response type: object + additionalProperties: false properties: email: type: string description: normalized email - token: - type: string - description: JWT token redirect: type: string description: validated redirect path + pattern: ^/(|[^/].*)$ required: - email - - token - redirect Admin: description: 管理者の情報 diff --git a/server/gen.go b/server/gen.go index d720259..e5fd65d 100644 --- a/server/gen.go +++ b/server/gen.go @@ -16,6 +16,7 @@ import ( "github.com/getkin/kin-openapi/openapi3" "github.com/labstack/echo/v4" "github.com/oapi-codegen/runtime" + openapi_types "github.com/oapi-codegen/runtime/types" ) // Defines values for GetCheckoutSessionsResponseDataStatus. @@ -186,21 +187,21 @@ type StripeEvent map[string]interface{} // VerifyEmailRequest Verify email request type VerifyEmailRequest struct { // Email isct email - Email string `json:"email"` + Email openapi_types.Email `json:"email"` } -// VerifyEmailResponse Verify email response -type VerifyEmailResponse struct { +// VerifyEmailStartResponse Verify email start response +type VerifyEmailStartResponse struct { // Email normalized email Email string `json:"email"` // Redirect validated redirect path Redirect string `json:"redirect"` - - // Token JWT token - Token string `json:"token"` } +// CsrfToken defines model for CsrfToken. +type CsrfToken = string + // CustomerId defines model for CustomerId. type CustomerId = string @@ -228,6 +229,24 @@ type GetCustomerParams struct { Email *string `form:"email,omitempty" json:"email,omitempty"` } +// PatchCustomerParams defines parameters for PatchCustomer. +type PatchCustomerParams struct { + // XCSRFToken Double-submit CSRF token + XCSRFToken CsrfToken `json:"X-CSRF-Token"` +} + +// PostCustomerParams defines parameters for PostCustomer. +type PostCustomerParams struct { + // XCSRFToken Double-submit CSRF token + XCSRFToken CsrfToken `json:"X-CSRF-Token"` +} + +// PostInvoiceParams defines parameters for PostInvoice. +type PostInvoiceParams struct { + // XCSRFToken Double-submit CSRF token + XCSRFToken CsrfToken `json:"X-CSRF-Token"` +} + // GetCheckoutSessionsParams defines parameters for GetCheckoutSessions. type GetCheckoutSessionsParams struct { // CustomerId Customer ID @@ -291,6 +310,12 @@ type PostVerifyEmailParams struct { Redirect *string `form:"redirect,omitempty" json:"redirect,omitempty"` } +// GetVerifyEmailConfirmParams defines parameters for GetVerifyEmailConfirm. +type GetVerifyEmailConfirmParams struct { + // Token 確認メールに含まれるワンタイムトークン + Token string `form:"token" json:"token"` +} + // PostWebhookInvoicePaidParams defines parameters for PostWebhookInvoicePaid. type PostWebhookInvoicePaidParams struct { // StripeSignature Stripe が送信する認証情報 @@ -317,27 +342,33 @@ type ServerInterface interface { // 管理者の一覧を取得 // (GET /admin) GetAdmins(ctx echo.Context) error + // CSRF トークン用 cookie を発行 + // (GET /csrf) + GetCsrf(ctx echo.Context) error // Customer を取得 // (GET /customer) GetCustomer(ctx echo.Context, params GetCustomerParams) error // Customer を更新 // (PATCH /customer) - PatchCustomer(ctx echo.Context) error + PatchCustomer(ctx echo.Context, params PatchCustomerParams) error // Customer を作成 // (POST /customer) - PostCustomer(ctx echo.Context) error + PostCustomer(ctx echo.Context, params PostCustomerParams) error // Invoice を作成 // (POST /invoice) - PostInvoice(ctx echo.Context) error + PostInvoice(ctx echo.Context, params PostInvoiceParams) error // オンライン決済ページ由来の入金一覧を取得 // (GET /list/checkout-sessions) GetCheckoutSessions(ctx echo.Context, params GetCheckoutSessionsParams) error // 請求書由来の入金一覧を取得 // (GET /list/invoices) GetInvoices(ctx echo.Context, params GetInvoicesParams) error - // isct メールアドレスを検証して JWT を発行 + // isct メールアドレスの確認メールを送信 // (POST /verify-email) PostVerifyEmail(ctx echo.Context, params PostVerifyEmailParams) error + // メール確認トークンを検証してログインを完了 + // (GET /verify-email/confirm) + GetVerifyEmailConfirm(ctx echo.Context, params GetVerifyEmailConfirmParams) error // Webhook の invoice.paid イベントを受け取る // (POST /webhook/invoice-paid) PostWebhookInvoicePaid(ctx echo.Context, params PostWebhookInvoicePaidParams) error @@ -357,6 +388,15 @@ func (w *ServerInterfaceWrapper) GetAdmins(ctx echo.Context) error { return err } +// GetCsrf converts echo context to params. +func (w *ServerInterfaceWrapper) GetCsrf(ctx echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.GetCsrf(ctx) + return err +} + // GetCustomer converts echo context to params. func (w *ServerInterfaceWrapper) GetCustomer(ctx echo.Context) error { var err error @@ -393,8 +433,30 @@ func (w *ServerInterfaceWrapper) GetCustomer(ctx echo.Context) error { func (w *ServerInterfaceWrapper) PatchCustomer(ctx echo.Context) error { var err error + // Parameter object where we will unmarshal all parameters from the context + var params PatchCustomerParams + + headers := ctx.Request().Header + // ------------- Required header parameter "X-CSRF-Token" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("X-CSRF-Token")]; found { + var XCSRFToken CsrfToken + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-CSRF-Token, got %d", n)) + } + + err = runtime.BindStyledParameterWithOptions("simple", "X-CSRF-Token", valueList[0], &XCSRFToken, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-CSRF-Token: %s", err)) + } + + params.XCSRFToken = XCSRFToken + } else { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Header parameter X-CSRF-Token is required, but not found")) + } + // Invoke the callback with all the unmarshaled arguments - err = w.Handler.PatchCustomer(ctx) + err = w.Handler.PatchCustomer(ctx, params) return err } @@ -402,8 +464,30 @@ func (w *ServerInterfaceWrapper) PatchCustomer(ctx echo.Context) error { func (w *ServerInterfaceWrapper) PostCustomer(ctx echo.Context) error { var err error + // Parameter object where we will unmarshal all parameters from the context + var params PostCustomerParams + + headers := ctx.Request().Header + // ------------- Required header parameter "X-CSRF-Token" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("X-CSRF-Token")]; found { + var XCSRFToken CsrfToken + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-CSRF-Token, got %d", n)) + } + + err = runtime.BindStyledParameterWithOptions("simple", "X-CSRF-Token", valueList[0], &XCSRFToken, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-CSRF-Token: %s", err)) + } + + params.XCSRFToken = XCSRFToken + } else { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Header parameter X-CSRF-Token is required, but not found")) + } + // Invoke the callback with all the unmarshaled arguments - err = w.Handler.PostCustomer(ctx) + err = w.Handler.PostCustomer(ctx, params) return err } @@ -411,8 +495,30 @@ func (w *ServerInterfaceWrapper) PostCustomer(ctx echo.Context) error { func (w *ServerInterfaceWrapper) PostInvoice(ctx echo.Context) error { var err error + // Parameter object where we will unmarshal all parameters from the context + var params PostInvoiceParams + + headers := ctx.Request().Header + // ------------- Required header parameter "X-CSRF-Token" ------------- + if valueList, found := headers[http.CanonicalHeaderKey("X-CSRF-Token")]; found { + var XCSRFToken CsrfToken + n := len(valueList) + if n != 1 { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Expected one value for X-CSRF-Token, got %d", n)) + } + + err = runtime.BindStyledParameterWithOptions("simple", "X-CSRF-Token", valueList[0], &XCSRFToken, runtime.BindStyledParameterOptions{ParamLocation: runtime.ParamLocationHeader, Explode: false, Required: true}) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter X-CSRF-Token: %s", err)) + } + + params.XCSRFToken = XCSRFToken + } else { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Header parameter X-CSRF-Token is required, but not found")) + } + // Invoke the callback with all the unmarshaled arguments - err = w.Handler.PostInvoice(ctx) + err = w.Handler.PostInvoice(ctx, params) return err } @@ -554,6 +660,24 @@ func (w *ServerInterfaceWrapper) PostVerifyEmail(ctx echo.Context) error { return err } +// GetVerifyEmailConfirm converts echo context to params. +func (w *ServerInterfaceWrapper) GetVerifyEmailConfirm(ctx echo.Context) error { + var err error + + // Parameter object where we will unmarshal all parameters from the context + var params GetVerifyEmailConfirmParams + // ------------- Required query parameter "token" ------------- + + err = runtime.BindQueryParameter("form", true, true, "token", ctx.QueryParams(), ¶ms.Token) + if err != nil { + return echo.NewHTTPError(http.StatusBadRequest, fmt.Sprintf("Invalid format for parameter token: %s", err)) + } + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.GetVerifyEmailConfirm(ctx, params) + return err +} + // PostWebhookInvoicePaid converts echo context to params. func (w *ServerInterfaceWrapper) PostWebhookInvoicePaid(ctx echo.Context) error { var err error @@ -614,6 +738,7 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL } router.GET(baseURL+"/admin", wrapper.GetAdmins) + router.GET(baseURL+"/csrf", wrapper.GetCsrf) router.GET(baseURL+"/customer", wrapper.GetCustomer) router.PATCH(baseURL+"/customer", wrapper.PatchCustomer) router.POST(baseURL+"/customer", wrapper.PostCustomer) @@ -621,6 +746,7 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL router.GET(baseURL+"/list/checkout-sessions", wrapper.GetCheckoutSessions) router.GET(baseURL+"/list/invoices", wrapper.GetInvoices) router.POST(baseURL+"/verify-email", wrapper.PostVerifyEmail) + router.GET(baseURL+"/verify-email/confirm", wrapper.GetVerifyEmailConfirm) router.POST(baseURL+"/webhook/invoice-paid", wrapper.PostWebhookInvoicePaid) } @@ -628,45 +754,51 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", } // GetSwagger returns the content of the embedded swagger specification file From 5b0a7912b7b4b012374a9563b2b00b855f230f1d Mon Sep 17 00:00:00 2001 From: kaitoyama Date: Sun, 22 Mar 2026 18:47:35 +0900 Subject: [PATCH 3/6] Fix OpenAPI regex and cookie auth usage --- openapi.yaml | 10 ++++- server/gen.go | 103 ++++++++++++++++++++++++++++---------------------- 2 files changed, 67 insertions(+), 46 deletions(-) diff --git a/openapi.yaml b/openapi.yaml index fe192cb..06ea681 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -73,6 +73,8 @@ paths: tags: - Customer operationId: getCustomer + security: + - cookieAuth: [] parameters: - $ref: '#/components/parameters/CustomerId' - name: traq_id @@ -101,6 +103,8 @@ paths: tags: - Customer operationId: postCustomer + security: + - cookieAuth: [] parameters: - $ref: '#/components/parameters/CsrfToken' requestBody: @@ -122,6 +126,8 @@ paths: tags: - Customer operationId: patchCustomer + security: + - cookieAuth: [] parameters: - $ref: '#/components/parameters/CsrfToken' requestBody: @@ -144,6 +150,8 @@ paths: tags: - Invoice operationId: postInvoice + security: + - cookieAuth: [] parameters: - $ref: '#/components/parameters/CsrfToken' requestBody: @@ -355,7 +363,7 @@ components: description: isct email format: email maxLength: 255 - pattern: (?i)^[^@\s]+@isct\.ac\.jp$ + pattern: ^[^@\s]+@[iI][sS][cC][tT]\.[aA][cC]\.[jJ][pP]$ required: - email VerifyEmailStartResponse: diff --git a/server/gen.go b/server/gen.go index e5fd65d..ccc5a39 100644 --- a/server/gen.go +++ b/server/gen.go @@ -19,6 +19,10 @@ import ( openapi_types "github.com/oapi-codegen/runtime/types" ) +const ( + CookieAuthScopes = "cookieAuth.Scopes" +) + // Defines values for GetCheckoutSessionsResponseDataStatus. const ( GetCheckoutSessionsResponseDataStatusComplete GetCheckoutSessionsResponseDataStatus = "complete" @@ -401,6 +405,8 @@ func (w *ServerInterfaceWrapper) GetCsrf(ctx echo.Context) error { func (w *ServerInterfaceWrapper) GetCustomer(ctx echo.Context) error { var err error + ctx.Set(CookieAuthScopes, []string{}) + // Parameter object where we will unmarshal all parameters from the context var params GetCustomerParams // ------------- Optional query parameter "customer_id" ------------- @@ -433,6 +439,8 @@ func (w *ServerInterfaceWrapper) GetCustomer(ctx echo.Context) error { func (w *ServerInterfaceWrapper) PatchCustomer(ctx echo.Context) error { var err error + ctx.Set(CookieAuthScopes, []string{}) + // Parameter object where we will unmarshal all parameters from the context var params PatchCustomerParams @@ -464,6 +472,8 @@ func (w *ServerInterfaceWrapper) PatchCustomer(ctx echo.Context) error { func (w *ServerInterfaceWrapper) PostCustomer(ctx echo.Context) error { var err error + ctx.Set(CookieAuthScopes, []string{}) + // Parameter object where we will unmarshal all parameters from the context var params PostCustomerParams @@ -495,6 +505,8 @@ func (w *ServerInterfaceWrapper) PostCustomer(ctx echo.Context) error { func (w *ServerInterfaceWrapper) PostInvoice(ctx echo.Context) error { var err error + ctx.Set(CookieAuthScopes, []string{}) + // Parameter object where we will unmarshal all parameters from the context var params PostInvoiceParams @@ -754,51 +766,52 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "H4sIAAAAAAAC/+xaa1cUyfn/Kn367wv9Z2C4icqbKIgrKyqRNe6J4Jya7hqmZLp7rK5G0XCO3eP1gFlx", - "FRc1J4vR1cgGNeaCl+iHKRjgW+RU9WW6p6tnhovE5ORlVz1Vz1PPvX7Vl2TF0IqGDnViyl2X5CLAQIME", - "Yv7VY+LcN8Yo1NmHCk0FoyJBhi53yQcNK1uATaaV1RCRegZPHJIIp0zJiM3nIVAhllOyDjQod8nfNjGa", - "pm88GgzPWQhDVe4i2IIp2VTyUAOMjYb0fqiPkLzc1dqZksl4kS03CUb6iDwxkZJ7LJMYGsR9alwqf07q", - "O+gLcs6CeLwih+JRZJAqh9nCC0ArFjyKzDH4m/PqMdLfe/xba6xbFonRq6tIH+mGOQPDuCDlqevLCw+o", - "fY86U9T+g9R3UKL2AnVe0NIMdRap84w6L2npxtL7p8s3bwmnqHNn+buZ5Y/3E04CuQCZrCtB+CxxYfuR", - "hkhcSnf/pfd/L997lcClwFeGd1dhDlgFIne1tqRkDVxAmqWxD/aFdO8r0BjSCRyBmEsxSAAmSB85kCMQ", - "b0pna7Mb05npSZABXITaShu0soF4ImcLzyc7nBmiSnY608rWd7oJFjdm0dBNyMOzG6gn4DkLmgLTdgNV", - "wt7kREo+ZOAsUlVRJFemJlJyn04g1kFhEOIxiHsxNgSG8okkk1NJkJNNpORjBjlkWLpAV8cMIuX4FDuF", - "qwF+hgOqhgRCrSzMrdy+tnr5KrUXyqWryz++llNyERtFiAlyT48EbAgGv3JNUVEtwWBAGMGVHHSabTYc", - "0BjZs1DhauvBEBDYp48ZSIEnPN2LFMIJJIWRM2/A0GQhEhPZpcuIRPf3qJIe6ZnWo+Rwtrd/VO07f9ja", - "gy4UOo5fvDhwdCx+qJRcBOMa1EnGwoU4i8OGSaAqeVJIHq108kR/hGWekKLZlU57dM1s9yJsVgwt3dzc", - "XF+VlUNG5REq2EvHNVJ5sgdADSDBMXv5cPhEZ6Guj2PDhPp+b5CdRqQ/VLeorK9Q+GkgFg9sNLzZ10xE", - "6QSTUbQLweBcZtMeH1P/V5D05KEyalhkEJomMnQz2ct5un1DS3+izhNaelN+/a68eIOWHtDSB+osrtx9", - "Xf79U2ovLF99unZ9emnx8upPz2ImUwHhmQ8RqLkNR2QaaIalk4xpZYlBgMC2yzf/svzhHi29X3n+u7Xr", - "0yzf356n9kdqv6D2lfLdl+Wb96h9Ze369NrcrbBSWvft25uScwbWAHHLUmeHHK9SKV+EBP41OLS1NsiB", - "ZwkosOXSPx+Vb9wu339annWknSd1dEEqzzprM9/vipykc8++zpaWttbdjXELRdgODHNyl/x/6UrTl/Zy", - "cTqIxKQo8PxE8hwlFg1mhkCTZFrb2jt2d+7Zu6+lVn5iAuqCskXt76m9IMWYUXt+bebx2uU/Lr3/gdrT", - "1LnpNQkD7oZ97n5hgYoo036UnO8+X8mcbXtBOzk3AISSYUO1FCIMsgF3rvrIbEnm2Kmz5t7RI9mvT2mW", - "pYh2NgkgltmARnlL85aWrvGI+kSdt4ydzhqq07xPL0DiZo0iT7Yp2ShCneXVikx8pKHY9wYAxmCcfeeB", - "mdHEzezPc0w0P9KpPUVthzqT1J6ssMoaRgECXczLK27JVS85yTeWMVRLJPejyyt/c1YeXKH2vB+41+KB", - "27KevFAEIgcJ0gLLiPanTfLArLDpzHA1GC1Mrs3/sH4Wm04+e1s6Ozo6Ovdsa/LZ2uaodvJZnZ8sv3bK", - "Dxf/G3NO5XCJiUbFIEf83MKUxhs5S1eMQgEqBGULLAeNGW7LXJHMX/eFpZ4BwyS+eyXeloIWz40CWnrB", - "7pPOc66lG+ttPNfXA26k16vdgUNPDs52OEEnwaUmQSV+yDWmkTCsUqeB3ngU1D51FNkJbSrSwCC/0/SO", - "eXkAqCpiHEFhIHQsF5qKCvQV1CFGiuRuIEG2A7tJFQygygJGv4YY5ca5Z4RULeaXAwUzxtDdQOI2DW70", - "DTokMhUi+d4QpGt/QAMXfKStbfduFumE3evlLnnnL9GuM6fP7B8aMod/sZ/tMjTUDJShoeazxR0Nut9w", - "bWVwLCh819ioSjikI/m4SKOa0ZkyCugiVAP9xPwSQxVhJnls9RgoIJWVUcmnkYqA5OWwDs+kd/729Jn0", - "cPP/79rRcMQGLOPaY0kdKhZGZHyQVU0v7gxjFMEDFjPiJRd/codCiCdrM5GeMd0usyILKKIjcNzFlZCe", - "MxI6VKRLBwb62DJEeKKPjo5BbLrULc2tzS1McaxygCKSu+R2PsTVkufypoGP94xArlhmK+AjbOw+ygEh", - "U66CutpaWtzjBqUbFIsFpPCl6bOmwfesIGtBt1ir63Cxp1g9YvqI6uH4EUbV0dKetGMgazqCpu12pa69", - "QgS5cWtbmgbweBUY5t6rwzgnASMm8yH3NMNsaVoxca6WknvYfEzFHSLcTurxdB6ViSP+tHSD1+WXtPRm", - "5e5zyXU9iTp3Vmbfrc5NhaVjLuoJF2oKEwX0aVKRJ4nTYmVWSNKh14GJVHIBFSG1ft2tiQon1XwhQu/N", - "JW83vEk3b6ynTnbojvruGWC6W+fPFWhP4MaB2MO8YydKPu4gA2x44y4SvGq5+ucltdtQx7dM9aKecyKO", - "32+vtT+D8coP/1qeeZVoPMMUBHdYN//JpmvdFtP1eNf1z2M/t7cX249lalSBbZKN6WM7X6Ytq645n9uU", - "wveibbFrgKQJzOqbyLVqAZkkrXjgY5PXFpo1q3HVG8Fmq3Id6qqH1wZWuO/bjWwdeYJuYEHkmT9e/iNQ", - "UHJrEUWe1t1kNIYUJzx4E8uMPjo3AihvZ6NS6xHqy2vGN/AKJmpz+pFJwvHopdqaYdjn0/wv/OrDmeuM", - "hvWhnnUjNgQiL1Qw+5m35Tf3kn5M8hgZekaDJG+o4qDNAzwCM8AihgYIUkChwPYwoa5m/Gq9zeHrF5cv", - "L1QDI2wkKsc4yNQU4EfJDVAI1YpHZhWc/eTR6vMPyx+nqD2/Zv9j5dl7as9SZ3Ll4eLyy4+0NJ3suwE0", - "VPc+ufWNlADEnIhCWARbMN5YtX0OCaLIocDrDigKLHrNVUcjnhT6j2rrnI9jr7Q0x3JSaZ46j2npJi39", - "zBKVvbDy+N3qi1uh2Ttrl+2lT3NiwCTsimnF0HMIa7XqREhZPR51Hb+slsee936rcKaoM0lLL1nJY5n1", - "CS39GEZ9kqCU9fzm2d5WP2G1uzmk6gc8CKXjJO8+G27A0h1u5x/d9KQOLJI3MLq4pe15oFtf1RUdsos0", - "zwvUvk/tn2jpz9R55XYXLEctTC29uyZ2jPMwmzeMUb95aPIfp5Nz1Sl3hZezB4D3k1YN1/AeOqg95bqo", - "m7BWX9xaff4heLIX/vLrrmwaRCM6IBb/RzXZH7Ypk4XffRpKYWJM1Jfl35phPFuyy4D/T18z8wCJ+84s", - "71LdP2LvU3t6+bsZ6kyG/MhbzlxpIhitPitzNg/o96zK3U9wRfFhhQhx6F0/6X0xQh/0EjFy/7ARcv8I", - "cXJWyKO0vLTHCTlqXnVC91lgeOJfAQAA//9EzjTDHS8AAA==", + "H4sIAAAAAAAC/+xaa1cUyfn/Kn36vy92/xkYbqLyaoHFFUUlonFPxnFOTXcNUzLdPVZXo2g4x+7xesCs", + "uIqLmpPF6GpkgxpzwUv0wxQM8C1yqvoy3dPVwwAua3LybqbrqaqnnsuvnvpVXZAVQysbOtSJKfdckMsA", + "Aw0SiPm/fhMXjhljUGd/VGgqGJUJMnS5R/7KsPIl2GJaeQ0RqX/k6D6JcMmUjFh7EQIVYjkl60CDco/8", + "TQuTaTnmyWB4xkIYqnIPwRZMyaZShBpg02hIH4L6KCnKPe3dKZlMlFl3k2Ckj8qTkym53zKJoUE8qMa1", + "8tukwa98Rc5YEE/U9FA8iRxS5fC08BzQyiVPIncY/vasepgMDRz5xhrvk0VqDOgq0kf7YMHAMK5Idfrq", + "yuI9at+hzjS1/ygNfiVRe5E6z2hlljpL1HlCnee0cm357eOV6zeETdS5tfLt7Mr7uwkrgVyBXN7VILyW", + "uLJDSEMkrqU7/vLbf1TvvEiYpcR7hkdXYQFYJSL3tLelZA2cQ5qlsT/sH9K9f4HFkE7gKMRcixECMEH6", + "aG+BQLwtm63Pbc1mpqdBDnAVGhttxMoH6omCLdyeHHBmSCo56Ewrv3HQTbK8McuGbkKenn1APQrPWNAU", + "uLYPqBL2GidT8j4D55GqijK51jSZkgd1ArEOSiMQj0M8gLEhcJQvJJlcSoJcbDIlHzbIPsPSBbY6bBCp", + "wJvYKlwL8DX0qhoSKLW6OL9688raxcvUXqxWLq/88FJOyWVslCEmyF09EkxDMPi164qaaQkGw8IMrmFQ", + "hg2WDWSM/GmocLP1YwgIHNTHDaTAo57tRQbhApLCxFk0YGiyFImp7MrlRKr7Y9Rpj/Rc+yGyPz8wNKYO", + "nt1v7UbnSl1Hzp8fPjQeX1RKLoMJDeokZ+FSfIr9hkmgKnlaSJ6sdPzoUGTKIiFlsyed9uRa2ehl2KoY", + "Wrq1tXVjU9YWGdVHaGAPjhtAeXIEQA0gwTIH+Ofwik5DXZ/Ahgn1L72PbDUi+6ENN5XNbRQ+DMTygX0N", + "D3aAqSgdZTqKRiEYnMltO+Jj5v8akv4iVMYMi4xA00SGbiZHOYfbV7TyZ+o8opVX1ZdvqkvXaOUerbyj", + "ztLq7ZfVPzym9uLK5cfrV2eWly6u/fgk5jIVEI58iEDNLTgizUAzLJ3kTCtPDAIEvl25/teVd3do5e3q", + "09+vX51heH9zgdrvqf2M2peqt59Xr9+h9qX1qzPr8zfCRmnfu3dPSi4YWAPE3Za6u+T4LpXyVUiYv8EM", + "He1NzsBRAgp8ufyvB9VrN6t3H1fnHOnz4zo6J1XnnPXZ776IrKR7997utraO9l3NzRbKsM8wLMg98v+l", + "a0Vf2sPidJCJSVngxYnkBUosG8wcgSbJtXd0du3q3r1nb1sjfGIK6oJti9rfUXtRik1G7YX12YfrF/+0", + "/PZ7as9Q57pXJAy7Aw6644UVKqNc5yFytu9sDTk79oBOcmYYCDXDhmopRJhkw25b/ZJZl9zhE6fNPWMH", + "8wdOaJaliEY2CSCW2YRFeUnzmlau8Iz6QJ3XbDqdFVQZXqeXIHFRo8zBNiUbZagzXK3pxL80lfveB4Ax", + "mGD/i8DMaeJi9qd5ppqf6dSeprZDnSlqT9WmyhtGCQJdPJe3uSXveskg3xxiqJZI7wcXV//urN67RO0F", + "P3GvxBO3bTO4UAaiAAlggSGi/WGbc2C2senMcQ0mWpxaX/h+81NsG3z2tHV3dXV1795R8Pm4xVFj8Flb", + "mKq+dKr3l/4bMae2uESgUTEoEB9bmNF4IWfpilEqQYWgfIlh0Ljhlsw1zfx+nxj0DBsm8cMr8bQUlHhu", + "FtDKM3aedJ5yK13bbOG5uRpwK7Ve4wocenrwabMJNgkONQkm8VOuOYuEaZUNCuitZ0HjVUeZndCgIguM", + "8DPNwLiHA0BVEZsRlIZDy3KpqahCX0MdYqRI7gASZCOwk1TJAKosmOg3EKPCBI+MkKnF8xVAyYxN6A4g", + "cZ8GJ/omAxKZCpH8aAjg2v+ggXM+09axaxfLdMLO9XKPfCpz6suTJ83sr77MoMFsxhzJZpT+bIYcy548", + "2ZoBvfwv+3n6QDZTHs5+1mRIZhsbiPND4fPHVs3EaR7J50qatZbODFRC56Ea2CwWqxiqCDPNY73HQQmp", + "bGuVfBmpDEhRjtg1/fnvMqfS2db//+KzprM4mDJuPQb0ULEwIhMjbCf1ctEwxhDstZhjL7iclPspxIKy", + "0hPpOdOtPGu6gDI6CCdcrgnpBSOhakW61Ds8yLohwsE/+nUcYtOVbmttb21jhmO7CSgjuUfu5J+4WYpc", + "3zTwOaBRyA3LfAV81o2dUTlJZMp19FdHW5u73GA7B+VyCSm8a/q0afAxa2xbUEE2qkRcPiq2RzF7RO1w", + "5CCT6mrrTBox0DUdYdh2uVo37iGi4bi3LU0DeKKOIHPP2mHuk4BRk8WQu5os65pWTFxoZOR+1h4zcZeI", + "y5P6PZtHdeK3ALRyje/Vz2nl1ertp5IbehJ1bq3OvVmbnw5rx0LUUy5UKCYq6MukItcUGbExayLp0I3B", + "ZCp5UxWxt/5e3JApTqoDhKy915Y8XHabYd5cnZ0c0F0bh2fA8247nj304l4M41Ymy+wQCq2ADRREebCq", + "LC/yiVKMx88w+7z1CAouwlz38F24z1AnPppnRGXqZJzy39lg2HnfVu//rTr7ItG3himAhrDp/pM9274j", + "nu33CIBfxL3uYULsXrYNoBpPlOxrn0z6NF1dd676uT0tvKD6FNweMHsCr/sedJ1eQiZJKx4Z2uKVpGbD", + "SqDuzmK7FcEG0nUXwU30cO/bmxk6ciXeRIfIs4N46RGhppLLmigTtukCpznmOuECnlhm9BK8GYJ7J4uk", + "Rpdin95BYAu3cqIaagiZJJyPHhI3TMNBX+Z/6bcxvbrJbNgcC7thxoZI7cXaHcLs6+qrO0kPpbyJDD2n", + "QVI0VHHSFgEehTlgEUMDBCmgVGJjmFBXc/5mvsPp628un16qBk7YSlaOc4KrJeCukuujEKMWz8w6ev3R", + "g7Wn71beT1N7Yd3+5+qTt9Seo87U6v2llefvaWUmOXYDWmrDs+zHr7MEpOpklD4j2ILxuqvj59AgyloK", + "oq5XUWDZq726momk0Luujxd8nAumlXmGSZUF6jykleu08hMDKntx9eGbtWc3Qq231i/ayx/mxWRNOBTT", + "iqEXENYa7RMhY/V70hvEZb0+9oL3zMOZps4UrTxnWx5D1ke08kOYcUqicTbz7LSzY2PA6nQxpO5BIITS", + "EVJ0rzG34Oku92AQHfS4DixSNDA6/zGq91rZ4NvWN3XNhuwYznGB2nep/SOt/IU6L9zqgmHU4vTymyvi", + "wDgL80XDGPOLhxb/sjwZq064PTzMHgbeo7EGoeFdvFB72g1RF7DWnt1Ye/oueEIgfILs9mwZQaM6IBZ/", + "M5scDzuEZOF7qKYgTMzH+rr8ogjj+ZIdBvw3hq0sAiQeO3O8SnVf6N6l9szKt7PUmQrFkdedhdJk8LV+", + "rSzYvEsGz6s8/ARHFJ91iAiH3hkk3XdG5INaIibuLzYi7i8hLs428qgs39rjgpyxr1uheyWRnfx3AAAA", + "//+gPhqOrS8AAA==", } // GetSwagger returns the content of the embedded swagger specification file From 57f9d9b28d44b237834bb42a14b49d7bf484da64 Mon Sep 17 00:00:00 2001 From: kaitoyama Date: Sun, 22 Mar 2026 19:03:42 +0900 Subject: [PATCH 4/6] Document redirect and CSRF contracts --- openapi.yaml | 32 ++++++++++---- server/gen.go | 114 +++++++++++++++++++++++++++----------------------- 2 files changed, 86 insertions(+), 60 deletions(-) diff --git a/openapi.yaml b/openapi.yaml index 06ea681..dce702e 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -13,11 +13,7 @@ paths: - Auth operationId: postVerifyEmail parameters: - - name: redirect - in: query - description: 検証後に遷移する相対パス - schema: - type: string + - $ref: '#/components/parameters/VerifyEmailRedirect' requestBody: required: true content: @@ -67,6 +63,14 @@ paths: responses: '204': description: No Content + headers: + Set-Cookie: + description: > + `checkin_csrf` cookie を発行する。state-changing request では、この cookie の値を + `X-CSRF-Token` header にそのままコピーして送る。 + schema: + type: string + example: checkin_csrf=csrf-token-value; Path=/; SameSite=Lax /customer: get: summary: Customer を取得 @@ -298,11 +302,19 @@ components: CsrfToken: name: X-CSRF-Token in: header - description: Double-submit CSRF token + description: > + Double-submit CSRF token。`/csrf` または `/verify-email/confirm` が発行した + `checkin_csrf` cookie の値をそのまま送る。 required: true schema: type: string minLength: 16 + VerifyEmailRedirect: + name: redirect + in: query + description: 検証後に遷移する相対パス + schema: + $ref: '#/components/schemas/RelativeRedirectPath' CustomerId: name: customer_id in: query @@ -353,6 +365,11 @@ components: in: cookie name: checkin_session schemas: + RelativeRedirectPath: + description: "`//` で始まらないアプリ内の相対パス" + type: string + pattern: ^/(|[^/].*)$ + example: /membership VerifyEmailRequest: description: Verify email request type: object @@ -375,9 +392,8 @@ components: type: string description: normalized email redirect: - type: string description: validated redirect path - pattern: ^/(|[^/].*)$ + $ref: '#/components/schemas/RelativeRedirectPath' required: - email - redirect diff --git a/server/gen.go b/server/gen.go index ccc5a39..d96eebc 100644 --- a/server/gen.go +++ b/server/gen.go @@ -185,6 +185,9 @@ type PostInvoiceRequest struct { ProductId string `json:"product_id"` } +// RelativeRedirectPath `//` で始まらないアプリ内の相対パス +type RelativeRedirectPath = string + // StripeEvent Generic Stripe event payload type StripeEvent map[string]interface{} @@ -199,8 +202,8 @@ type VerifyEmailStartResponse struct { // Email normalized email Email string `json:"email"` - // Redirect validated redirect path - Redirect string `json:"redirect"` + // Redirect `//` で始まらないアプリ内の相対パス + Redirect RelativeRedirectPath `json:"redirect"` } // CsrfToken defines model for CsrfToken. @@ -221,6 +224,9 @@ type StartingAfter = string // SubscriptionId defines model for SubscriptionId. type SubscriptionId = string +// VerifyEmailRedirect `//` で始まらないアプリ内の相対パス +type VerifyEmailRedirect = RelativeRedirectPath + // GetCustomerParams defines parameters for GetCustomer. type GetCustomerParams struct { // CustomerId Customer ID @@ -235,19 +241,19 @@ type GetCustomerParams struct { // PatchCustomerParams defines parameters for PatchCustomer. type PatchCustomerParams struct { - // XCSRFToken Double-submit CSRF token + // XCSRFToken Double-submit CSRF token。`/csrf` または `/verify-email/confirm` が発行した `checkin_csrf` cookie の値をそのまま送る。 XCSRFToken CsrfToken `json:"X-CSRF-Token"` } // PostCustomerParams defines parameters for PostCustomer. type PostCustomerParams struct { - // XCSRFToken Double-submit CSRF token + // XCSRFToken Double-submit CSRF token。`/csrf` または `/verify-email/confirm` が発行した `checkin_csrf` cookie の値をそのまま送る。 XCSRFToken CsrfToken `json:"X-CSRF-Token"` } // PostInvoiceParams defines parameters for PostInvoice. type PostInvoiceParams struct { - // XCSRFToken Double-submit CSRF token + // XCSRFToken Double-submit CSRF token。`/csrf` または `/verify-email/confirm` が発行した `checkin_csrf` cookie の値をそのまま送る。 XCSRFToken CsrfToken `json:"X-CSRF-Token"` } @@ -311,7 +317,7 @@ type GetInvoicesParamsCollectionMethod string // PostVerifyEmailParams defines parameters for PostVerifyEmail. type PostVerifyEmailParams struct { // Redirect 検証後に遷移する相対パス - Redirect *string `form:"redirect,omitempty" json:"redirect,omitempty"` + Redirect *VerifyEmailRedirect `form:"redirect,omitempty" json:"redirect,omitempty"` } // GetVerifyEmailConfirmParams defines parameters for GetVerifyEmailConfirm. @@ -766,52 +772,56 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "//+gPhqOrS8AAA==", + "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", + "ctV/BwAA//9UM4tONTIAAA==", } // GetSwagger returns the content of the embedded swagger specification file From 8f0bd1b660d7f531786d5a6609b3bf0b7b15dfd8 Mon Sep 17 00:00:00 2001 From: kaitoyama Date: Sun, 22 Mar 2026 20:40:34 +0900 Subject: [PATCH 5/6] Harden confirm flow and secure cookie contract --- openapi.yaml | 104 ++++++++++++++++++++++++++++++++++++--- server/gen.go | 133 ++++++++++++++++++++++++++++++-------------------- 2 files changed, 176 insertions(+), 61 deletions(-) diff --git a/openapi.yaml b/openapi.yaml index dce702e..54c446d 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -33,7 +33,7 @@ paths: $ref: '#/components/responses/InternalServerError' /verify-email/confirm: get: - summary: メール確認トークンを検証してログインを完了 + summary: メール確認トークンの確認ページを表示 tags: - Auth operationId: getVerifyEmailConfirm @@ -45,13 +45,61 @@ paths: schema: type: string minLength: 32 + responses: + '200': + description: OK + headers: + Cache-Control: + description: ブラウザや中継に保存させない + schema: + type: string + example: no-store + content: + text/html: + schema: + type: string + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '500': + $ref: '#/components/responses/InternalServerError' + post: + summary: メール確認トークンを消費してログインを完了 + tags: + - Auth + operationId: postVerifyEmailConfirm + requestBody: + required: true + content: + application/x-www-form-urlencoded: + schema: + $ref: '#/components/schemas/VerifyEmailConfirmRequest' responses: '303': description: See Other + headers: + Location: + description: 検証完了後に遷移する相対パス + schema: + $ref: '#/components/schemas/RelativeRedirectPath' + Cache-Control: + description: ブラウザや中継に保存させない + schema: + type: string + example: no-store + Set-Cookie: + description: > + `__Host-checkin_session` と `__Host-checkin_csrf` cookie を発行する。 + `__Host-checkin_csrf` の値は以後の state-changing request で + `X-CSRF-Token` header にそのままコピーして送る。 + schema: + type: string + example: __Host-checkin_session=session-token; Path=/; HttpOnly; Secure; SameSite=Lax '400': $ref: '#/components/responses/BadRequest' '401': - description: Unauthorized + $ref: '#/components/responses/Unauthorized' '500': $ref: '#/components/responses/InternalServerError' /csrf: @@ -66,11 +114,11 @@ paths: headers: Set-Cookie: description: > - `checkin_csrf` cookie を発行する。state-changing request では、この cookie の値を + `__Host-checkin_csrf` cookie を発行する。state-changing request では、この cookie の値を `X-CSRF-Token` header にそのままコピーして送る。 schema: type: string - example: checkin_csrf=csrf-token-value; Path=/; SameSite=Lax + example: __Host-checkin_csrf=csrf-token-value; Path=/; Secure; SameSite=Lax /customer: get: summary: Customer を取得 @@ -98,6 +146,12 @@ paths: application/json: schema: $ref: '#/components/schemas/Customer' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' '404': $ref: '#/components/responses/NotFound' '500': @@ -117,12 +171,24 @@ paths: schema: $ref: '#/components/schemas/PostCustomerRequest' responses: + '200': + description: OK + content: + application/json: + schema: + $ref: '#/components/schemas/Customer' '201': description: Created content: application/json: schema: $ref: '#/components/schemas/Customer' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' patch: @@ -146,6 +212,12 @@ paths: application/json: schema: $ref: '#/components/schemas/Customer' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' /invoice: @@ -170,6 +242,12 @@ paths: application/json: schema: $ref: '#/components/schemas/CreateInvoiceResponse' + '400': + $ref: '#/components/responses/BadRequest' + '401': + $ref: '#/components/responses/Unauthorized' + '403': + $ref: '#/components/responses/Forbidden' '500': $ref: '#/components/responses/InternalServerError' /webhook/invoice-paid: @@ -303,8 +381,8 @@ components: name: X-CSRF-Token in: header description: > - Double-submit CSRF token。`/csrf` または `/verify-email/confirm` が発行した - `checkin_csrf` cookie の値をそのまま送る。 + Double-submit CSRF token。`/csrf` または `POST /verify-email/confirm` が発行した + `__Host-checkin_csrf` cookie の値をそのまま送る。 required: true schema: type: string @@ -353,6 +431,8 @@ components: responses: BadRequest: description: Bad request + Unauthorized: + description: Unauthorized Forbidden: description: Forbidden NotFound: @@ -363,7 +443,7 @@ components: cookieAuth: type: apiKey in: cookie - name: checkin_session + name: __Host-checkin_session schemas: RelativeRedirectPath: description: "`//` で始まらないアプリ内の相対パス" @@ -383,6 +463,16 @@ components: pattern: ^[^@\s]+@[iI][sS][cC][tT]\.[aA][cC]\.[jJ][pP]$ required: - email + VerifyEmailConfirmRequest: + description: Verify email confirm request + type: object + additionalProperties: false + properties: + token: + type: string + minLength: 32 + required: + - token VerifyEmailStartResponse: description: Verify email start response type: object diff --git a/server/gen.go b/server/gen.go index d96eebc..73ad2d1 100644 --- a/server/gen.go +++ b/server/gen.go @@ -191,6 +191,11 @@ type RelativeRedirectPath = string // StripeEvent Generic Stripe event payload type StripeEvent map[string]interface{} +// VerifyEmailConfirmRequest Verify email confirm request +type VerifyEmailConfirmRequest struct { + Token string `json:"token"` +} + // VerifyEmailRequest Verify email request type VerifyEmailRequest struct { // Email isct email @@ -241,19 +246,19 @@ type GetCustomerParams struct { // PatchCustomerParams defines parameters for PatchCustomer. type PatchCustomerParams struct { - // XCSRFToken Double-submit CSRF token。`/csrf` または `/verify-email/confirm` が発行した `checkin_csrf` cookie の値をそのまま送る。 + // XCSRFToken Double-submit CSRF token。`/csrf` または `POST /verify-email/confirm` が発行した `__Host-checkin_csrf` cookie の値をそのまま送る。 XCSRFToken CsrfToken `json:"X-CSRF-Token"` } // PostCustomerParams defines parameters for PostCustomer. type PostCustomerParams struct { - // XCSRFToken Double-submit CSRF token。`/csrf` または `/verify-email/confirm` が発行した `checkin_csrf` cookie の値をそのまま送る。 + // XCSRFToken Double-submit CSRF token。`/csrf` または `POST /verify-email/confirm` が発行した `__Host-checkin_csrf` cookie の値をそのまま送る。 XCSRFToken CsrfToken `json:"X-CSRF-Token"` } // PostInvoiceParams defines parameters for PostInvoice. type PostInvoiceParams struct { - // XCSRFToken Double-submit CSRF token。`/csrf` または `/verify-email/confirm` が発行した `checkin_csrf` cookie の値をそのまま送る。 + // XCSRFToken Double-submit CSRF token。`/csrf` または `POST /verify-email/confirm` が発行した `__Host-checkin_csrf` cookie の値をそのまま送る。 XCSRFToken CsrfToken `json:"X-CSRF-Token"` } @@ -344,6 +349,9 @@ type PostInvoiceJSONRequestBody = PostInvoiceRequest // PostVerifyEmailJSONRequestBody defines body for PostVerifyEmail for application/json ContentType. type PostVerifyEmailJSONRequestBody = VerifyEmailRequest +// PostVerifyEmailConfirmFormdataRequestBody defines body for PostVerifyEmailConfirm for application/x-www-form-urlencoded ContentType. +type PostVerifyEmailConfirmFormdataRequestBody = VerifyEmailConfirmRequest + // PostWebhookInvoicePaidJSONRequestBody defines body for PostWebhookInvoicePaid for application/json ContentType. type PostWebhookInvoicePaidJSONRequestBody = StripeEvent @@ -376,9 +384,12 @@ type ServerInterface interface { // isct メールアドレスの確認メールを送信 // (POST /verify-email) PostVerifyEmail(ctx echo.Context, params PostVerifyEmailParams) error - // メール確認トークンを検証してログインを完了 + // メール確認トークンの確認ページを表示 // (GET /verify-email/confirm) GetVerifyEmailConfirm(ctx echo.Context, params GetVerifyEmailConfirmParams) error + // メール確認トークンを消費してログインを完了 + // (POST /verify-email/confirm) + PostVerifyEmailConfirm(ctx echo.Context) error // Webhook の invoice.paid イベントを受け取る // (POST /webhook/invoice-paid) PostWebhookInvoicePaid(ctx echo.Context, params PostWebhookInvoicePaidParams) error @@ -696,6 +707,15 @@ func (w *ServerInterfaceWrapper) GetVerifyEmailConfirm(ctx echo.Context) error { return err } +// PostVerifyEmailConfirm converts echo context to params. +func (w *ServerInterfaceWrapper) PostVerifyEmailConfirm(ctx echo.Context) error { + var err error + + // Invoke the callback with all the unmarshaled arguments + err = w.Handler.PostVerifyEmailConfirm(ctx) + return err +} + // PostWebhookInvoicePaid converts echo context to params. func (w *ServerInterfaceWrapper) PostWebhookInvoicePaid(ctx echo.Context) error { var err error @@ -765,6 +785,7 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL router.GET(baseURL+"/list/invoices", wrapper.GetInvoices) router.POST(baseURL+"/verify-email", wrapper.PostVerifyEmail) router.GET(baseURL+"/verify-email/confirm", wrapper.GetVerifyEmailConfirm) + router.POST(baseURL+"/verify-email/confirm", wrapper.PostVerifyEmailConfirm) router.POST(baseURL+"/webhook/invoice-paid", wrapper.PostWebhookInvoicePaid) } @@ -772,56 +793,60 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "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", - "ctV/BwAA//9UM4tONTIAAA==", + "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", } // GetSwagger returns the content of the embedded swagger specification file From c7720b84b6cfcffd45a3b29abbc1fb91f87cf08b Mon Sep 17 00:00:00 2001 From: kaitoyama Date: Sun, 22 Mar 2026 20:48:47 +0900 Subject: [PATCH 6/6] Document auth without validator enforcement --- openapi.yaml | 17 ++----- server/gen.go | 121 +++++++++++++++++++++++--------------------------- 2 files changed, 59 insertions(+), 79 deletions(-) diff --git a/openapi.yaml b/openapi.yaml index 54c446d..f1e3853 100644 --- a/openapi.yaml +++ b/openapi.yaml @@ -122,11 +122,10 @@ paths: /customer: get: summary: Customer を取得 + description: session cookie または信頼済み proxy auth が必要 tags: - Customer operationId: getCustomer - security: - - cookieAuth: [] parameters: - $ref: '#/components/parameters/CustomerId' - name: traq_id @@ -158,11 +157,10 @@ paths: $ref: '#/components/responses/InternalServerError' post: summary: Customer を作成 + description: session cookie または信頼済み proxy auth が必要。`X-CSRF-Token` も必須 tags: - Customer operationId: postCustomer - security: - - cookieAuth: [] parameters: - $ref: '#/components/parameters/CsrfToken' requestBody: @@ -193,11 +191,10 @@ paths: $ref: '#/components/responses/InternalServerError' patch: summary: Customer を更新 + description: session cookie または信頼済み proxy auth が必要。`X-CSRF-Token` も必須 tags: - Customer operationId: patchCustomer - security: - - cookieAuth: [] parameters: - $ref: '#/components/parameters/CsrfToken' requestBody: @@ -223,11 +220,10 @@ paths: /invoice: post: summary: Invoice を作成 + description: session cookie または信頼済み proxy auth が必要。`X-CSRF-Token` も必須 tags: - Invoice operationId: postInvoice - security: - - cookieAuth: [] parameters: - $ref: '#/components/parameters/CsrfToken' requestBody: @@ -439,11 +435,6 @@ components: description: Not found InternalServerError: description: Internal server error - securitySchemes: - cookieAuth: - type: apiKey - in: cookie - name: __Host-checkin_session schemas: RelativeRedirectPath: description: "`//` で始まらないアプリ内の相対パス" diff --git a/server/gen.go b/server/gen.go index 73ad2d1..fd617a8 100644 --- a/server/gen.go +++ b/server/gen.go @@ -19,10 +19,6 @@ import ( openapi_types "github.com/oapi-codegen/runtime/types" ) -const ( - CookieAuthScopes = "cookieAuth.Scopes" -) - // Defines values for GetCheckoutSessionsResponseDataStatus. const ( GetCheckoutSessionsResponseDataStatusComplete GetCheckoutSessionsResponseDataStatus = "complete" @@ -422,8 +418,6 @@ func (w *ServerInterfaceWrapper) GetCsrf(ctx echo.Context) error { func (w *ServerInterfaceWrapper) GetCustomer(ctx echo.Context) error { var err error - ctx.Set(CookieAuthScopes, []string{}) - // Parameter object where we will unmarshal all parameters from the context var params GetCustomerParams // ------------- Optional query parameter "customer_id" ------------- @@ -456,8 +450,6 @@ func (w *ServerInterfaceWrapper) GetCustomer(ctx echo.Context) error { func (w *ServerInterfaceWrapper) PatchCustomer(ctx echo.Context) error { var err error - ctx.Set(CookieAuthScopes, []string{}) - // Parameter object where we will unmarshal all parameters from the context var params PatchCustomerParams @@ -489,8 +481,6 @@ func (w *ServerInterfaceWrapper) PatchCustomer(ctx echo.Context) error { func (w *ServerInterfaceWrapper) PostCustomer(ctx echo.Context) error { var err error - ctx.Set(CookieAuthScopes, []string{}) - // Parameter object where we will unmarshal all parameters from the context var params PostCustomerParams @@ -522,8 +512,6 @@ func (w *ServerInterfaceWrapper) PostCustomer(ctx echo.Context) error { func (w *ServerInterfaceWrapper) PostInvoice(ctx echo.Context) error { var err error - ctx.Set(CookieAuthScopes, []string{}) - // Parameter object where we will unmarshal all parameters from the context var params PostInvoiceParams @@ -793,60 +781,61 @@ func RegisterHandlersWithBaseURL(router EchoRouter, si ServerInterface, baseURL // Base64 encoded, gzipped, json marshaled Swagger object var swaggerSpec = []string{ - "H4sIAAAAAAAC/+xbfXPTRhr/Khpd/2jv7DgJ4S0dZgopLWlTyJH22rngOhtpHS9Yklmt8kIvM5FMeZmE", - "a6EQmtJOSw8KR9qklDuOEg4+zMZO8i1udleyJWvl2EnKMR3+gVh6dvfZZ3/P++oTVbOMkmVCk9hq7ydq", - "CWBgQAIx/9Vn4/z71ilosh86tDWMSgRZptqrvmk5o0WYtp1RAxGlb+j4WwphlHTGG8loNs6PKNR9St1v", - "qbusjAweG3pfyYxDjPJTaWgAVMxolplH2GBkc2sLj9dvzlH3OnW/VUZyuSOWTdJaAWqnkJkTk2mWdQpB", - "hbpLlZlb1LtC3W+ou8SXeLox41Jvls54J0w1pSLGXgECHWI1pZrAgGqv+lGasZgWm0mpGJ52EIa62kuw", - "A1OqrRWgAdguDWQOQHOMFNTerj0plUyV2HCbYGSOqdPTKbXPsYllQNyvx4USvFP63wwYOe1APFXnQ/Mp", - "ckhXw8vCSWCUij5F7ij864R+lAwcPvaRM35IlbFx2NSROXYI5i0M44xU585Xlr6i7jXqzTGZ9r/JJEe9", - "e7Q8T71H1LtDvWVavrC6crty8ZL0FfWuVD6brzy9nrATyBnIjQoOwnuJMzuADETiXIr5V1ceVq/9nLBK", - "kY8Mz67DPHCKRO3t6kypBphEhmOwH+wXMv1fNYkhk8AxiDkXQwRggsyxg3kC8bZktrGwNZnZPgc5wFlo", - "LrQhZ7TGngxs4ffJgLNDVMmgs53RVkD3F67Ah5n+Hoc6wlCTnGr11tfrd59Uns5Rd3HD/c/anRXqLlBv", - "du3Go8ryU1q+TL1fE5jFwaRhLl/BMK/2qn/I1C1VRry1M8dhERA0DgNuBgEpqNOMVQztkmXakNuxQ0A/", - "Dk870JawewjoCvZfTqfUtyw8inRdZvLqr6ZTar9JIDZBcQjicYgPY2xJIBUQKTanUiAnm06pRy3yluWY", - "klM9ahElz19Np9QPTOCQgoXRGSghjbxle/alwigP6gaSbGFt6eba5+fWZz6l7lK1/Gnlu/tqSi1hqwQx", - "QUJWSLISweDPAmJ1yBAMBqUgqdvWYTZZtkZjjZ5kZ8tsKIaAwH5z3EIaPO6flEx8nEDRGDlDOYY2U/0Y", - "y4IuJ2M9mKOBe2Tmut4jR0YPD5zS+yeOOHvRZLHn2Jkzg++NxzeVUktgyoAmyTm4GF+CeSuoKz4Xik+r", - "fHB8ILJkgZCS3ZvJ+HQdbPYS7NAsI9PR0bG5KOubjPIjFbDvZpq4qGQEcP8cH8nVPrKjk9A0p7BlQ/MN", - "/yHbjUx+aFNn2Z4DDCxGTHvY0/Bk7zAWleOMR9ksBIPTuW0jPib+tyHpY9GL5ZAhaNvIMu1klHM38oCW", - "/0m9W7T8oHr/cfXRBVr+ipafUO/R2tX71W9us7Dn09sb5y+vPppZ/+FO7Mh0QLitRAQaIo6LvAaG5Zgk", - "ZzujxCJAcraVi79Unlyj5ZW1u3/fOH+Z+bHPF3lwdY+6Z6tXl6sXr1H37Mb5yxs3L4WF0rV//76Umrew", - "AYhwt3t61Lj3TQUsJKzfZIXurhZX4FZCZidX//t19cLn1eu3qwue8uoHJppUqgvexvwXr0V2smfv/j2d", - "nd1du1tbLaRhzTxUTROTtMDHieIDJaYNdo5Am+S6unf17N6zd9/+zmb2iTFoSpwcdb+g7pISW4y56Pnv", - "N2b+sbryJXUvU++iH/wMign7xXxhhkoot+s9MnFoom45u/eBXeT0IJByhi3d0YhUyQbFu8YtsyG5ox+e", - "tPedenf0nQ8Nx9FkM9sEEMduQaI8VPuVls9xjXomwg9oskBxmKc/RUiE1ShxY5tSrRI0mV2t88SftKT7", - "/gOAMZhivwvAzhnyIP3Hm4y1QNOpO0ddjyUy7mx9qVHLKkJgytfynVuy10s28q1ZDN2R8f31zNq/vbWv", - "zlJ3MVDcc3HF7WzHLpSADCA1s8Asovtsm2tg5thMdnBNFlqa3Vj8sv0ltm189nXu6enp2bP3uRqfnQ2O", - "mhuf9cXZ6n2veuPR79Hm1DeXaGh0DPIksC1MaDyQc0zNKhahRtBokdmgcUuEzHXOgnEvmOkZtGwSwCsx", - "t6qFeEILaPkey5O9u1xKF9oNPNuLAbcS6zWPwKHPB182myCTWlKTIJJA5VqTSLhctEkAvXUtaL7raMUq", - "NKlMAtJ8PLb6SCYzolD3TuXOLIsxvYsizKTe97R8nZbvVc6x/LShXlDXiIwBjVGI7QIqcUUiLMlWe9WP", - "M6/+bfjjTLbjj6+9IhPIEE+4Do/7RgroOmIMgeJgSOaiHhjl921oQow0RUygQDYDS/OKFtBViRRCZZI+", - "UeUMwUG+bB4U7di6Yh6F407x66W1WkUjVEhQpw1VMHd1b3a6YlS2+SZ2gPskrhNUHtkaUQJ9qznE4IEB", - "JoMddu/eHYHA8MdvnDhhZ//0xjDqzw7bQ9lhrS87TN7PnjjRMQwO8p/sz5PvZIdLg9lXWlT6TQTEK4vh", - "DG+rYuIFQiWoXbUqLZMJqIjOQL0msxj4cahet4WKmtwS1iaNy4c5S6g5GJGpITa1b894Ff+gI6wCLwGK", - "R/UaYEP13xZBfH1LoITehVOiyIfMvJWQACBTOTjYz4Yhwq1G9Ok4xLag7uzo6uhkEmKOGZSQ2qvu4o84", - "rgqc7QwIymljkEuQHQoICrMs3ef1NlttqDt2d3aKXdciI1AqFZHGh2ZO2hafs17qrAXjzU5IlPZi7p7J", - "IyqHY+8yqp7OXUkz1njNREqbuwXXzUfI6p/80B3DAHiqodYoyhbh8jgBYzaDkthNlg3lbaNmQu5j72Mi", - "7pEVUZU+KwgjRSuIEw9Bku4TeIs7paZtJ+9K0KNaEK0mFgnCtFYA5hgyxwL7xrwadZfpjOtn3I1tK2Uk", - "3IkaUQRzLA8PN7So94CWr7LYzL1O3R+i/S1Z/V7C+wH2T5rb9/Q4KDrwdYXp8oHM68oQU0z4ujIEDDiE", - "CDwwACYlhnA6ep68x0fLF3jIuEzLD9au3o3JJ3yyTMv9gw3lK4mHG9CkIk3IYTkQ6ySZUENuOpUc28n6", - "DUFI2LQRkxSOSpti/rvk6bLbNBGtpXvJxqAF1Q51S/iQrs2HRFsRWzE6PUKPm4+otU22baV818TxFXZK", - "w1l2QiHQ18rlEttVk3eWZ8FEK8SRPcgebx3btQa8AA4/lUOWPrVjmJHlcdPxDtpLmO6Eb9wK6qo3/lWd", - "/zkRdZYtMafhQ32JudYw1y0A9Juv1udXCl/CPAJzUQ+Rw5yFEKhe6k7GfFAPfzEh31AakiJ+BzEo7bG/", - "BGQzQNbaJhI8BtgScCwim2Q0v9OU9pNUu2l829AQ3m6cuwl1w+2hFkaIS1qtTB25R9XCgMhdtXhAHan7", - "Jwfr0TZD22F7a23BhFtbxLGjmVcr3cPnGfo3u3Hw4pUGtnDlQRZ/DyCbhPXR9xFN1bA/oHmpfpv3rtrU", - "hvZaXJtqbKhjuFRv0M7/Wn1wLel2rb+QZeYMSAqWLlfaAsBjMAccYhmAIA0Ui2wOG5p6LggznrP6Bs7l", - "xVPV2iFsRSvDF76bR26hYnrbmim7lfpbxXGSrsh0tDpOsAPjcV33b8FBtO0gwc5BTYOlrcd2OwQh3syh", - "5ZvMspQXeZfvIi3/yMyNu7T2/eP1e5dCb69szLirz27KC4myLwiaWft4Jy6OroYbsg38uIv+TThvjnqz", - "tLzMHBezj7do+btwNTSpxNjOFweyft3mZofASZIpEKMYBVGsnCuxLJHqeB/QCjDdZ5kEW5IuEy3Pc4/9", - "A/UeUu/s6qOf1h4uUHdx9dk3lZ++pO416t4QjdyEIrVppW0iPhaIl5qfX/KxUzFMAJEAMXUohGAdXLDw", - "rqzfvLt263Ec1qnWrGIdvq2Ztcn0xMREOm9hI+3gIjQ1SxfXk9q2Mg0t7BbM3S7hrBo+V4BQOUYKvCD1", - "YoAupQ5YQlqJHzEsza0+Prfppwzb+lYh1VZXys80RxTq3lXabFkl0Pu9KXd5deU23+qSktzcej79K3+X", - "B/z/RRer3r86QkjpmFmcaqOT9bsyL96V6sML67+sCOnS8k/U+1lkUiwe45iVu88JOFqwrFNBopQObl0m", - "W6APxQg/Ph0E/tcHTRyof0mGunPCkQv4rd+7tH73Se0uqvQbPTEyPYTGTEAcrrLJXlPqJnc+3gvfGWop", - "0JN3owNe/q9xmH+WTOGDj1U6GAIUjp0FnpGLT9iuU/dy5bN56s2GcOQPZ1Carj1t3CsDm3/Fwj9VDj9J", - "OSao/UaIQxdWky7ORehreVOMPNhshDzYQpycJS1RWp7GxAn5fYWGHYoLGdnp/wUAAP//vDK9Jk07AAA=", + "H4sIAAAAAAAC/+xbfVPbxrr/Khrd/tHea2Mg5I1OZprQtKGlCTe0t51LXLOW1liJpXVWK17SwwyS80IG", + "ctqkCSlNOw09eeGEFprmnBwacsKHWWzgW5zZlWRL1srYQNNM238SLD27++yzv+d99ZmsIL2IDGgQU+7+", + "TC4CDHRIIOa/ekyc+xCdgwb7oUJTwVqRaMiQu+W3kZUtwKRpZXWNSD0Dp9+RCKOkk85QSjFxbkii9gtq", + "f0ftZWmo/9TAh1JqBGItN56EOtAKKQUZOQ3rjGxmY+7Z5vwMtW9T+ztpKJM5gUySVPJQOacZGXcyBaFz", + "GpSovVSevEedG9T+ltpLfIkXW5M2dabppHPGkBOyxtjLQ6BCLCdkA+hQ7pY/STIWk+5mEjKG5y0NQ1Xu", + "JtiCCdlU8lAHbJe6ZvRBY5jk5e6OAwmZjBfZcJNgzRiWJyYSco9lEqRD3KtGheK/k3rf9hk5b0E8XuND", + "8SgymioHl4VjQC8WPIrMSfj/o+pJ0nf81CfWyDFZxMZxQ9WM4WMwhzCMMlKZuVJe+prat6gzw2Ta+zaT", + "HHUe0dIsdVao85A6y7Q0tb56v3z1mvAVdW6UP58tv7gdsxPIGchkXQ6Ce4ky26fpGoly6c6/vvq0cuun", + "mFUKfGRwdhXmgFUgcndHe0LWwZimWzr7wX5phverKjHNIHAYYs7FAAGYaMbw0RyBeFcy25rbmcxMj4MM", + "4Cw0FtqAla2yJwJb8H084MwAVTzoTCvbDOj+jyvwcaa/p6GqYagITrVy75vNheflFzPUXtyy/7XxcJXa", + "c9SZ3rizUl5+QUvXqfNLDLPYnzTI5WsY5uRu+b9SNUuVct+aqdOwAIg2An1u+gHJyxOMVQzNIjJMyO3Y", + "MaCehuctaArYPQZUCXsvJxLyOwhnNVUVmbzaq4mE3GsQiA1QGIB4BOLjGCMBpHwiyeRUEuRkEwn5JCLv", + "IMsQnOpJRKQcfzWRkD8ygEXyCGsXoIA09Jbt2ZMKozyq6ppgCxtL8xtfXN6cvETtpUrpUvnuYzkhFzEq", + "Qkw0V1aaYCWCwf+6EKtBhmDQLwRJzbYOssnSVRqUPcvOltlQDAGBvcYI0hR42jspkfg4gaQwcoZyDE2m", + "+hGWXbqMiHV/jjruNSPT8QE5kT3ed07tHT1hHdTGCl2nLlzo/2AkuqmEXATjOjRIxsKF6BLMW0FV8riQ", + "PFrpo9N9oSXzhBTN7lTKo2tjsxdhm4L0VFtb2/airG0yzI9QwJ6baeCi4hHA/XN0JFf70I7OQsMYx8iE", + "xlveQ7Ybkfy0bZ1law7QtxgR7WFPg5O9x1iUTjMeRbMQDM5ndo34iPjfhaSHRS/IIgPQNDVkmPEo527k", + "CS39nTr3aOlJ5fGzysoULX1NS8+ps7Jx83Hl2/ss7Ll0f+vK9fWVyc0HDyNHpgLCbaVGoO7GcaHXQEeW", + "QTKmlSWIAMHZlq/+XH5+i5ZWNxb+unXlOvNjXyzy4OoRtS9Wbi5Xrt6i9sWtK9e35q8FhdJx+PChhJxD", + "WAfEdbcHuuSo9034LMSs32CFzo4mV+BWQmQn1//9TWXqi8rt+5U5R3r9I0Mbkypzztbsl2+EdnLg4OED", + "7e2dHfubWy2gYY08VFUT47TAw4nkASWiDWaGQJNkOjr3de0/cPDQ4fZG9okxaAicHLW/pPaSFFmMuejZ", + "77cm/7a++hW1r1Pnqhf89LsT9rrzBRkqapl9H5DRY6M1y9l5COwj5/uBkDOMVEshQiXrd9/Vb5kNyZz8", + "+Kx56Nz72fc+1i1LEc1sEkAsswmJ8lDtF1q6zDVqzQ0/oMECxUGe/hQgca1GkRvbhIyK0GB2tcYTf9KU", + "7nsPAMZgnP3OAzOji4P0H+YZa76mU3uG2g5LZOzp2lJZhAoQGOK1POcW7/XijXxzFkO1RHx/M7nxT2fj", + "64vUXvQV93JUcdtbsQtFIAJI1Swwi2iv7XINzBybwQ6uwUJL01uLX7W+xK6Nz6H2A11dXQcOvlTjs7fB", + "UWPjs7k4XXnsVO6s/B5tTm1zsYZGxSBHfNvChMYDOctQUKEAFaJlC8wGjSA3ZK5x5o97xUxPPzKJD6/Y", + "3Koa4rlaQEuPWJ7sLHApTbUaeLYWA+4k1mscgUOPD75sOkYm1aQmRiS+yjUnkWC5aJsAeuda0HjX4YpV", + "YFKRBIT5eGT1oVRqSKL2w/LDaRZjOlfdMJM639PSbVp6VL7M8tO6ekFNI1I61LMQm3mtyBWJsCRb7pY/", + "Tb3+l8FPU+m2/37jNZFABnjCdXzEM1JAVTXGECj0B2Tu1gPD/L4LDYg1RXInkCCbgaV5BQRUWSCFQJmk", + "x61yBuAgXjYHCmZkXXceieNO8uql1VpFPVSIX6cNVDD3dW53uu6odONN7AH3cVzHqLxmKkTy9a3qEP0H", + "Ohjzd9i5f38IAoOfvnXmjJn+n7cGtd70oDmQHlR60oPkw/SZM22D4Cj/yf48+156sNiffq1Jpd9GQLyy", + "GMzwdiomXiCU/NpVs9IymIAK2gWoVmUWAT8O1Ot2UFETW8LqpFH5sCGakUMx8blmSEf7exmjGuFKHX46", + "ArHpUre3dbS1sw0wvwmKmtwt7+OP+LHnuVhSwK92DUO+QSYz4NdNWTbOy2GmXFcW7Gxv50YWVQMXUCwW", + "NIUPTZ01EZ+zVomsxsqNBOhW3iLemMkjLIdT7zOqrvZ9cTNWeU2FKo/7Xa4bjxCVJ3mJ0NJ1gMfrSoFu", + "VSFYvSZg2GQn7e4mzYbyrk4jIfew9xERd4lqnFIP8qM8t1PDiQcgSfbwPo/AZzTsCjk3/BbSnNsJYoEa", + "TCp5YAxrxrBvfpjTofYynbS9hLi+qyQNBRtFQ5LLHEuTg/0m6jyhpZssdLJvU/tBuP0kKq8LeD/C/kly", + "85scAQULvikxVTuSelMagIqF4ZvSANDhgEbgkT4wJrBTE+Hz5C04WpriEd0yLT3ZuLkQkU/wZC2S9w82", + "kE54hxsWvull0lVxeY299bX5rbvP3eRMKmI0Ni4Bi+Qlas+U1y5tPrDdeLcOJv5qiVC3cVAM6RpJKtB5", + "m0jEB3GixoIf+zXsuMTFncLul/cufrr0Lo1Nc3ldvFlpwkgE2iJ8SMf2Q8I9h52Yry7XIjQeUe2P7J29", + "qxW+BWauKtA0z2eJkt9DJaCTTp1ZoY5TXru0dXcqoh/9bO2da0i1X+/Cj5/tMaSO7xnyRGnfRLTh9ifY", + "99RXB7FbufOPyuxPsdhFJvmtoBuAxp/IbQ65nS4Mf/XVerzy5B9NWdwqi1hZWOSj1Qrov7Hm+KX8V1Nx", + "6qpaQr3ZQyQLrwf8wWFdbekIUO2DxwV1QTNJSvG6YEkPwWbDzK2uWb3b0Hwb6rqbTU2McC+QNTN16I5X", + "EwNC9+iiOUCoJxGfX4RbIC1nGs21LGNulBHLDKedzXQ2X2a20ug2xKtXF9nBdQxRRtGnmSSoj56naaiG", + "vT7Nn+q3fV+tRW1orf22rcYGuplLtebx7C+VJ7fibv56CyEjo0OSR6pYafMAD8MMsAjSAdEUUCiwOUxo", + "qBk/WHnJ6us7l1dPVauHsBOtDF5GD8Z/0dAsUOhvWTNFN2Z/rUBN0LGZCFfuCbZgNHDr/DU4CLdEBNg5", + "qiiwuPPgbY8gxBtNtDTPLEtpkXcgr9LSD8zc2Esb3z/bfHQt8PbG1qS9vjYvrqKKvm5oZO2jXcIouupu", + "79bxYy96t/ScGepM09Iyc1zMPt6jpbvBUnBcVbSVryFEvcTtzQ6BYySVJ3ohDKJILVtgWUKtgR6g5GGy", + "BxkEI0EHjJZmucd+QJ2n1Lm4vvLjxtM5ai+ur31b/vErat+i9h23yRxToTdQ0iTuhwzROvvLyy72Kobx", + "IeIjpgaFAKz9yx/Ojc35hY17z6KwTjRnFWvwbc6sjSVHR0eTOYT1pIUL0FCQ6l6datnK1LXXmzB3+1xn", + "VfcpBYTSKZLndatXA3QJuQ+50or9wGJpZv3Z5W0/s9jVdxSJllpyXqY5JFF7QWqxXxdD7zXm7OX11ft8", + "q0tSfGfv5TTvvF0e8f53W3i15t0JQoqnjMJ4C22835V5cW5Unk5t/rzqSpeWfqTOT24mxeIxjlmx+xyF", + "2TxC5/xEKenfCI23QB+7I7z4tB94X0Y0cKDeBR5qz7iO3IXf5qNrmwvPq/dkhd8PuiOTA9qwAYjFVTbe", + "awrd5N7He8H7TE0FeuJWvM/LbxqHeWfJFN7/kKaNIUDi2JnjGbn7ed1tal8vfz5LnekAjrzhDEoT1af1", + "e2Vg8+6XeKfK4Scox/gV5BBx4DJt3KW+EH01b4qQ+5sNkftbiJKzpCVMy9OYKCG/rFG3Q/c2SnriPwEA", + "AP///p1g3+k7AAA=", } // GetSwagger returns the content of the embedded swagger specification file