fix: add __HOST prefix to cookies (#175)

This commit is contained in:
Elias Schneider
2025-01-24 12:01:27 +01:00
committed by GitHub
parent ef1aeb7152
commit 164ce6a3d7
21 changed files with 80 additions and 46 deletions

View File

@@ -1,7 +1,9 @@
package controller package controller
import ( import (
"github.com/stonith404/pocket-id/backend/internal/utils/cookie"
"net/http" "net/http"
"strconv"
"time" "time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@@ -184,7 +186,10 @@ func (uc *UserController) exchangeOneTimeAccessTokenHandler(c *gin.Context) {
return 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) c.JSON(http.StatusOK, userDto)
} }
@@ -201,7 +206,10 @@ func (uc *UserController) getSetupAccessTokenHandler(c *gin.Context) {
return 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) c.JSON(http.StatusOK, userDto)
} }

View File

@@ -5,8 +5,9 @@ import (
"github.com/stonith404/pocket-id/backend/internal/common" "github.com/stonith404/pocket-id/backend/internal/common"
"github.com/stonith404/pocket-id/backend/internal/dto" "github.com/stonith404/pocket-id/backend/internal/dto"
"github.com/stonith404/pocket-id/backend/internal/middleware" "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" "net/http"
"strconv"
"time" "time"
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
@@ -42,12 +43,12 @@ func (wc *WebauthnController) beginRegistrationHandler(c *gin.Context) {
return 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) c.JSON(http.StatusOK, options.Response)
} }
func (wc *WebauthnController) verifyRegistrationHandler(c *gin.Context) { func (wc *WebauthnController) verifyRegistrationHandler(c *gin.Context) {
sessionID, err := c.Cookie("session_id") sessionID, err := c.Cookie(cookie.SessionIdCookieName)
if err != nil { if err != nil {
c.Error(&common.MissingSessionIdError{}) c.Error(&common.MissingSessionIdError{})
return return
@@ -76,12 +77,12 @@ func (wc *WebauthnController) beginLoginHandler(c *gin.Context) {
return 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) c.JSON(http.StatusOK, options.Response)
} }
func (wc *WebauthnController) verifyLoginHandler(c *gin.Context) { func (wc *WebauthnController) verifyLoginHandler(c *gin.Context) {
sessionID, err := c.Cookie("session_id") sessionID, err := c.Cookie(cookie.SessionIdCookieName)
if err != nil { if err != nil {
c.Error(&common.MissingSessionIdError{}) c.Error(&common.MissingSessionIdError{})
return return
@@ -105,7 +106,10 @@ func (wc *WebauthnController) verifyLoginHandler(c *gin.Context) {
return 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) c.JSON(http.StatusOK, userDto)
} }
@@ -165,6 +169,6 @@ func (wc *WebauthnController) updateCredentialHandler(c *gin.Context) {
} }
func (wc *WebauthnController) logoutHandler(c *gin.Context) { func (wc *WebauthnController) logoutHandler(c *gin.Context) {
utils.AddAccessTokenCookie(c, "0", "") cookie.AddAccessTokenCookie(c, 0, "")
c.Status(http.StatusNoContent) c.Status(http.StatusNoContent)
} }

View File

@@ -4,6 +4,7 @@ import (
"github.com/gin-gonic/gin" "github.com/gin-gonic/gin"
"github.com/stonith404/pocket-id/backend/internal/common" "github.com/stonith404/pocket-id/backend/internal/common"
"github.com/stonith404/pocket-id/backend/internal/service" "github.com/stonith404/pocket-id/backend/internal/service"
"github.com/stonith404/pocket-id/backend/internal/utils/cookie"
"strings" "strings"
) )
@@ -19,7 +20,7 @@ func NewJwtAuthMiddleware(jwtService *service.JwtService, ignoreUnauthenticated
func (m *JwtAuthMiddleware) Add(adminOnly bool) gin.HandlerFunc { func (m *JwtAuthMiddleware) Add(adminOnly bool) gin.HandlerFunc {
return func(c *gin.Context) { return func(c *gin.Context) {
// Extract the token from the cookie or the Authorization header // 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 { if err != nil {
authorizationHeaderSplitted := strings.Split(c.GetHeader("Authorization"), " ") authorizationHeaderSplitted := strings.Split(c.GetHeader("Authorization"), " ")
if len(authorizationHeaderSplitted) == 2 { if len(authorizationHeaderSplitted) == 2 {

View File

@@ -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)
}

View File

@@ -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"
}
}

View File

@@ -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)
}

View File

@@ -1,4 +1,5 @@
import { env } from '$env/dynamic/private'; import { env } from '$env/dynamic/private';
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import type { Handle, HandleServerError } from '@sveltejs/kit'; import type { Handle, HandleServerError } from '@sveltejs/kit';
import { AxiosError } from 'axios'; import { AxiosError } from 'axios';
import jwt from 'jsonwebtoken'; import jwt from 'jsonwebtoken';
@@ -9,7 +10,7 @@ import jwt from 'jsonwebtoken';
process.env.INTERNAL_BACKEND_URL = env.INTERNAL_BACKEND_URL ?? 'http://localhost:8080'; process.env.INTERNAL_BACKEND_URL = env.INTERNAL_BACKEND_URL ?? 'http://localhost:8080';
export const handle: Handle = async ({ event, resolve }) => { 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 isSignedIn: boolean = false;
let isAdmin: boolean = false; let isAdmin: boolean = false;

View File

@@ -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';

View File

@@ -1,10 +1,11 @@
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import AppConfigService from '$lib/services/app-config-service'; import AppConfigService from '$lib/services/app-config-service';
import UserService from '$lib/services/user-service'; import UserService from '$lib/services/user-service';
import type { LayoutServerLoad } from './$types'; import type { LayoutServerLoad } from './$types';
export const load: LayoutServerLoad = async ({ cookies }) => { export const load: LayoutServerLoad = async ({ cookies }) => {
const userService = new UserService(cookies.get('access_token')); const userService = new UserService(cookies.get(ACCESS_TOKEN_COOKIE_NAME));
const appConfigService = new AppConfigService(cookies.get('access_token')); const appConfigService = new AppConfigService(cookies.get(ACCESS_TOKEN_COOKIE_NAME));
const user = await userService const user = await userService
.getCurrent() .getCurrent()

View File

@@ -1,9 +1,10 @@
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import OidcService from '$lib/services/oidc-service'; import OidcService from '$lib/services/oidc-service';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ url, cookies }) => { export const load: PageServerLoad = async ({ url, cookies }) => {
const clientId = url.searchParams.get('client_id'); 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!); const client = await oidcService.getClient(clientId!);

View File

@@ -1,10 +1,11 @@
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import UserService from '$lib/services/user-service'; import UserService from '$lib/services/user-service';
import WebAuthnService from '$lib/services/webauthn-service'; import WebAuthnService from '$lib/services/webauthn-service';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ cookies }) => { export const load: PageServerLoad = async ({ cookies }) => {
const webauthnService = new WebAuthnService(cookies.get('access_token')); const webauthnService = new WebAuthnService(cookies.get(ACCESS_TOKEN_COOKIE_NAME));
const userService = new UserService(cookies.get('access_token')); const userService = new UserService(cookies.get(ACCESS_TOKEN_COOKIE_NAME));
const account = await userService.getCurrent(); const account = await userService.getCurrent();
const passkeys = await webauthnService.listCredentials(); const passkeys = await webauthnService.listCredentials();
return { return {

View File

@@ -1,8 +1,9 @@
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import AppConfigService from '$lib/services/app-config-service'; import AppConfigService from '$lib/services/app-config-service';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ cookies }) => { 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); const appConfig = await appConfigService.list(true);
return { appConfig }; return { appConfig };
}; };

View File

@@ -1,8 +1,9 @@
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import OIDCService from '$lib/services/oidc-service'; import OIDCService from '$lib/services/oidc-service';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ cookies }) => { 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(); const clients = await oidcService.listClients();
return clients; return clients;
}; };

