From 164ce6a3d7fa8ae5275c94302952cf318e3b3113 Mon Sep 17 00:00:00 2001 From: Elias Schneider Date: Fri, 24 Jan 2025 12:01:27 +0100 Subject: [PATCH] fix: add `__HOST` prefix to cookies (#175) --- backend/internal/controller/user_controller.go | 12 ++++++++++-- .../internal/controller/webauthn_controller.go | 18 +++++++++++------- backend/internal/middleware/jwt_auth.go | 3 ++- backend/internal/utils/cookie/add_cookie.go | 13 +++++++++++++ backend/internal/utils/cookie/cookie_names.go | 16 ++++++++++++++++ backend/internal/utils/cookie_util.go | 12 ------------ frontend/src/hooks.server.ts | 3 ++- frontend/src/lib/constants.ts | 2 ++ frontend/src/routes/+layout.server.ts | 5 +++-- frontend/src/routes/authorize/+page.server.ts | 3 ++- .../routes/settings/account/+page.server.ts | 5 +++-- .../application-configuration/+page.server.ts | 3 ++- .../admin/oidc-clients/+page.server.ts | 3 ++- .../admin/oidc-clients/[id]/+page.server.ts | 3 ++- .../settings/admin/user-groups/+page.server.ts | 3 ++- .../admin/user-groups/[id]/+page.server.ts | 3 ++- .../settings/admin/users/+page.server.ts | 3 ++- .../settings/admin/users/[id]/+page.server.ts | 3 ++- .../routes/settings/audit-log/+page.server.ts | 3 ++- reverse-proxy/Caddyfile | 5 ----- reverse-proxy/Caddyfile.trust-proxy | 5 ----- 21 files changed, 80 insertions(+), 46 deletions(-) create mode 100644 backend/internal/utils/cookie/add_cookie.go create mode 100644 backend/internal/utils/cookie/cookie_names.go delete mode 100644 backend/internal/utils/cookie_util.go create mode 100644 frontend/src/lib/constants.ts diff --git a/backend/internal/controller/user_controller.go b/backend/internal/controller/user_controller.go index f970130..c280de9 100644 --- a/backend/internal/controller/user_controller.go +++ b/backend/internal/controller/user_controller.go @@ -1,7 +1,9 @@ package controller import ( + "github.com/stonith404/pocket-id/backend/internal/utils/cookie" "net/http" + "strconv" "time" "github.com/gin-gonic/gin" @@ -184,7 +186,10 @@ func (uc *UserController) exchangeOneTimeAccessTokenHandler(c *gin.Context) { return } - utils.AddAccessTokenCookie(c, uc.appConfigService.DbConfig.SessionDuration.Value, token) + sessionDurationInMinutesParsed, _ := strconv.Atoi(uc.appConfigService.DbConfig.SessionDuration.Value) + maxAge := sessionDurationInMinutesParsed * 60 + cookie.AddAccessTokenCookie(c, maxAge, token) + c.JSON(http.StatusOK, userDto) } @@ -201,7 +206,10 @@ func (uc *UserController) getSetupAccessTokenHandler(c *gin.Context) { return } - utils.AddAccessTokenCookie(c, uc.appConfigService.DbConfig.SessionDuration.Value, token) + sessionDurationInMinutesParsed, _ := strconv.Atoi(uc.appConfigService.DbConfig.SessionDuration.Value) + maxAge := sessionDurationInMinutesParsed * 60 + cookie.AddAccessTokenCookie(c, maxAge, token) + c.JSON(http.StatusOK, userDto) } diff --git a/backend/internal/controller/webauthn_controller.go b/backend/internal/controller/webauthn_controller.go index fbf5ce5..1bca716 100644 --- a/backend/internal/controller/webauthn_controller.go +++ b/backend/internal/controller/webauthn_controller.go @@ -5,8 +5,9 @@ import ( "github.com/stonith404/pocket-id/backend/internal/common" "github.com/stonith404/pocket-id/backend/internal/dto" "github.com/stonith404/pocket-id/backend/internal/middleware" - "github.com/stonith404/pocket-id/backend/internal/utils" + "github.com/stonith404/pocket-id/backend/internal/utils/cookie" "net/http" + "strconv" "time" "github.com/gin-gonic/gin" @@ -42,12 +43,12 @@ func (wc *WebauthnController) beginRegistrationHandler(c *gin.Context) { return } - c.SetCookie("session_id", options.SessionID, int(options.Timeout.Seconds()), "/", "", true, true) + cookie.AddSessionIdCookie(c, int(options.Timeout.Seconds()), options.SessionID) c.JSON(http.StatusOK, options.Response) } func (wc *WebauthnController) verifyRegistrationHandler(c *gin.Context) { - sessionID, err := c.Cookie("session_id") + sessionID, err := c.Cookie(cookie.SessionIdCookieName) if err != nil { c.Error(&common.MissingSessionIdError{}) return @@ -76,12 +77,12 @@ func (wc *WebauthnController) beginLoginHandler(c *gin.Context) { return } - c.SetCookie("session_id", options.SessionID, int(options.Timeout.Seconds()), "/", "", true, true) + cookie.AddSessionIdCookie(c, int(options.Timeout.Seconds()), options.SessionID) c.JSON(http.StatusOK, options.Response) } func (wc *WebauthnController) verifyLoginHandler(c *gin.Context) { - sessionID, err := c.Cookie("session_id") + sessionID, err := c.Cookie(cookie.SessionIdCookieName) if err != nil { c.Error(&common.MissingSessionIdError{}) return @@ -105,7 +106,10 @@ func (wc *WebauthnController) verifyLoginHandler(c *gin.Context) { return } - utils.AddAccessTokenCookie(c, wc.appConfigService.DbConfig.SessionDuration.Value, token) + sessionDurationInMinutesParsed, _ := strconv.Atoi(wc.appConfigService.DbConfig.SessionDuration.Value) + maxAge := sessionDurationInMinutesParsed * 60 + cookie.AddAccessTokenCookie(c, maxAge, token) + c.JSON(http.StatusOK, userDto) } @@ -165,6 +169,6 @@ func (wc *WebauthnController) updateCredentialHandler(c *gin.Context) { } func (wc *WebauthnController) logoutHandler(c *gin.Context) { - utils.AddAccessTokenCookie(c, "0", "") + cookie.AddAccessTokenCookie(c, 0, "") c.Status(http.StatusNoContent) } diff --git a/backend/internal/middleware/jwt_auth.go b/backend/internal/middleware/jwt_auth.go index 1033970..1e1f970 100644 --- a/backend/internal/middleware/jwt_auth.go +++ b/backend/internal/middleware/jwt_auth.go @@ -4,6 +4,7 @@ import ( "github.com/gin-gonic/gin" "github.com/stonith404/pocket-id/backend/internal/common" "github.com/stonith404/pocket-id/backend/internal/service" + "github.com/stonith404/pocket-id/backend/internal/utils/cookie" "strings" ) @@ -19,7 +20,7 @@ func NewJwtAuthMiddleware(jwtService *service.JwtService, ignoreUnauthenticated func (m *JwtAuthMiddleware) Add(adminOnly bool) gin.HandlerFunc { return func(c *gin.Context) { // Extract the token from the cookie or the Authorization header - token, err := c.Cookie("access_token") + token, err := c.Cookie(cookie.AccessTokenCookieName) if err != nil { authorizationHeaderSplitted := strings.Split(c.GetHeader("Authorization"), " ") if len(authorizationHeaderSplitted) == 2 { diff --git a/backend/internal/utils/cookie/add_cookie.go b/backend/internal/utils/cookie/add_cookie.go new file mode 100644 index 0000000..b4591e1 --- /dev/null +++ b/backend/internal/utils/cookie/add_cookie.go @@ -0,0 +1,13 @@ +package cookie + +import ( + "github.com/gin-gonic/gin" +) + +func AddAccessTokenCookie(c *gin.Context, maxAgeInSeconds int, token string) { + c.SetCookie(AccessTokenCookieName, token, maxAgeInSeconds, "/", "", true, true) +} + +func AddSessionIdCookie(c *gin.Context, maxAgeInSeconds int, sessionID string) { + c.SetCookie(SessionIdCookieName, sessionID, maxAgeInSeconds, "/", "", true, true) +} diff --git a/backend/internal/utils/cookie/cookie_names.go b/backend/internal/utils/cookie/cookie_names.go new file mode 100644 index 0000000..a012162 --- /dev/null +++ b/backend/internal/utils/cookie/cookie_names.go @@ -0,0 +1,16 @@ +package cookie + +import ( + "github.com/stonith404/pocket-id/backend/internal/common" + "strings" +) + +var AccessTokenCookieName = "__Host-access_token" +var SessionIdCookieName = "__Host-session" + +func init() { + if strings.HasPrefix(common.EnvConfig.AppURL, "http://") { + AccessTokenCookieName = "access_token" + SessionIdCookieName = "session" + } +} diff --git a/backend/internal/utils/cookie_util.go b/backend/internal/utils/cookie_util.go deleted file mode 100644 index b045a98..0000000 --- a/backend/internal/utils/cookie_util.go +++ /dev/null @@ -1,12 +0,0 @@ -package utils - -import ( - "github.com/gin-gonic/gin" - "strconv" -) - -func AddAccessTokenCookie(c *gin.Context, sessionDurationInMinutes string, token string) { - sessionDurationInMinutesParsed, _ := strconv.Atoi(sessionDurationInMinutes) - maxAge := sessionDurationInMinutesParsed * 60 - c.SetCookie("access_token", token, maxAge, "/", "", true, true) -} diff --git a/frontend/src/hooks.server.ts b/frontend/src/hooks.server.ts index 07795fd..bfe01ab 100644 --- a/frontend/src/hooks.server.ts +++ b/frontend/src/hooks.server.ts @@ -1,4 +1,5 @@ import { env } from '$env/dynamic/private'; +import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants'; import type { Handle, HandleServerError } from '@sveltejs/kit'; import { AxiosError } from 'axios'; import jwt from 'jsonwebtoken'; @@ -9,7 +10,7 @@ import jwt from 'jsonwebtoken'; process.env.INTERNAL_BACKEND_URL = env.INTERNAL_BACKEND_URL ?? 'http://localhost:8080'; export const handle: Handle = async ({ event, resolve }) => { - const accessToken = event.cookies.get('access_token'); + const accessToken = event.cookies.get(ACCESS_TOKEN_COOKIE_NAME); let isSignedIn: boolean = false; let isAdmin: boolean = false; diff --git a/frontend/src/lib/constants.ts b/frontend/src/lib/constants.ts new file mode 100644 index 0000000..5e019b9 --- /dev/null +++ b/frontend/src/lib/constants.ts @@ -0,0 +1,2 @@ +export const HTTPS_ENABLED = process.env.PUBLIC_APP_URL?.startsWith('https://') ?? false; +export const ACCESS_TOKEN_COOKIE_NAME = HTTPS_ENABLED ? '__Host-access_token' : 'access_token'; diff --git a/frontend/src/routes/+layout.server.ts b/frontend/src/routes/+layout.server.ts index 7296fa4..c39ba63 100644 --- a/frontend/src/routes/+layout.server.ts +++ b/frontend/src/routes/+layout.server.ts @@ -1,10 +1,11 @@ +import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants'; import AppConfigService from '$lib/services/app-config-service'; import UserService from '$lib/services/user-service'; import type { LayoutServerLoad } from './$types'; export const load: LayoutServerLoad = async ({ cookies }) => { - const userService = new UserService(cookies.get('access_token')); - const appConfigService = new AppConfigService(cookies.get('access_token')); + const userService = new UserService(cookies.get(ACCESS_TOKEN_COOKIE_NAME)); + const appConfigService = new AppConfigService(cookies.get(ACCESS_TOKEN_COOKIE_NAME)); const user = await userService .getCurrent() diff --git a/frontend/src/routes/authorize/+page.server.ts b/frontend/src/routes/authorize/+page.server.ts index ed81215..191595e 100644 --- a/frontend/src/routes/authorize/+page.server.ts +++ b/frontend/src/routes/authorize/+page.server.ts @@ -1,9 +1,10 @@ +import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants'; import OidcService from '$lib/services/oidc-service'; import type { PageServerLoad } from './$types'; export const load: PageServerLoad = async ({ url, cookies }) => { const clientId = url.searchParams.get('client_id'); - const oidcService = new OidcService(cookies.get('access_token')); + const oidcService = new OidcService(cookies.get(ACCESS_TOKEN_COOKIE_NAME)); const client = await oidcService.getClient(clientId!); diff --git a/frontend/src/routes/settings/account/+page.server.ts b/frontend/src/routes/settings/account/+page.server.ts index 18064e8..b6e0465 100644 --- a/frontend/src/routes/settings/account/+page.server.ts +++ b/frontend/src/routes/settings/account/+page.server.ts @@ -1,10 +1,11 @@ +import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants'; import UserService from '$lib/services/user-service'; import WebAuthnService from '$lib/services/webauthn-service'; import type { PageServerLoad } from './$types'; export const load: PageServerLoad = async ({ cookies }) => { - const webauthnService = new WebAuthnService(cookies.get('access_token')); - const userService = new UserService(cookies.get('access_token')); + const webauthnService = new WebAuthnService(cookies.get(ACCESS_TOKEN_COOKIE_NAME)); + const userService = new UserService(cookies.get(ACCESS_TOKEN_COOKIE_NAME)); const account = await userService.getCurrent(); const passkeys = await webauthnService.listCredentials(); return { diff --git a/frontend/src/routes/settings/admin/application-configuration/+page.server.ts b/frontend/src/routes/settings/admin/application-configuration/+page.server.ts index 566b010..0300057 100644 --- a/frontend/src/routes/settings/admin/application-configuration/+page.server.ts +++ b/frontend/src/routes/settings/admin/application-configuration/+page.server.ts @@ -1,8 +1,9 @@ +import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants'; import AppConfigService from '$lib/services/app-config-service'; import type { PageServerLoad } from './$types'; export const load: PageServerLoad = async ({ cookies }) => { - const appConfigService = new AppConfigService(cookies.get('access_token')); + const appConfigService = new AppConfigService(cookies.get(ACCESS_TOKEN_COOKIE_NAME)); const appConfig = await appConfigService.list(true); return { appConfig }; }; diff --git a/frontend/src/routes/settings/admin/oidc-clients/+page.server.ts b/frontend/src/routes/settings/admin/oidc-clients/+page.server.ts index 3b253ca..20bf975 100644 --- a/frontend/src/routes/settings/admin/oidc-clients/+page.server.ts +++ b/frontend/src/routes/settings/admin/oidc-clients/+page.server.ts @@ -1,8 +1,9 @@ +import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants'; import OIDCService from '$lib/services/oidc-service'; import type { PageServerLoad } from './$types'; export const load: PageServerLoad = async ({ cookies }) => { - const oidcService = new OIDCService(cookies.get('access_token')); + const oidcService = new OIDCService(cookies.get(ACCESS_TOKEN_COOKIE_NAME)); const clients = await oidcService.listClients(); return clients; }; diff --git a/frontend/src/routes/settings/admin/oidc-clients/[id]/+page.server.ts b/frontend/src/routes/settings/admin/oidc-clients/[id]/+page.server.ts index 6d2e764..065ebfc 100644 --- a/frontend/src/routes/settings/admin/oidc-clients/[id]/+page.server.ts +++ b/frontend/src/routes/settings/admin/oidc-clients/[id]/+page.server.ts @@ -1,7 +1,8 @@ +import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants'; import OidcService from '$lib/services/oidc-service'; import type { PageServerLoad } from './$types'; export const load: PageServerLoad = async ({ params, cookies }) => { - const oidcService = new OidcService(cookies.get('access_token')); + const oidcService = new OidcService(cookies.get(ACCESS_TOKEN_COOKIE_NAME)); return await oidcService.getClient(params.id); }; diff --git a/frontend/src/routes/settings/admin/user-groups/+page.server.ts b/frontend/src/routes/settings/admin/user-groups/+page.server.ts index 810881e..70ebb38 100644 --- a/frontend/src/routes/settings/admin/user-groups/+page.server.ts +++ b/frontend/src/routes/settings/admin/user-groups/+page.server.ts @@ -1,8 +1,9 @@ +import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants'; import UserGroupService from '$lib/services/user-group-service'; import type { PageServerLoad } from './$types'; export const load: PageServerLoad = async ({ cookies }) => { - const userGroupService = new UserGroupService(cookies.get('access_token')); + const userGroupService = new UserGroupService(cookies.get(ACCESS_TOKEN_COOKIE_NAME)); const userGroups = await userGroupService.list(); return userGroups; }; diff --git a/frontend/src/routes/settings/admin/user-groups/[id]/+page.server.ts b/frontend/src/routes/settings/admin/user-groups/[id]/+page.server.ts index 44c4178..00938c9 100644 --- a/frontend/src/routes/settings/admin/user-groups/[id]/+page.server.ts +++ b/frontend/src/routes/settings/admin/user-groups/[id]/+page.server.ts @@ -1,8 +1,9 @@ +import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants'; import UserGroupService from '$lib/services/user-group-service'; import type { PageServerLoad } from './$types'; export const load: PageServerLoad = async ({ params, cookies }) => { - const userGroupService = new UserGroupService(cookies.get('access_token')); + const userGroupService = new UserGroupService(cookies.get(ACCESS_TOKEN_COOKIE_NAME)); const userGroup = await userGroupService.get(params.id); return { userGroup }; diff --git a/frontend/src/routes/settings/admin/users/+page.server.ts b/frontend/src/routes/settings/admin/users/+page.server.ts index 6ad88b9..15d419d 100644 --- a/frontend/src/routes/settings/admin/users/+page.server.ts +++ b/frontend/src/routes/settings/admin/users/+page.server.ts @@ -1,8 +1,9 @@ +import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants'; import UserService from '$lib/services/user-service'; import type { PageServerLoad } from './$types'; export const load: PageServerLoad = async ({ cookies }) => { - const userService = new UserService(cookies.get('access_token')); + const userService = new UserService(cookies.get(ACCESS_TOKEN_COOKIE_NAME)); const users = await userService.list(); return users; }; diff --git a/frontend/src/routes/settings/admin/users/[id]/+page.server.ts b/frontend/src/routes/settings/admin/users/[id]/+page.server.ts index 2333230..576306a 100644 --- a/frontend/src/routes/settings/admin/users/[id]/+page.server.ts +++ b/frontend/src/routes/settings/admin/users/[id]/+page.server.ts @@ -1,8 +1,9 @@ +import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants'; import UserService from '$lib/services/user-service'; import type { PageServerLoad } from './$types'; export const load: PageServerLoad = async ({ params, cookies }) => { - const userService = new UserService(cookies.get('access_token')); + const userService = new UserService(cookies.get(ACCESS_TOKEN_COOKIE_NAME)); const user = await userService.get(params.id); return user; }; diff --git a/frontend/src/routes/settings/audit-log/+page.server.ts b/frontend/src/routes/settings/audit-log/+page.server.ts index 124789c..5ec6ca8 100644 --- a/frontend/src/routes/settings/audit-log/+page.server.ts +++ b/frontend/src/routes/settings/audit-log/+page.server.ts @@ -1,8 +1,9 @@ +import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants'; import AuditLogService from '$lib/services/audit-log-service'; import type { PageServerLoad } from './$types'; export const load: PageServerLoad = async ({ cookies }) => { - const auditLogService = new AuditLogService(cookies.get('access_token')); + const auditLogService = new AuditLogService(cookies.get(ACCESS_TOKEN_COOKIE_NAME)); const auditLogs = await auditLogService.list({ sort: { column: 'createdAt', diff --git a/reverse-proxy/Caddyfile b/reverse-proxy/Caddyfile index b6bd36d..d6a8e07 100644 --- a/reverse-proxy/Caddyfile +++ b/reverse-proxy/Caddyfile @@ -2,9 +2,4 @@ reverse_proxy /api/* http://localhost:{$BACKEND_PORT:8080} reverse_proxy /.well-known/* http://localhost:{$BACKEND_PORT:8080} reverse_proxy /* http://localhost:{$PORT:3000} - - log { - output file /var/log/caddy/access.log - level WARN - } } diff --git a/reverse-proxy/Caddyfile.trust-proxy b/reverse-proxy/Caddyfile.trust-proxy index 3214273..cbb6259 100644 --- a/reverse-proxy/Caddyfile.trust-proxy +++ b/reverse-proxy/Caddyfile.trust-proxy @@ -8,9 +8,4 @@ reverse_proxy /* http://localhost:{$PORT:3000} { trusted_proxies 0.0.0.0/0 } - - log { - output file /var/log/caddy/access.log - level WARN - } }