Skip to content

Commit

Permalink
Use __Host- cookie prefix instead of setting Domain
Browse files Browse the repository at this point in the history
  • Loading branch information
evan-goode committed Feb 20, 2025
1 parent 9b19ada commit 96dfb52
Show file tree
Hide file tree
Showing 3 changed files with 33 additions and 35 deletions.
40 changes: 19 additions & 21 deletions front.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,14 @@ Web front end for creating user accounts, changing passwords, skins, player name
*/

const BROWSER_TOKEN_AGE_SEC = 24 * 60 * 60
const OIDC_STATE_COOKIE_NAME = "state"
const COOKIE_PREFIX = "__Host-"
const BROWSER_TOKEN_COOKIE_NAME = COOKIE_PREFIX + "browserToken"
const SUCCESS_MESSAGE_COOKIE_NAME = COOKIE_PREFIX + "successMessage"
const WARNING_MESSAGE_COOKIE_NAME = COOKIE_PREFIX + "warningMessage"
const ERROR_MESSAGE_COOKIE_NAME = COOKIE_PREFIX + "errorMessage"
const OIDC_STATE_COOKIE_NAME = COOKIE_PREFIX + "state"
const ID_TOKEN_COOKIE_NAME = COOKIE_PREFIX + "idToken"
const CHALLENGE_TOKEN_COOKIE_NAME = COOKIE_PREFIX + "challengeToken"

// https://echo.labstack.com/guide/templates/
// https://stackoverflow.com/questions/36617949/how-to-use-base-template-file-for-golang-html-template/69244593#69244593
Expand Down Expand Up @@ -81,31 +88,29 @@ func (app *App) setMessageCookie(c *echo.Context, cookieName string, template st
Value: url.QueryEscape(message),
Path: "/",
SameSite: http.SameSiteLaxMode,
Domain: app.Config.Domain,
HttpOnly: true,
})
}

func (app *App) setSuccessMessage(c *echo.Context, template string, args ...interface{}) {
app.setMessageCookie(c, "successMessage", template, args...)
app.setMessageCookie(c, SUCCESS_MESSAGE_COOKIE_NAME, template, args...)
}

func (app *App) setWarningMessage(c *echo.Context, template string, args ...interface{}) {
app.setMessageCookie(c, "warningMessage", template, args...)
app.setMessageCookie(c, WARNING_MESSAGE_COOKIE_NAME, template, args...)
}

func (app *App) setErrorMessage(c *echo.Context, template string, args ...interface{}) {
app.setMessageCookie(c, "errorMessage", template, args...)
app.setMessageCookie(c, ERROR_MESSAGE_COOKIE_NAME, template, args...)
}

func (app *App) setBrowserToken(c *echo.Context, browserToken string) {
(*c).SetCookie(&http.Cookie{
Name: "browserToken",
Name: BROWSER_TOKEN_COOKIE_NAME,
Value: browserToken,
MaxAge: BROWSER_TOKEN_AGE_SEC,
Path: "/",
SameSite: http.SameSiteLaxMode,
Domain: app.Config.Domain,
HttpOnly: true,
})
}
Expand Down Expand Up @@ -200,7 +205,7 @@ func withBrowserAuthentication(app *App, requireLogin bool, f func(c echo.Contex
return err
}

cookie, err := c.Cookie("browserToken")
cookie, err := c.Cookie(BROWSER_TOKEN_COOKIE_NAME)

var user User
if err != nil || cookie.Value == "" {
Expand Down Expand Up @@ -281,7 +286,6 @@ func FrontRoot(app *App) func(c echo.Context) error {
Value: stateBase64,
Path: "/",
SameSite: http.SameSiteLaxMode,
Domain: app.Config.Domain,
HttpOnly: true,
})

Expand Down Expand Up @@ -379,7 +383,6 @@ func FrontRegistration(app *App) func(c echo.Context) error {
Value: stateBase64,
Path: "/",
SameSite: http.SameSiteLaxMode,
Domain: app.Config.Domain,
HttpOnly: true,
})

