diff --git a/api/jwt/jwt.go b/api/jwt/jwt.go
index 77b9fc279..54a1f03af 100644
--- a/api/jwt/jwt.go
+++ b/api/jwt/jwt.go
@@ -3,12 +3,14 @@ package jwt
 import (
 	"errors"
 	"fmt"
+	"os"
 	"time"
 
 	"github.com/golang-jwt/jwt"
 	"github.com/gorilla/securecookie"
 	portainer "github.com/portainer/portainer/api"
 	"github.com/portainer/portainer/api/dataservices"
+	log "github.com/sirupsen/logrus"
 )
 
 // scope represents JWT scopes that are supported in JWT claims.
@@ -164,6 +166,12 @@ func (service *Service) generateSignedToken(data *portainer.TokenData, expiresAt
 		return "", fmt.Errorf("invalid scope: %v", scope)
 	}
 
+	if _, ok := os.LookupEnv("DOCKER_EXTENSION"); ok {
+		// Set expiration to 99 years for docker desktop extension.
+		log.Infof("[message: detected docker desktop extension mode]")
+		expiresAt = time.Now().Add(time.Hour * 8760 * 99).Unix()
+	}
+
 	cl := claims{
 		UserID:              int(data.ID),
 		Username:            data.Username,
diff --git a/build/docker-extension/docker-compose.yml b/build/docker-extension/docker-compose.yml
index 551d58b3a..c4170fcf8 100644
--- a/build/docker-extension/docker-compose.yml
+++ b/build/docker-extension/docker-compose.yml
@@ -5,6 +5,8 @@ services:
     image: ${DESKTOP_PLUGIN_IMAGE}
     command: ['--admin-password', '$$$$2y$$$$05$$$$bsb.XmF.r2DU6/9oVUaDxu3.Lxhmg1R8M0NMLK6JJKUiqUcaNjvdu']
     restart: unless-stopped
+    environment:
+      - DOCKER_EXTENSION=1
     security_opt:
       - no-new-privileges:true
     volumes: