extend the expiration date for the auth token cookie (#41261)

**Related issue:** Resolves #41262

This extends the expiration date for the host auth token cookie.

# Checklist for submitter

- [x] Changes file added for user-visible changes in `changes/`,
`orbit/changes/` or `ee/fleetd-chrome/changes`.
- [x] QA'd all new/changed functionality manually
This commit is contained in:
Gabriel Hernandez 2026-03-10 17:15:09 +00:00 committed by GitHub
parent 46f8cf4b12
commit aefad76342
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
10 changed files with 86 additions and 14 deletions

View file

@ -0,0 +1 @@
- Updated expiration date of the auth token cookie to match the fleet session duration

View file

@ -120,9 +120,12 @@ const LoginPage = ({ router, location }: ILoginPageProps) => {
try {
const response = await sessionsAPI.login(formData);
const { user, available_teams, token } = response;
const { user, available_teams, token, token_expires_at } = response;
authToken.save(token);
const expiresAt = token_expires_at
? new Date(token_expires_at)
: undefined;
authToken.save(token, expiresAt);
setCurrentUser(user);
setAvailableTeams(user, available_teams);

View file

@ -33,10 +33,16 @@ const LoginPreviewPage = ({ router }: ILoginPreviewPageProps): JSX.Element => {
const { DASHBOARD } = paths;
try {
const { user, available_teams, token } = await sessionsAPI.login(
formData
);
authToken.save(token);
const {
user,
available_teams,
token,
token_expires_at,
} = await sessionsAPI.login(formData);
const expiresAt = token_expires_at
? new Date(token_expires_at)
: undefined;
authToken.save(token, expiresAt);
setCurrentUser(user);
setAvailableTeams(user, available_teams);

View file

@ -43,9 +43,12 @@ const MfaPage = ({ router, params }: IMfaPage) => {
try {
const response = await sessionsAPI.finishMFA({ token: mfaToken });
const { user, available_teams, token } = response;
const { user, available_teams, token, token_expires_at } = response;
authToken.save(token);
const expiresAt = token_expires_at
? new Date(token_expires_at)
: undefined;
authToken.save(token, expiresAt);
setCurrentUser(user);
setAvailableTeams(user, available_teams);

View file

@ -23,6 +23,7 @@ export interface ILoginResponse {
available_teams: ITeamSummary[];
available_fleets: ITeamSummary[];
token: string;
token_expires_at?: string;
}
export default {

View file

@ -4,8 +4,14 @@
*/
import Cookie from "js-cookie";
const save = (token: string): void => {
Cookie.set("__Host-token", token, { secure: true, sameSite: "lax" });
const DEFAULT_EXPIRATION_DAYS = 5;
const save = (token: string, expiresAt?: Date): void => {
Cookie.set("__Host-token", token, {
secure: true,
sameSite: "lax",
expires: expiresAt ?? DEFAULT_EXPIRATION_DAYS,
});
};
const get = (): string | null => {
@ -13,9 +19,12 @@ const get = (): string | null => {
};
const remove = (): void => {
// NOTE: the entire cookie including the name and values must be provided
// NOTE: the secure and sameSite from the cookie must be provided
// to correctly remove. That is why we include the options here as well.
Cookie.remove("__Host-token", { secure: true, sameSite: "lax" });
Cookie.remove("__Host-token", {
secure: true,
sameSite: "lax",
});
};
export default {

View file

@ -240,6 +240,8 @@ type Service interface {
// SSOSettings returns non-sensitive single sign on information used before authentication
SSOSettings(ctx context.Context) (*SessionSSOSettings, error)
Login(ctx context.Context, email, password string, supportsEmailVerification bool) (user *User, session *Session, err error)
// GetSessionDuration returns the configured session duration
GetSessionDuration(ctx context.Context) time.Duration
Logout(ctx context.Context) (err error)
CompleteMFA(ctx context.Context, token string) (*Session, *User, error)
DestroySession(ctx context.Context) (err error)

View file

@ -111,6 +111,8 @@ type SSOSettingsFunc func(ctx context.Context) (*fleet.SessionSSOSettings, error
type LoginFunc func(ctx context.Context, email string, password string, supportsEmailVerification bool) (user *fleet.User, session *fleet.Session, err error)
type GetSessionDurationFunc func(ctx context.Context) time.Duration
type LogoutFunc func(ctx context.Context) (err error)
type CompleteMFAFunc func(ctx context.Context, token string) (*fleet.Session, *fleet.User, error)
@ -1026,6 +1028,9 @@ type Service struct {
LoginFunc LoginFunc
LoginFuncInvoked bool
GetSessionDurationFunc GetSessionDurationFunc
GetSessionDurationFuncInvoked bool
LogoutFunc LogoutFunc
LogoutFuncInvoked bool
@ -2515,6 +2520,13 @@ func (s *Service) Login(ctx context.Context, email string, password string, supp
return s.LoginFunc(ctx, email, password, supportsEmailVerification)
}
func (s *Service) GetSessionDuration(ctx context.Context) time.Duration {
s.mu.Lock()
s.GetSessionDurationFuncInvoked = true
s.mu.Unlock()
return s.GetSessionDurationFunc(ctx)
}
func (s *Service) Logout(ctx context.Context) (err error) {
s.mu.Lock()
s.LogoutFuncInvoked = true

View file

@ -23,6 +23,10 @@ func (mw metricsMiddleware) Login(ctx context.Context, email string, password st
return user, session, err
}
func (mw metricsMiddleware) GetSessionDuration(ctx context.Context) time.Duration {
return mw.Service.GetSessionDuration(ctx)
}
func (mw metricsMiddleware) Logout(ctx context.Context) error {
var err error
defer func(begin time.Time) {

View file

@ -120,6 +120,7 @@ type loginResponse struct {
User *fleet.User `json:"user,omitempty"`
AvailableTeams []*fleet.TeamSummary `json:"available_teams" renameto:"available_fleets"`
Token string `json:"token,omitempty"`
TokenExpiresAt *time.Time `json:"token_expires_at,omitempty"`
Err error `json:"error,omitempty"`
}
@ -159,7 +160,20 @@ func loginEndpoint(ctx context.Context, request interface{}, svc fleet.Service)
return loginResponse{Err: err}, nil
}
}
return loginResponse{user, availableTeams, session.Key, nil}, nil
// Calculate token expiration time if session duration is configured
var tokenExpiresAt *time.Time
if sessionDuration := svc.GetSessionDuration(ctx); sessionDuration > 0 {
expiresAt := time.Now().Add(sessionDuration).UTC()
tokenExpiresAt = &expiresAt
}
return loginResponse{
User: user,
AvailableTeams: availableTeams,
Token: session.Key,
TokenExpiresAt: tokenExpiresAt,
}, nil
}
var (
@ -254,6 +268,10 @@ func (svc *Service) makeSession(ctx context.Context, userID uint) (*fleet.Sessio
return svc.ds.NewSession(ctx, userID, svc.config.Session.KeySize)
}
func (svc *Service) GetSessionDuration(ctx context.Context) time.Duration {
return svc.config.Session.Duration
}
////////////////////////////////////////////////////////////////////////////////
// Session create (second step of MFA)
////////////////////////////////////////////////////////////////////////////////
@ -281,7 +299,20 @@ func sessionCreateEndpoint(ctx context.Context, request interface{}, svc fleet.S
return loginResponse{Err: err}, nil
}
}
return loginResponse{user, availableTeams, session.Key, nil}, nil
// Calculate token expiration time if session duration is configured
var tokenExpiresAt *time.Time
if sessionDuration := svc.GetSessionDuration(ctx); sessionDuration > 0 {
expiresAt := time.Now().Add(sessionDuration).UTC()
tokenExpiresAt = &expiresAt
}
return loginResponse{
User: user,
AvailableTeams: availableTeams,
Token: session.Key,
TokenExpiresAt: tokenExpiresAt,
}, nil
}
func (svc *Service) CompleteMFA(ctx context.Context, token string) (*fleet.Session, *fleet.User, error) {