diff --git a/.env.sample b/.env.sample index 291648f..6e1e6a6 100644 --- a/.env.sample +++ b/.env.sample @@ -9,6 +9,7 @@ DB_DEBUG=false SERVER_URL=https://example.com/api/v1 SERVER_PORT=8081 SERVER_ALLOW_ORIGINS=http://localhost:3000 +SERVER_CSRF_ENABLED=false # for testing purposes API_LOGIN_URL=http://localhost:3000/ API_APPROVE_URL=http://localhost:3000/approve diff --git a/frontend/src/utils/client.ts b/frontend/src/utils/client.ts index 44a6c7f..fc6effa 100644 --- a/frontend/src/utils/client.ts +++ b/frontend/src/utils/client.ts @@ -1,7 +1,15 @@ import createClient from 'openapi-fetch' import { paths } from './api' +import { fromCookie } from './cookie' export const client = createClient({ + fetch: (input: Request) => { + const csrf = fromCookie('_csrf') + if (csrf) { + input.headers.set('X-CSRF-Token', csrf) + } + return fetch(input) + }, baseUrl: process.env.NEXT_PUBLIC_API_URL, credentials: 'include', }) diff --git a/frontend/src/utils/cookie.ts b/frontend/src/utils/cookie.ts new file mode 100644 index 0000000..51e9472 --- /dev/null +++ b/frontend/src/utils/cookie.ts @@ -0,0 +1,5 @@ +export const fromCookie = (key: string): string | undefined => { + const match = document.cookie.match(new RegExp('(^| )' + key + '=([^;]+)')) + if (match) return match[2] + return +} diff --git a/internal/server/init.go b/internal/server/init.go index a7f8aef..fcae1be 100644 --- a/internal/server/init.go +++ b/internal/server/init.go @@ -7,6 +7,7 @@ import ( "context" "os" "os/signal" + "strings" "time" "github.com/kelseyhightower/envconfig" @@ -20,6 +21,7 @@ var config = &Config{} type Config struct { Port string `required:"true"` AllowOrigins []string `split_words:"true" required:"true"` + CSRFEnabled bool `split_words:"true" default:"true"` } func Init() *echo.Echo { @@ -35,9 +37,20 @@ func Init() *echo.Echo { } e.Use(middleware.Recover()) e.Use(middleware.Secure()) + if config.CSRFEnabled { + e.Use(middleware.CSRFWithConfig(middleware.CSRFConfig{ + Skipper: func(c echo.Context) bool { return strings.HasPrefix(c.Path(), "/oauth") }, + CookiePath: "/", + })) + } e.Use(middleware.CORSWithConfig(middleware.CORSConfig{ - AllowOrigins: config.AllowOrigins, - AllowHeaders: []string{echo.HeaderOrigin, echo.HeaderContentType, echo.HeaderAccept}, + AllowOrigins: config.AllowOrigins, + AllowHeaders: []string{ + echo.HeaderOrigin, + echo.HeaderContentType, + echo.HeaderAccept, + echo.HeaderXCSRFToken, + }, AllowCredentials: true, })) e.Use(myMiddleware.Session())