Expand Down Expand Up @@ -435,7 +438,7 @@ func FrontCompleteRegistration(app *App) func(c echo.Context) error {
return withBrowserAuthentication(app, false, func(c echo.Context, user *User) error {
inviteCode := c.QueryParam("invite")

cookie, err := c.Cookie("idToken")
cookie, err := c.Cookie(ID_TOKEN_COOKIE_NAME)
if err != nil || cookie.Value == "" {
return NewWebError(returnURL, "Missing ID token cookie")
}
Expand Down Expand Up @@ -579,11 +582,10 @@ func (app *App) oidcSignIn(c echo.Context, state oidcState) error {

// User doesn't already exist, set ID token cookie and complete registration
c.SetCookie(&http.Cookie{
Name: "idToken",
Name: ID_TOKEN_COOKIE_NAME,
Value: encryptedIDToken,
Path: "/",
SameSite: http.SameSiteLaxMode,
Domain: app.Config.Domain,
HttpOnly: true,
})

Expand All @@ -604,7 +606,6 @@ func FrontOIDCCallback(app *App) func(c echo.Context) error {
Value: "",
Path: "/",
SameSite: http.SameSiteLaxMode,
Domain: app.Config.Domain,
HttpOnly: true,
})

Expand Down Expand Up @@ -849,7 +850,6 @@ func FrontUser(app *App) func(c echo.Context) error {
Value: stateBase64,
Path: "/",
SameSite: http.SameSiteLaxMode,
Domain: app.Config.Domain,
HttpOnly: true,
})

Expand Down Expand Up @@ -1165,19 +1165,18 @@ func frontChallenge(app *App, action string) func(c echo.Context) error {
inviteCode := c.QueryParam("inviteCode")

var challengeToken string
cookie, err := c.Cookie("challengeToken")
cookie, err := c.Cookie(CHALLENGE_TOKEN_COOKIE_NAME)
if err != nil || cookie.Value == "" {
challengeToken, err = MakeChallengeToken()
if err != nil {
return err
}
c.SetCookie(&http.Cookie{
Name: "challengeToken",
Name: CHALLENGE_TOKEN_COOKIE_NAME,
Value: challengeToken,
MaxAge: BROWSER_TOKEN_AGE_SEC,
Path: "/",
SameSite: http.SameSiteLaxMode,
Domain: app.Config.Domain,
HttpOnly: true,
})
} else {
Expand Down Expand Up @@ -1285,7 +1284,7 @@ func FrontRegister(app *App) func(c echo.Context) error {
username := playerName
idTokens := []string{}
if useIDToken {
cookie, err := c.Cookie("idToken")
cookie, err := c.Cookie(ID_TOKEN_COOKIE_NAME)
if err != nil || cookie.Value == "" {
return NewWebError(returnURL, "Missing ID token cookie")
}
Expand Down Expand Up @@ -1349,11 +1348,10 @@ func FrontRegister(app *App) func(c echo.Context) error {

if useIDToken {
c.SetCookie(&http.Cookie{
Name: "idToken",
Name: ID_TOKEN_COOKIE_NAME,
Value: "",
Path: "/",
SameSite: http.SameSiteLaxMode,
Domain: app.Config.Domain,
HttpOnly: true,
})
}
Expand Down
26 changes: 13 additions & 13 deletions front_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -82,20 +82,20 @@ func (ts *TestSuite) testPublic(t *testing.T) {
}

func getErrorMessage(rec *httptest.ResponseRecorder) string {
return Unwrap(url.QueryUnescape(getCookie(rec, "errorMessage").Value))
return Unwrap(url.QueryUnescape(getCookie(rec, ERROR_MESSAGE_COOKIE_NAME).Value))
}

func (ts *TestSuite) registrationShouldFail(t *testing.T, rec *httptest.ResponseRecorder, errorMessage string, returnURL string) {
assert.Equal(t, http.StatusSeeOther, rec.Code)
assert.Equal(t, errorMessage, getErrorMessage(rec))
assert.Equal(t, "", getCookie(rec, "browserToken").Value)
assert.Equal(t, "", getCookie(rec, BROWSER_TOKEN_COOKIE_NAME).Value)
assert.Equal(t, returnURL, rec.Header().Get("Location"))
}

func (ts *TestSuite) registrationShouldSucceed(t *testing.T, rec *httptest.ResponseRecorder) {
assert.Equal(t, http.StatusSeeOther, rec.Code)
assert.Equal(t, "", getErrorMessage(rec))
assert.NotEqual(t, "", getCookie(rec, "browserToken").Value)
assert.NotEqual(t, "", getCookie(rec, BROWSER_TOKEN_COOKIE_NAME).Value)
assert.Equal(t, ts.App.FrontEndURL+"/web/user", rec.Header().Get("Location"))
}

Expand Down Expand Up @@ -145,14 +145,14 @@ func (ts *TestSuite) updatePlayerShouldSucceed(t *testing.T, rec *httptest.Respo
func (ts *TestSuite) loginShouldSucceed(t *testing.T, rec *httptest.ResponseRecorder) {
assert.Equal(t, http.StatusSeeOther, rec.Code)
assert.Equal(t, "", getErrorMessage(rec))
assert.NotEqual(t, "", getCookie(rec, "browserToken").Value)
assert.NotEqual(t, "", getCookie(rec, BROWSER_TOKEN_COOKIE_NAME).Value)
assert.Equal(t, ts.App.FrontEndURL+"/web/user", rec.Header().Get("Location"))
}

func (ts *TestSuite) loginShouldFail(t *testing.T, rec *httptest.ResponseRecorder, errorMessage string) {
assert.Equal(t, http.StatusSeeOther, rec.Code)
assert.Equal(t, errorMessage, getErrorMessage(rec))
assert.Equal(t, "", getCookie(rec, "browserToken").Value)
assert.Equal(t, "", getCookie(rec, BROWSER_TOKEN_COOKIE_NAME).Value)
assert.Equal(t, ts.App.FrontEndURL, rec.Header().Get("Location"))
}

Expand Down Expand Up @@ -348,7 +348,7 @@ func (ts *TestSuite) testRegistrationNewPlayer(t *testing.T) {
form.Set("password", TEST_PASSWORD)
rec := ts.PostForm(t, ts.Server, "/web/register", form, nil, nil)
ts.registrationShouldSucceed(t, rec)
browserTokenCookie := getCookie(rec, "browserToken")
browserTokenCookie := getCookie(rec, BROWSER_TOKEN_COOKIE_NAME)

// Check that the user has been created with a correct password hash/salt
var user User
Expand Down Expand Up @@ -384,7 +384,7 @@ func (ts *TestSuite) testRegistrationNewPlayer(t *testing.T) {
form.Set("returnUrl", ts.App.FrontEndURL+"/web/registration")
rec := ts.PostForm(t, ts.Server, "/web/register", form, nil, nil)
ts.registrationShouldSucceed(t, rec)
browserTokenCookie := getCookie(rec, "browserToken")
browserTokenCookie := getCookie(rec, BROWSER_TOKEN_COOKIE_NAME)

// Users not in the DefaultAdmins list should not be admins
var user User
Expand Down Expand Up @@ -489,7 +489,7 @@ func (ts *TestSuite) testRegistrationNewPlayerChosenUUID(t *testing.T) {
rec := ts.PostForm(t, ts.Server, "/web/register", form, nil, nil)

// Registration should succeed, grant a browserToken, and redirect to user page
assert.NotEqual(t, "", getCookie(rec, "browserToken"))
assert.NotEqual(t, "", getCookie(rec, BROWSER_TOKEN_COOKIE_NAME))
ts.registrationShouldSucceed(t, rec)

// Check that the user has been created and has a player with the chosen UUID
Expand Down Expand Up @@ -588,7 +588,7 @@ func (ts *TestSuite) solveRegisterChallenge(t *testing.T, username string) *http
rec := httptest.NewRecorder()
ts.Server.ServeHTTP(rec, req)
assert.Equal(t, http.StatusOK, rec.Code)
challengeToken := getCookie(rec, "challengeToken")
challengeToken := getCookie(rec, CHALLENGE_TOKEN_COOKIE_NAME)
assert.NotEqual(t, "", challengeToken.Value)

base64Exp, err := regexp.Compile("src=\"data:image\\/png;base64,([A-Za-z0-9+/&#;]*={0,2})\"")
Expand Down Expand Up @@ -618,7 +618,7 @@ func (ts *TestSuite) solveCreatePlayerChallenge(t *testing.T, playerName string)
rec := httptest.NewRecorder()
ts.Server.ServeHTTP(rec, req)
assert.Equal(t, http.StatusOK, rec.Code)
challengeToken := getCookie(rec, "challengeToken")
challengeToken := getCookie(rec, CHALLENGE_TOKEN_COOKIE_NAME)
assert.NotEqual(t, "", challengeToken.Value)

base64Exp, err := regexp.Compile("src=\"data:image\\/png;base64,([A-Za-z0-9+/&#;]*={0,2})\"")
Expand Down Expand Up @@ -751,7 +751,7 @@ func (ts *TestSuite) testLoginLogout(t *testing.T) {
form.Set("returnUrl", ts.App.FrontEndURL+"/web/registration")
rec := ts.PostForm(t, ts.Server, "/web/login", form, nil, nil)
ts.loginShouldSucceed(t, rec)
browserTokenCookie := getCookie(rec, "browserToken")
browserTokenCookie := getCookie(rec, BROWSER_TOKEN_COOKIE_NAME)

// The BrowserToken we get should match the one in the database
var user User
Expand Down Expand Up @@ -1090,7 +1090,7 @@ func (ts *TestSuite) testUserUpdate(t *testing.T) {
form.Set("returnUrl", ts.App.FrontEndURL+"/web/registration")
rec = ts.PostForm(t, ts.Server, "/web/login", form, nil, nil)
ts.loginShouldSucceed(t, rec)
browserTokenCookie = getCookie(rec, "browserToken")
browserTokenCookie = getCookie(rec, BROWSER_TOKEN_COOKIE_NAME)
}
{
// As an admin, test updating another user's account
Expand Down Expand Up @@ -1453,7 +1453,7 @@ func (ts *TestSuite) testDeleteAccount(t *testing.T) {
form.Set("password", TEST_PASSWORD)
rec := ts.PostForm(t, ts.Server, "/web/register", form, nil, nil)
ts.registrationShouldSucceed(t, rec)
browserTokenCookie := getCookie(rec, "browserToken")
browserTokenCookie := getCookie(rec, BROWSER_TOKEN_COOKIE_NAME)

// Check that usernameB has been created
var otherUser User
Expand Down
2 changes: 1 addition & 1 deletion test_suite_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -185,7 +185,7 @@ func (ts *TestSuite) CreateTestUser(t *testing.T, app *App, server *echo.Echo, u
rec := httptest.NewRecorder()
server.ServeHTTP(rec, req)

browserToken := getCookie(rec, "browserToken")
browserToken := getCookie(rec, BROWSER_TOKEN_COOKIE_NAME)
assert.NotNil(t, browserToken)

assert.Nil(t, app.DB.First(&user, "username = ?", user.Username).Error)
Expand Down

0 comments on commit 96dfb52

Please sign in to comment.