View File

@@ -1,7 +1,8 @@
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import OidcService from '$lib/services/oidc-service'; import OidcService from '$lib/services/oidc-service';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params, cookies }) => { 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); return await oidcService.getClient(params.id);
}; };

View File

@@ -1,8 +1,9 @@
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import UserGroupService from '$lib/services/user-group-service'; import UserGroupService from '$lib/services/user-group-service';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ cookies }) => { 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(); const userGroups = await userGroupService.list();
return userGroups; return userGroups;
}; };

View File

@@ -1,8 +1,9 @@
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import UserGroupService from '$lib/services/user-group-service'; import UserGroupService from '$lib/services/user-group-service';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params, cookies }) => { 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); const userGroup = await userGroupService.get(params.id);
return { userGroup }; return { userGroup };

View File

@@ -1,8 +1,9 @@
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import UserService from '$lib/services/user-service'; import UserService from '$lib/services/user-service';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ cookies }) => { 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(); const users = await userService.list();
return users; return users;
}; };

View File

@@ -1,8 +1,9 @@
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import UserService from '$lib/services/user-service'; import UserService from '$lib/services/user-service';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ params, cookies }) => { 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); const user = await userService.get(params.id);
return user; return user;
}; };

View File

@@ -1,8 +1,9 @@
import { ACCESS_TOKEN_COOKIE_NAME } from '$lib/constants';
import AuditLogService from '$lib/services/audit-log-service'; import AuditLogService from '$lib/services/audit-log-service';
import type { PageServerLoad } from './$types'; import type { PageServerLoad } from './$types';
export const load: PageServerLoad = async ({ cookies }) => { 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({ const auditLogs = await auditLogService.list({
sort: { sort: {
column: 'createdAt', column: 'createdAt',

View File

@@ -2,9 +2,4 @@
reverse_proxy /api/* http://localhost:{$BACKEND_PORT:8080} reverse_proxy /api/* http://localhost:{$BACKEND_PORT:8080}
reverse_proxy /.well-known/* http://localhost:{$BACKEND_PORT:8080} reverse_proxy /.well-known/* http://localhost:{$BACKEND_PORT:8080}
reverse_proxy /* http://localhost:{$PORT:3000} reverse_proxy /* http://localhost:{$PORT:3000}
log {
output file /var/log/caddy/access.log
level WARN
}
} }

View File

@@ -8,9 +8,4 @@
reverse_proxy /* http://localhost:{$PORT:3000} { reverse_proxy /* http://localhost:{$PORT:3000} {
trusted_proxies 0.0.0.0/0 trusted_proxies 0.0.0.0/0
} }
log {
output file /var/log/caddy/access.log
level WARN
}
} }