ServerAuthManager class

Пакет: com.hypixel.hytale.server.core.auth

Файл: com/hypixel/hytale/server/core/auth/ServerAuthManager.java

Поля (107)

МодификаторыТипИмя
SUCCESS, PENDING_PROFILE_SELECTION, FAILED
final HytaleLogger LOGGER
NONE, SINGLEPLAYER, EXTERNAL_SESSION, OAUTH_BROWSER, OAUTH_DEVICE, OAUTH_STORE
break
break
instance
return
return
Thread var1
OptionSet var1
AuthCredentialStoreProvider var1
String var1
SessionServiceClient.GameSessionResponse var1
SessionServiceClient.GameSessionResponse var1
SessionServiceClient.GameSessionResponse var1
SessionServiceClient.GameSessionResponse var1
IAuthCredentialStore var1
X509Certificate var1
UUID var1
StringBuilder var1
SessionServiceClient.GameSessionResponse var1
boolean var1
JWTValidator.SessionTokenClaims var10
UUID var11
SessionServiceClient.GameProfile var2
IAuthCredentialStore var2
UUID var2
long var2
IAuthCredentialStore var2
SessionServiceClient.GameProfile[] var2
SessionServiceClient.GameProfile[] var2
JWTValidator var2
IAuthCredentialStore var2
IAuthCredentialStore var2
OptionSet var2
IAuthCredentialStore var2
Instant var2
Instant var2
Date var2
long var2
String var2
SessionServiceClient.GameSessionResponse var2
UUID var2
String var3
var3
var3
IAuthCredentialStore.OAuthTokens var3
IAuthCredentialStore var3
IAuthCredentialStore var3
IAuthCredentialStore var3
ServerAuthManager.AuthMode var3
ServerAuthManager.AuthMode var3
JWTValidator.IdentityTokenClaims var3
JWTValidator var3
String var3
IAuthCredentialStore.OAuthTokens var3
UUID var3
SessionServiceClient.GameSessionResponse var3
String var3
Instant var3
Instant var3
SessionServiceClient.GameSessionResponse var3
String var4
var4
var4
ServerAuthManager.AuthResult var4
OAuthClient.TokenResponse var4
OAuthClient.TokenResponse var4
IAuthCredentialStore.OAuthTokens var4
SessionServiceClient.GameProfile var4
OptionSet var4
boolean var4
var4
var4
var4
var4
var4
var4
SessionServiceClient.GameProfile[] var4
Instant var4
Instant var4
SessionServiceClient.GameSessionResponse var4
var4
long var4
long var4
Instant var4
String var5
UUID var5
UUID var5
OptionSet var5
String var5
TelemetryService var5
boolean var6
var6
String var6
UUID var6
String var7
UUID var7
String var7
OAuthClient.TokenResponse var7
String var8
JWTValidator.IdentityTokenClaims var8
long var8
UUID var8
SessionServiceClient.GameSessionResponse var9
UUID var9
SessionServiceClient.GameProfile var9

Методы (109)

МодификаторыВозвратСигнатура
AuthMode AuthMode()
AuthResult AuthResult()
private void attemptSessionRefreshvoid attemptSessionRefresh(int var1)
public boolean cancelActiveFlowboolean cancelActiveFlow()
public void checkPendingFatalErrorvoid checkPendingFatalError()
public void clearPendingProfilesvoid clearPendingProfiles()
private boolean completeAuthWithProfileboolean completeAuthWithProfile(SessionServiceClient.GameProfile var1, ServerAuthManager.AuthMode var2)
private ServerAuthManager.AuthResult createGameSessionFromOAuthServerAuthManager.AuthResult createGameSessionFromOAuth(ServerAuthManager.AuthMode var1)
for for(SessionServiceClient.GameProfile var7 : var2)
for for(SessionServiceClient.GameProfile var8 : var4)
for for(int var10 = 0; var10 < var4.length; var10++)
for for(int var6 = 1; var6 <= 3; var6++)
for for(SessionServiceClient.GameProfile var12 : var1)
for for(SessionServiceClient.GameProfile var7 : var1)
public ServerAuthManager.AuthMode getAuthModeServerAuthManager.AuthMode getAuthMode()
public String getAuthStatusString getAuthStatus()
static ServerAuthManager getInstanceServerAuthManager getInstance()
public boolean hasIdentityTokenboolean hasIdentityToken()
public boolean hasPendingProfilesboolean hasPendingProfiles()
public boolean hasSessionTokenboolean hasSessionToken()
if if(instance == null)
if if(instance == null)
if if(this.profileServiceClient == null)
if if(this.profileServiceClient == null)
if if(var1 == null)
if if(var3 != null || var4 != null)
if if(var6)
if if(this.isSingleplayer)
if if(this.pendingFatalError != null)
if if(this.sessionServiceClient == null)
if if(this.refreshTask != null)
if if(this.tokenExpiry != null)
if if(var2 > 0L)
if if(this.isSingleplayer)
if if(this.isSingleplayer)
if if(this.isSingleplayer)
if if(var5 != null)
if if(this.cancelActiveFlow != null)
if if(var2 == null || var2.length == 0)
if if(var2 != null && var2.length != 0)
if if(this.sessionServiceClient == null)
if if(var3 == null)
if if(var1 == null && var2 == null)
if if(this.sessionServiceClient == null)
if if(var2 != null)
if if(var8 == null)
if if(var6 != null)
if if(var4)
if if(var1 != null)
if if(var10 == null)
if if(var6 != null)
if if(var4)
if if(var3 == null)
if if(this.sessionServiceClient == null)
if if(var4 != null && var4.length != 0)
if if(var9 != null)
if if(var5 == null)
if if(var6 > 1)
if if(var6 < 3)
if if(var1.length == 1)
if if(var3 != null)
if if(var3 == null)
if if(var4 != null)
if if(var5 != null)
if if(this.sessionServiceClient == null)
if if(var4 == null)
if if(var4 == null)
if if(var2 != null)
if if(var2 != null && var3 != null)
if if(var1 == null)
if if(var2 != null)
if if(this.refreshTask != null)
if if(var2 > 300L)
if if(var2 != null)
if if(var1 > 1)
if if(this.sessionServiceClient == null)
if if(var2 == null)
if if(var3 != null)
if if(!var1)
if if(var2 == null)
if if(var3 == null)
if if(var4 != null)
public void initializevoid initialize()
public void initializeCredentialStorevoid initializeCredentialStore()
public boolean isOwnerboolean isOwner(@Nullable UUID var1)
public boolean isSingleplayerboolean isSingleplayer()
public void logoutvoid logout()
private void parseAndScheduleRefreshvoid parseAndScheduleRefresh()
private boolean refreshGameSessionboolean refreshGameSession(String var1)
private boolean refreshGameSessionViaOAuthboolean refreshGameSessionViaOAuth()
private boolean refreshOAuthTokensboolean refreshOAuthTokens()
private boolean refreshOAuthTokensboolean refreshOAuthTokens(boolean var1)
public CompletableFuture<ServerAuthManager.AuthResult> registerCredentialStoreCompletableFuture<ServerAuthManager.AuthResult> registerCredentialStore(IAuthCredentialStore var1)
public boolean selectPendingProfileboolean selectPendingProfile(int var1)
public boolean selectPendingProfileByUsernameboolean selectPendingProfileByUsername(String var1)
private void setExpiryAndScheduleRefreshvoid setExpiryAndScheduleRefresh(@Nonnull Instant var1)
public void setGameSessionvoid setGameSession(@Nonnull SessionServiceClient.GameSessionResponse var1)
public void setServerCertificatevoid setServerCertificate(@Nonnull X509Certificate var1)
public void shutdownvoid shutdown()
public CompletableFuture<ServerAuthManager.AuthResult> startFlowAsyncCompletableFuture<ServerAuthManager.AuthResult> startFlowAsync(@Nonnull OAuthBrowserFlow var1)
public CompletableFuture<ServerAuthManager.AuthResult> startFlowAsyncCompletableFuture<ServerAuthManager.AuthResult> startFlowAsync(OAuthDeviceFlow var1)
public void swapCredentialStoreProvidervoid swapCredentialStoreProvider(@Nonnull AuthCredentialStoreProvider var1)
switch switch(var4)
switch switch(var2)
switch switch(var2)
synchronized synchronized(ServerAuthManager.class)
synchronized synchronized(this)
private boolean validateInitialTokensboolean validateInitialTokens(@Nullable String var1, @Nullable String var2)
private boolean validateOfflineTokenboolean validateOfflineToken(@Nonnull String var1)

Исходный код

Показать/скрыть
class="kw">package com.hypixel.hytale.server.core.auth;

class="kw">import com.hypixel.hytale.codec.lookup.Priority;
class="kw">import com.hypixel.hytale.logger.HytaleLogger;
class="kw">import com.hypixel.hytale.server.core.HytaleServer;
class="kw">import com.hypixel.hytale.server.core.Message;
class="kw">import com.hypixel.hytale.server.core.Options;
class="kw">import com.hypixel.hytale.server.core.ShutdownReason;
class="kw">import com.hypixel.hytale.server.core.auth.oauth.OAuthBrowserFlow;
class="kw">import com.hypixel.hytale.server.core.auth.oauth.OAuthClient;
class="kw">import com.hypixel.hytale.server.core.auth.oauth.OAuthDeviceFlow;
class="kw">import com.hypixel.hytale.server.core.telemetry.TelemetryService;
class="kw">import com.nimbusds.jwt.SignedJWT;
class="kw">import java.io.IOException;
class="kw">import java.security.cert.X509Certificate;
class="kw">import java.time.Duration;
class="kw">import java.time.Instant;
class="kw">import java.util.Date;
class="kw">import java.util.Map;
class="kw">import java.util.UUID;
class="kw">import java.util.concurrent.CompletableFuture;
class="kw">import java.util.concurrent.CompletionException;
class="kw">import java.util.concurrent.ConcurrentHashMap;
class="kw">import java.util.concurrent.Executors;
class="kw">import java.util.concurrent.ScheduledExecutorService;
class="kw">import java.util.concurrent.ScheduledFuture;
class="kw">import java.util.concurrent.TimeUnit;
class="kw">import java.util.concurrent.atomic.AtomicReference;
class="kw">import java.util.logging.Level;
class="kw">import javax.annotation.Nonnull;
class="kw">import javax.annotation.Nullable;
class="kw">import joptsimple.OptionSet;

class="kw">public class ServerAuthManager {
   class="kw">private class="kw">static class="kw">final HytaleLogger LOGGER = HytaleLogger.forEnclosingClass();
   class="kw">private class="kw">static class="kw">final int REFRESH_BUFFER_SECONDS = 300;
   class="kw">private class="kw">static class="kw">final int REFRESH_MAX_RETRIES = 3;
   class="kw">private class="kw">static class="kw">final int REFRESH_RETRY_BASE_DELAY_SECONDS = 30;
   class="kw">private class="kw">static class="kw">volatile ServerAuthManager instance;
   class="kw">private class="kw">volatile ServerAuthManager.AuthMode authMode = ServerAuthManager.AuthMode.NONE;
   class="kw">private class="kw">volatile Instant tokenExpiry;
   class="kw">private class="kw">final AtomicReference<SessionServiceClient.GameSessionResponse> gameSession = new AtomicReference<>();
   class="kw">private class="kw">final AtomicReference<IAuthCredentialStore> credentialStore = new AtomicReference<>(new DefaultAuthCredentialStore());
   class="kw">private class="kw">final Map<UUID, SessionServiceClient.GameProfile> availableProfiles = new ConcurrentHashMap<>();
   class="kw">private class="kw">volatile SessionServiceClient.GameProfile[] pendingProfiles;
   class="kw">private class="kw">volatile ServerAuthManager.AuthMode pendingAuthMode;
   class="kw">private class="kw">final AtomicReference<X509Certificate> serverCertificate = new AtomicReference<>();
   class="kw">private class="kw">final UUID serverSessionId = UUID.randomUUID();
   class="kw">private class="kw">volatile boolean isSingleplayer;
   class="kw">private OAuthClient oauthClient;
   class="kw">private class="kw">volatile SessionServiceClient sessionServiceClient;
   class="kw">private class="kw">volatile ProfileServiceClient profileServiceClient;
   class="kw">private class="kw">final ScheduledExecutorService refreshScheduler = Executors.newSingleThreadScheduledExecutor(var0 -> {
      Thread var1 = new Thread(var0, "TokenRefresh");
      var1.setDaemon(true);
      class="kw">return var1;
   });
   class="kw">private ScheduledFuture<?> refreshTask;
   class="kw">private Runnable cancelActiveFlow;
   class="kw">private class="kw">volatile Message pendingFatalError;

   class="kw">private ServerAuthManager() {
   }

   class="kw">public class="kw">static ServerAuthManager getInstance() {
      if (instance == null) {
         class="kw">synchronized (ServerAuthManager.class) {
            if (instance == null) {
               instance = new ServerAuthManager();
            }
         }
      }

      class="kw">return instance;
   }

   @Nonnull
   class="kw">public ProfileServiceClient getProfileServiceClient() {
      if (this.profileServiceClient == null) {
         class="kw">synchronized (this) {
            if (this.profileServiceClient == null) {
               this.profileServiceClient = new ProfileServiceClient("https://account-data.hytale.com");
            }
         }
      }

      class="kw">return this.profileServiceClient;
   }

   class="kw">public void initialize() {
      OptionSet var1 = Options.getOptionSet();
      if (var1 == null) {
         LOGGER.at(Level.WARNING).log("Options not parsed, cannot initialize ServerAuthManager");
      } else {
         this.oauthClient = new OAuthClient();
         this.isSingleplayer = var1.has(Options.SINGLEPLAYER);
         if (this.isSingleplayer && var1.has(Options.OWNER_UUID)) {
            SessionServiceClient.GameProfile var2 = new SessionServiceClient.GameProfile();
            var2.uuid = (UUID)var1.valueOf(Options.OWNER_UUID);
            var2.username = var1.has(Options.OWNER_NAME) ? (String)var1.valueOf(Options.OWNER_NAME) : null;
            this.credentialStore.get().setProfile(var2.uuid);
            LOGGER.at(Level.INFO).log("Singleplayer mode, owner: %s (%s)", var2.username, var2.uuid);
         }

         if (this.isSingleplayer && var1.valueOf(Options.AUTH_MODE) == Options.AuthMode.OFFLINE) {
            String var7 = System.getenv("HYTALE_SERVER_OFFLINE_TOKEN");
            if (var7 != null && !var7.isEmpty()) {
               LOGGER.at(Level.INFO).log("Offline token loaded from environment");
               if (this.validateOfflineToken(var7)) {
                  LOGGER.at(Level.INFO).log("Offline token validated, singleplayer offline mode");
                  LOGGER.at(Level.INFO).log("Server session ID: %s", this.serverSessionId);
               } else {
                  this.pendingFatalError = Message.translation("client.disconnection.shutdownReason.authFailed.offlineTokenValidationFailed");
                  LOGGER.at(Level.SEVERE).log("%s", this.pendingFatalError.getAnsiMessage());
               }
            } else {
               this.pendingFatalError = Message.translation("client.disconnection.shutdownReason.authFailed.needOfficialLauncher");
               LOGGER.at(Level.SEVERE).log("%s", this.pendingFatalError.getAnsiMessage());
            }
         } else {
            boolean var6 = false;
            String var3 = null;
            String var4 = null;
            if (var1.has(Options.SESSION_TOKEN)) {
               var3 = (String)var1.valueOf(Options.SESSION_TOKEN);
               LOGGER.at(Level.INFO).log("Session token loaded from CLI");
            } else {
               String var5 = System.getenv("HYTALE_SERVER_SESSION_TOKEN");
               if (var5 != null && !var5.isEmpty()) {
                  var3 = var5;
                  LOGGER.at(Level.INFO).log("Session token loaded from environment");
               }
            }

            if (var1.has(Options.IDENTITY_TOKEN)) {
               var4 = (String)var1.valueOf(Options.IDENTITY_TOKEN);
               LOGGER.at(Level.INFO).log("Identity token loaded from CLI");
            } else {
               String var8 = System.getenv("HYTALE_SERVER_IDENTITY_TOKEN");
               if (var8 != null && !var8.isEmpty()) {
                  var4 = var8;
                  LOGGER.at(Level.INFO).log("Identity token loaded from environment");
               }
            }

            if (var3 != null || var4 != null) {
               if (this.validateInitialTokens(var3, var4)) {
                  SessionServiceClient.GameSessionResponse var9 = new SessionServiceClient.GameSessionResponse();
                  var9.sessionToken = var3;
                  var9.identityToken = var4;
                  this.gameSession.set(var9);
                  var6 = true;
               } else {
                  this.pendingFatalError = Message.translation("client.disconnection.shutdownReason.authFailed.tokenValidationFailed");
                  LOGGER.at(Level.SEVERE).log("%s", this.pendingFatalError.getAnsiMessage());
               }
            }

            if (var6) {
               if (this.isSingleplayer) {
                  this.authMode = ServerAuthManager.AuthMode.SINGLEPLAYER;
                  LOGGER.at(Level.INFO).log("Auth mode: SINGLEPLAYER");
               } else {
                  this.authMode = ServerAuthManager.AuthMode.EXTERNAL_SESSION;
                  LOGGER.at(Level.INFO).log("Auth mode: EXTERNAL_SESSION");
               }

               this.parseAndScheduleRefresh();
            } else {
               LOGGER.at(Level.INFO).log("No server tokens configured. Use /auth login to authenticate, or provide tokens via CLI/environment.");
            }

            LOGGER.at(Level.INFO).log("Server session ID: %s", this.serverSessionId);
            LOGGER.at(Level.FINE)
               .log(
                  "ServerAuthManager initialized - session token: %s, identity token: %s, auth mode: %s",
                  this.hasSessionToken() ? "present" : "missing",
                  this.hasIdentityToken() ? "present" : "missing",
                  this.authMode
               );
         }
      }
   }

   class="kw">public void checkPendingFatalError() {
      if (this.pendingFatalError != null) {
         HytaleServer.get().shutdownServer(ShutdownReason.AUTH_FAILED.withMessage(this.pendingFatalError));
      }
   }

   class="kw">public void initializeCredentialStore() {
      AuthCredentialStoreProvider var1 = HytaleServer.get().getConfig().getAuthCredentialStoreProvider();
      this.credentialStore.set(var1.createStore());
      LOGGER.at(Level.INFO)
         .log("Auth credential store: %s", AuthCredentialStoreProvider.CODEC.getIdFor((Class<? class="kw">extends AuthCredentialStoreProvider>)var1.getClass()));
      IAuthCredentialStore var2 = this.credentialStore.get();
      IAuthCredentialStore.OAuthTokens var3 = var2.getTokens();
      if (var3.isValid()) {
         LOGGER.at(Level.INFO).log("Found stored credentials, attempting to restore session...");
         ServerAuthManager.AuthResult var4 = this.createGameSessionFromOAuth(ServerAuthManager.AuthMode.OAUTH_STORE);
         class="kw">switch (var4) {
            case SUCCESS:
               LOGGER.at(Level.INFO).log("Session restored from stored credentials");
               break;
            case PENDING_PROFILE_SELECTION:
               LOGGER.at(Level.INFO).log("Session restored but profile selection required - use /auth select");
               break;
            class="kw">default:
               LOGGER.at(Level.WARNING).log("Failed to restore session from stored credentials");
         }
      }
   }

   class="kw">public void shutdown() {
      this.cancelActiveFlow();
      this.refreshScheduler.shutdownNow();
      if (this.isSingleplayer()) {
         String var1 = this.getSessionToken();
         if (var1 != null && !var1.isEmpty()) {
            if (this.sessionServiceClient == null) {
               this.sessionServiceClient = new SessionServiceClient("https://sessions.hytale.com");
            }

            this.sessionServiceClient.terminateSession(var1);
         }
      }
   }

   class="kw">public void logout() {
      this.cancelActiveFlow();
      if (this.refreshTask != null) {
         this.refreshTask.cancel(false);
         this.refreshTask = null;
      }

      this.gameSession.set(null);
      this.credentialStore.get().clear();
      this.availableProfiles.clear();
      this.pendingProfiles = null;
      this.pendingAuthMode = null;
      this.tokenExpiry = null;
      this.authMode = ServerAuthManager.AuthMode.NONE;
      LOGGER.at(Level.INFO).log("Server logged out");
   }

   @Nullable
   class="kw">public SessionServiceClient.GameSessionResponse getGameSession() {
      class="kw">return this.gameSession.get();
   }

   class="kw">public void setGameSession(@Nonnull SessionServiceClient.GameSessionResponse var1) {
      this.gameSession.set(var1);
      LOGGER.at(Level.FINE).log("Game session updated");
   }

   @Nullable
   class="kw">public String getIdentityToken() {
      SessionServiceClient.GameSessionResponse var1 = this.gameSession.get();
      class="kw">return var1 != null ? var1.identityToken : null;
   }

   @Nullable
   class="kw">public String getSessionToken() {
      SessionServiceClient.GameSessionResponse var1 = this.gameSession.get();
      class="kw">return var1 != null ? var1.sessionToken : null;
   }

   class="kw">public boolean hasIdentityToken() {
      SessionServiceClient.GameSessionResponse var1 = this.gameSession.get();
      class="kw">return var1 != null && var1.identityToken != null;
   }

   class="kw">public boolean hasSessionToken() {
      SessionServiceClient.GameSessionResponse var1 = this.gameSession.get();
      class="kw">return var1 != null && var1.sessionToken != null;
   }

   @Nullable
   class="kw">public String getOAuthAccessToken() {
      if (!this.refreshOAuthTokens()) {
         class="kw">return null;
      }

      IAuthCredentialStore var1 = this.credentialStore.get();
      class="kw">return var1.getTokens().accessToken();
   }

   class="kw">public void setServerCertificate(@Nonnull X509Certificate var1) {
      this.serverCertificate.set(var1);
      LOGGER.at(Level.INFO).log("Server certificate set: %s", var1.getSubjectX500Principal());
   }

   @Nullable
   class="kw">public X509Certificate getServerCertificate() {
      class="kw">return this.serverCertificate.get();
   }

   @Nullable
   class="kw">public String getServerCertificateFingerprint() {
      X509Certificate var1 = this.serverCertificate.get();
      class="kw">return var1 == null ? null : CertificateUtil.computeCertificateFingerprint(var1);
   }

   @Nonnull
   class="kw">public UUID getServerSessionId() {
      class="kw">return this.serverSessionId;
   }

   class="kw">public ServerAuthManager.AuthMode getAuthMode() {
      class="kw">return this.authMode;
   }

   class="kw">public boolean isSingleplayer() {
      class="kw">return this.isSingleplayer;
   }

   class="kw">public boolean isOwner(@Nullable UUID var1) {
      UUID var2 = this.credentialStore.get().getProfile();
      class="kw">return var2 != null && var2.equals(var1);
   }

   @Nullable
   class="kw">public SessionServiceClient.GameProfile getSelectedProfile() {
      UUID var1 = this.credentialStore.get().getProfile();
      class="kw">return var1 == null ? null : this.availableProfiles.get(var1);
   }

   @Nullable
   class="kw">public Instant getTokenExpiry() {
      class="kw">return this.tokenExpiry;
   }

   class="kw">public String getAuthStatus() {
      StringBuilder var1 = new StringBuilder();
      var1.append(this.authMode.name());
      if (this.hasSessionToken() && this.hasIdentityToken()) {
         var1.append(" (authenticated)");
      } else if (!this.hasSessionToken() && !this.hasIdentityToken()) {
         var1.append(" (no tokens)");
      } else {
         var1.append(" (partial)");
      }

      if (this.tokenExpiry != null) {
         long var2 = this.tokenExpiry.getEpochSecond() - Instant.now().getEpochSecond();
         if (var2 > 0L) {
            var1.append(String.format(" [expires in %dm %ds]", var2 / 60L, var2 % 60L));
         } else {
            var1.append(" [EXPIRED]");
         }
      }

      class="kw">return var1.toString();
   }

   class="kw">public CompletableFuture<ServerAuthManager.AuthResult> startFlowAsync(@Nonnull OAuthBrowserFlow var1) {
      if (this.isSingleplayer) {
         class="kw">return CompletableFuture.completedFuture(ServerAuthManager.AuthResult.FAILED);
      }

      this.cancelActiveFlow();
      this.cancelActiveFlow = this.oauthClient.startFlow(var1);
      class="kw">return var1.getFuture().thenApply(var2 -> {
         class="kw">switch (var2) {
            case SUCCESS:
               IAuthCredentialStore var3 = this.credentialStore.get();
               OAuthClient.TokenResponse var4 = var1.getTokenResponse();
               var3.setTokens(new IAuthCredentialStore.OAuthTokens(var4.accessToken(), var4.refreshToken(), Instant.now().plusSeconds(var4.expiresIn())));
               class="kw">return this.createGameSessionFromOAuth(ServerAuthManager.AuthMode.OAUTH_BROWSER);
            case FAILED:
               LOGGER.at(Level.WARNING).log("OAuth browser flow failed: %s", var1.getErrorMessage());
               class="kw">return ServerAuthManager.AuthResult.FAILED;
            case UNKNOWN:
            class="kw">default:
               LOGGER.at(Level.WARNING).log("OAuth browser flow completed with unexpected result: %s", var2);
               class="kw">return ServerAuthManager.AuthResult.FAILED;
         }
      });
   }

   class="kw">public CompletableFuture<ServerAuthManager.AuthResult> startFlowAsync(OAuthDeviceFlow var1) {
      if (this.isSingleplayer) {
         class="kw">return CompletableFuture.completedFuture(ServerAuthManager.AuthResult.FAILED);
      }

      this.cancelActiveFlow();
      this.cancelActiveFlow = this.oauthClient.startFlow(var1);
      class="kw">return var1.getFuture().thenApply(var2 -> {
         class="kw">switch (var2) {
            case SUCCESS:
               IAuthCredentialStore var3 = this.credentialStore.get();
               OAuthClient.TokenResponse var4 = var1.getTokenResponse();
               var3.setTokens(new IAuthCredentialStore.OAuthTokens(var4.accessToken(), var4.refreshToken(), Instant.now().plusSeconds(var4.expiresIn())));
               class="kw">return this.createGameSessionFromOAuth(ServerAuthManager.AuthMode.OAUTH_DEVICE);
            case FAILED:
               LOGGER.at(Level.WARNING).log("OAuth device flow failed: %s", var1.getErrorMessage());
               class="kw">return ServerAuthManager.AuthResult.FAILED;
            case UNKNOWN:
            class="kw">default:
               LOGGER.at(Level.WARNING).log("OAuth device flow completed with unexpected result: %s", var2);
               class="kw">return ServerAuthManager.AuthResult.FAILED;
         }
      });
   }

   class="kw">public CompletableFuture<ServerAuthManager.AuthResult> registerCredentialStore(IAuthCredentialStore var1) {
      if (this.isSingleplayer) {
         class="kw">return CompletableFuture.completedFuture(ServerAuthManager.AuthResult.FAILED);
      }

      if (this.hasSessionToken() && this.hasIdentityToken()) {
         class="kw">return CompletableFuture.completedFuture(ServerAuthManager.AuthResult.FAILED);
      }

      this.credentialStore.set(var1);
      class="kw">return CompletableFuture.completedFuture(this.createGameSessionFromOAuth(ServerAuthManager.AuthMode.OAUTH_STORE));
   }

   class="kw">public void swapCredentialStoreProvider(@Nonnull AuthCredentialStoreProvider var1) {
      IAuthCredentialStore var2 = this.credentialStore.get();
      IAuthCredentialStore var3 = var1.createStore();
      IAuthCredentialStore.OAuthTokens var4 = var2.getTokens();
      if (var4.isValid()) {
         var3.setTokens(var4);
      }

      UUID var5 = var2.getProfile();
      if (var5 != null) {
         var3.setProfile(var5);
      }

      this.credentialStore.set(var3);
      LOGGER.at(Level.INFO).log("Swapped credential store to: %s", var1.getClass().getSimpleName());
   }

   class="kw">public boolean cancelActiveFlow() {
      if (this.cancelActiveFlow != null) {
         this.cancelActiveFlow.run();
         this.cancelActiveFlow = null;
         class="kw">return true;
      } else {
         class="kw">return false;
      }
   }

   @Nullable
   class="kw">public SessionServiceClient.GameProfile[] getPendingProfiles() {
      class="kw">return this.pendingProfiles;
   }

   class="kw">public boolean hasPendingProfiles() {
      class="kw">return this.pendingProfiles != null && this.pendingProfiles.length > 0;
   }

   class="kw">public boolean selectPendingProfile(int var1) {
      SessionServiceClient.GameProfile[] var2 = this.pendingProfiles;
      ServerAuthManager.AuthMode var3 = this.pendingAuthMode;
      if (var2 == null || var2.length == 0) {
         LOGGER.at(Level.WARNING).log("No pending profiles to select");
         class="kw">return false;
      } else if (var1 >= 1 && var1 <= var2.length) {
         SessionServiceClient.GameProfile var4 = var2[var1 - 1];
         LOGGER.at(Level.INFO).log("Selected profile: %s (%s)", var4.username, var4.uuid);
         class="kw">return this.completeAuthWithProfile(var4, var3 != null ? var3 : ServerAuthManager.AuthMode.OAUTH_BROWSER);
      } else {
         LOGGER.at(Level.WARNING).log("Invalid profile index: %d (valid range: 1-%d)", var1, var2.length);
         class="kw">return false;
      }
   }

   class="kw">public boolean selectPendingProfileByUsername(String var1) {
      SessionServiceClient.GameProfile[] var2 = this.pendingProfiles;
      ServerAuthManager.AuthMode var3 = this.pendingAuthMode;
      if (var2 != null && var2.length != 0) {
         for (SessionServiceClient.GameProfile var7 : var2) {
            if (var7.username != null && var7.username.equalsIgnoreCase(var1)) {
               LOGGER.at(Level.INFO).log("Selected profile: %s (%s)", var7.username, var7.uuid);
               class="kw">return this.completeAuthWithProfile(var7, var3 != null ? var3 : ServerAuthManager.AuthMode.OAUTH_BROWSER);
            }
         }

         LOGGER.at(Level.WARNING).log("No profile found with username: %s", var1);
         class="kw">return false;
      } else {
         LOGGER.at(Level.WARNING).log("No pending profiles to select");
         class="kw">return false;
      }
   }

   class="kw">public void clearPendingProfiles() {
      this.pendingProfiles = null;
      this.pendingAuthMode = null;
   }

   class="kw">private boolean validateOfflineToken(@Nonnull String var1) {
      if (this.sessionServiceClient == null) {
         this.sessionServiceClient = new SessionServiceClient("https://sessions.hytale.com");
      }

      JWTValidator var2 = new JWTValidator(this.sessionServiceClient, "https://sessions.hytale.com", "");
      JWTValidator.IdentityTokenClaims var3 = var2.validateOfflineToken(var1);
      if (var3 == null) {
         LOGGER.at(Level.WARNING).log("Offline token validation failed");
         class="kw">return false;
      }

      OptionSet var4 = Options.getOptionSet();
      UUID var5 = var4 != null && var4.has(Options.OWNER_UUID) ? (UUID)var4.valueOf(Options.OWNER_UUID) : null;
      String var6 = var4 != null && var4.has(Options.OWNER_NAME) ? (String)var4.valueOf(Options.OWNER_NAME) : null;
      UUID var7 = var3.getSubjectAsUUID();
      if (var5 == null || var7 != null && var7.equals(var5)) {
         if (var6 != null && var3.username != null && !var3.username.equals(var6)) {
            LOGGER.at(Level.WARNING).log("Offline token username mismatch: token has '%s', expected '%s'", var3.username, var6);
            class="kw">return false;
         } else {
            LOGGER.at(Level.INFO).log("Offline token validated for %s (%s)", var3.username, var3.subject);
            class="kw">return true;
         }
      } else {
         LOGGER.at(Level.WARNING).log("Offline token UUID mismatch: token has %s, expected %s", var3.subject, var5);
         class="kw">return false;
      }
   }

   class="kw">private boolean validateInitialTokens(@Nullable String var1, @Nullable String var2) {
      if (var1 == null && var2 == null) {
         class="kw">return false;
      }

      if (this.sessionServiceClient == null) {
         this.sessionServiceClient = new SessionServiceClient("https://sessions.hytale.com");
      }

      JWTValidator var3 = new JWTValidator(this.sessionServiceClient, "https://sessions.hytale.com", "");
      boolean var4 = true;
      OptionSet var5 = Options.getOptionSet();
      UUID var6 = var5 != null && var5.has(Options.OWNER_UUID) ? (UUID)var5.valueOf(Options.OWNER_UUID) : null;
      String var7 = var5 != null && var5.has(Options.OWNER_NAME) ? (String)var5.valueOf(Options.OWNER_NAME) : null;
      if (var2 != null) {
         JWTValidator.IdentityTokenClaims var8 = var3.validateIdentityToken(var2);
         if (var8 == null) {
            LOGGER.at(Level.WARNING).log("Identity token validation failed");
            var4 = false;
         } else if (!var8.hasScope("hytale:server")) {
            LOGGER.at(Level.WARNING).log("Identity token missing required scope: expected %s, got %s", "hytale:server", var8.scope);
            var4 = false;
         } else {
            if (var6 != null) {
               UUID var9 = var8.getSubjectAsUUID();
               if (var9 == null || !var9.equals(var6)) {
                  LOGGER.at(Level.WARNING).log("Identity token UUID mismatch: token has %s, expected %s", var8.subject, var6);
                  var4 = false;
               }
            }

            if (var7 != null && var8.username != null && !var8.username.equals(var7)) {
               LOGGER.at(Level.WARNING).log("Identity token username mismatch: token has '%s', expected '%s'", var8.username, var7);
               var4 = false;
            }

            if (var4) {
               LOGGER.at(Level.INFO).log("Identity token validated for %s (%s)", var8.username, var8.subject);
            }
         }
      }

      if (var1 != null) {
         JWTValidator.SessionTokenClaims var10 = var3.validateSessionToken(var1);
         if (var10 == null) {
            LOGGER.at(Level.WARNING).log("Session token validation failed");
            var4 = false;
         } else {
            if (var6 != null) {
               UUID var11 = var10.getSubjectAsUUID();
               if (var11 == null || !var11.equals(var6)) {
                  LOGGER.at(Level.WARNING).log("Session token UUID mismatch: token has %s, expected %s", var10.subject, var6);
                  var4 = false;
               }
            }

            if (var4) {
               LOGGER.at(Level.INFO).log("Session token validated");
            }
         }
      }

      class="kw">return var4;
   }

   class="kw">private ServerAuthManager.AuthResult createGameSessionFromOAuth(ServerAuthManager.AuthMode var1) {
      if (!this.refreshOAuthTokens()) {
         LOGGER.at(Level.WARNING).log("No valid OAuth tokens to create game session");
         class="kw">return ServerAuthManager.AuthResult.FAILED;
      }

      IAuthCredentialStore var2 = this.credentialStore.get();
      String var3 = var2.getTokens().accessToken();
      if (var3 == null) {
         LOGGER.at(Level.WARNING).log("No access token in credential store");
         class="kw">return ServerAuthManager.AuthResult.FAILED;
      }

      if (this.sessionServiceClient == null) {
         this.sessionServiceClient = new SessionServiceClient("https://sessions.hytale.com");
      }

      SessionServiceClient.GameProfile[] var4 = this.sessionServiceClient.getGameProfiles(var3);
      if (var4 != null && var4.length != 0) {
         this.availableProfiles.clear();

         for (SessionServiceClient.GameProfile var8 : var4) {
            this.availableProfiles.put(var8.uuid, var8);
         }

         SessionServiceClient.GameProfile var9 = this.tryAutoSelectProfile(var4);
         if (var9 != null) {
            class="kw">return this.completeAuthWithProfile(var9, var1) ? ServerAuthManager.AuthResult.SUCCESS : ServerAuthManager.AuthResult.FAILED;
         }

         this.pendingProfiles = var4;
         this.pendingAuthMode = var1;
         this.cancelActiveFlow = null;
         LOGGER.at(Level.INFO).log("Multiple profiles available. Use '/auth select <number>' to choose:");

         for (int var10 = 0; var10 < var4.length; var10++) {
            LOGGER.at(Level.INFO).log("  [%d] %s (%s)", var10 + 1, var4[var10].username, var4[var10].uuid);
         }

         class="kw">return ServerAuthManager.AuthResult.PENDING_PROFILE_SELECTION;
      } else {
         LOGGER.at(Level.WARNING).log("No game profiles found for this account");
         class="kw">return ServerAuthManager.AuthResult.FAILED;
      }
   }

   class="kw">private boolean refreshOAuthTokens() {
      class="kw">return this.refreshOAuthTokens(false);
   }

   class="kw">private boolean refreshOAuthTokens(boolean var1) {
      IAuthCredentialStore var2 = this.credentialStore.get();
      IAuthCredentialStore.OAuthTokens var3 = var2.getTokens();
      Instant var4 = var3.accessTokenExpiresAt();
      if (!var1 && var4 != null && !var4.isBefore(Instant.now().plusSeconds(300L))) {
         class="kw">return true;
      }

      String var5 = var3.refreshToken();
      if (var5 == null) {
         LOGGER.at(Level.WARNING).log("No refresh token present to refresh OAuth tokens");
         class="kw">return false;
      }

      for (int var6 = 1; var6 <= 3; var6++) {
         if (var6 > 1) {
            LOGGER.at(Level.INFO).log("Refreshing OAuth tokens (attempt %d/%d)...", var6, 3);
         } else {
            LOGGER.at(Level.INFO).log("Refreshing OAuth tokens...");
         }

         try {
            OAuthClient.TokenResponse var7 = this.oauthClient.refreshTokens(var5);
            if (var7 != null && var7.isSuccess()) {
               var2.setTokens(new IAuthCredentialStore.OAuthTokens(var7.accessToken(), var7.refreshToken(), Instant.now().plusSeconds(var7.expiresIn())));
               class="kw">return true;
            }

            LOGGER.at(Level.WARNING).log("OAuth token refresh rejected by server");
            class="kw">return false;
         } catch (InterruptedException var12) {
            Thread.currentThread().interrupt();
            LOGGER.at(Level.WARNING).log("OAuth token refresh interrupted");
            class="kw">return false;
         } catch (IOException var13) {
            if (var6 < 3) {
               long var8 = 30L * (1L << var6 - 1);
               LOGGER.at(Level.WARNING).log("OAuth token refresh IO error (attempt %d/%d), retrying in %d seconds...", var6, 3, var8);

               try {
                  Thread.sleep(Duration.ofSeconds(var8));
               } catch (InterruptedException var11) {
                  Thread.currentThread().interrupt();
                  LOGGER.at(Level.WARNING).log("OAuth token refresh retry interrupted");
                  class="kw">return false;
               }
            } else {
               LOGGER.at(Level.WARNING).log("OAuth token refresh failed after %d attempts due to IO errors", 3);
            }
         }
      }

      class="kw">return false;
   }

   @Nullable
   class="kw">private SessionServiceClient.GameProfile tryAutoSelectProfile(SessionServiceClient.GameProfile[] var1) {
      OptionSet var2 = Options.getOptionSet();
      if (var2 != null && var2.has(Options.OWNER_UUID)) {
         UUID var8 = (UUID)var2.valueOf(Options.OWNER_UUID);

         for (SessionServiceClient.GameProfile var12 : var1) {
            if (var12.uuid.equals(var8)) {
               LOGGER.at(Level.INFO).log("Selected profile from --owner-uuid: %s (%s)", var12.username, var12.uuid);
               class="kw">return var12;
            }
         }

         LOGGER.at(Level.WARNING).log("Specified --owner-uuid %s not found in available profiles", var8);
         class="kw">return null;
      } else {
         if (var1.length == 1) {
            LOGGER.at(Level.INFO).log("Auto-selected profile: %s (%s)", var1[0].username, var1[0].uuid);
            class="kw">return var1[0];
         }

         UUID var3 = this.credentialStore.get().getProfile();
         if (var3 != null) {
            for (SessionServiceClient.GameProfile var7 : var1) {
               if (var7.uuid.equals(var3)) {
                  LOGGER.at(Level.INFO).log("Auto-selected profile from storage: %s (%s)", var7.username, var7.uuid);
                  class="kw">return var7;
               }
            }
         }

         class="kw">return null;
      }
   }

   class="kw">private boolean completeAuthWithProfile(SessionServiceClient.GameProfile var1, ServerAuthManager.AuthMode var2) {
      SessionServiceClient.GameSessionResponse var3 = this.createGameSession(var1.uuid);
      if (var3 == null) {
         LOGGER.at(Level.WARNING).log("Failed to create game session");
         class="kw">return false;
      }

      this.gameSession.set(var3);
      this.authMode = var2;
      this.cancelActiveFlow = null;
      this.pendingProfiles = null;
      this.pendingAuthMode = null;
      Instant var4 = this.getEffectiveExpiry(var3);
      if (var4 != null) {
         this.setExpiryAndScheduleRefresh(var4);
      }

      LOGGER.at(Level.INFO).log("Authentication successful! Mode: %s", var2);
      TelemetryService var5 = TelemetryService.get();
      if (var5 != null) {
         var5.onAuthenticated();
      }

      class="kw">return true;
   }

   @Nullable
   class="kw">private SessionServiceClient.GameSessionResponse createGameSession(UUID var1) {
      if (this.sessionServiceClient == null) {
         this.sessionServiceClient = new SessionServiceClient("https://sessions.hytale.com");
      }

      if (!this.refreshOAuthTokens()) {
         LOGGER.at(Level.WARNING).log("OAuth token refresh for game session creation failed");
         class="kw">return null;
      }

      IAuthCredentialStore var2 = this.credentialStore.get();
      String var3 = var2.getTokens().accessToken();
      SessionServiceClient.GameSessionResponse var4 = this.sessionServiceClient.createGameSession(var3, var1);
      if (var4 == null) {
         LOGGER.at(Level.WARNING).log("Trying force refresh of OAuth tokens because game session creation failed");
         if (!this.refreshOAuthTokens(true)) {
            LOGGER.at(Level.WARNING).log("Force refresh failed");
            class="kw">return null;
         }

         var4 = this.sessionServiceClient.createGameSession(var3, var1);
         if (var4 == null) {
            LOGGER.at(Level.WARNING).log("Game session creation with force refreshed tokens failed");
            class="kw">return null;
         }
      }

      var2.setProfile(var1);
      class="kw">return var4;
   }

   class="kw">private void parseAndScheduleRefresh() {
      SessionServiceClient.GameSessionResponse var1 = this.gameSession.get();
      Instant var2 = this.getEffectiveExpiry(var1);
      if (var2 != null) {
         this.setExpiryAndScheduleRefresh(var2);
      }
   }

   @Nullable
   class="kw">private Instant getEffectiveExpiry(@Nullable SessionServiceClient.GameSessionResponse var1) {
      Instant var2 = var1 != null ? var1.getExpiresAtInstant() : null;
      Instant var3 = this.parseIdentityTokenExpiry(var1 != null ? var1.identityToken : this.getIdentityToken());
      if (var2 != null && var3 != null) {
         class="kw">return var2.isBefore(var3) ? var2 : var3;
      } else {
         class="kw">return var2 != null ? var2 : var3;
      }
   }

   @Nullable
   class="kw">private Instant parseIdentityTokenExpiry(@Nullable String var1) {
      if (var1 == null) {
         class="kw">return null;
      }

      try {
         Date var2 = SignedJWT.parse(var1).getJWTClaimsSet().getExpirationTime();
         if (var2 != null) {
            class="kw">return var2.toInstant();
         }
      } catch (Exception var3) {
         ((HytaleLogger.Api)LOGGER.at(Level.WARNING).withCause(var3)).log("Failed to parse identity token expiry");
      }

      class="kw">return null;
   }

   class="kw">private void setExpiryAndScheduleRefresh(@Nonnull Instant var1) {
      this.tokenExpiry = var1;
      if (this.refreshTask != null) {
         this.refreshTask.cancel(false);
      }

      long var2 = var1.getEpochSecond() - Instant.now().getEpochSecond();
      if (var2 > 300L) {
         long var4 = Math.max(var2 - 300L, 60L);
         LOGGER.at(Level.INFO).log("Token refresh scheduled in %d seconds", var4);
         this.refreshTask = this.refreshScheduler.schedule(() -> this.attemptSessionRefresh(1), var4, TimeUnit.SECONDS);
      }
   }

   class="kw">private void attemptSessionRefresh(int var1) {
      String var2 = this.getSessionToken();
      if (var2 != null) {
         if (var1 > 1) {
            LOGGER.at(Level.INFO).log("Refreshing game session with Session Service (attempt %d/%d)...", var1, 3);
         } else {
            LOGGER.at(Level.INFO).log("Refreshing game session with Session Service...");
         }

         try {
            if (this.refreshGameSession(var2)) {
               class="kw">return;
            }
         } catch (CompletionException var6) {
            if (var6.getCause() class="kw">instanceof IOException && var1 < 3) {
               long var4 = 30L * (1L << var1 - 1);
               LOGGER.at(Level.WARNING).log("Game session refresh IO error (attempt %d/%d), retrying in %d seconds...", var1, 3, var4);
               this.refreshTask = this.refreshScheduler.schedule(() -> this.attemptSessionRefresh(var1 + 1), var4, TimeUnit.SECONDS);
               class="kw">return;
            }

            if (var6.getCause() class="kw">instanceof IOException) {
               LOGGER.at(Level.WARNING).log("Game session refresh failed after %d attempts due to IO errors", 3);
            } else {
               LOGGER.at(Level.WARNING).log("Session Service refresh failed: %s", var6.getMessage());
            }
         } catch (Exception var7) {
            LOGGER.at(Level.WARNING).log("Session Service refresh failed: %s", var7.getMessage());
         }
      }

      LOGGER.at(Level.INFO).log("Game session refresh failed, attempting OAuth refresh...");
      if (!this.refreshGameSessionViaOAuth()) {
         LOGGER.at(Level.WARNING).log("All refresh attempts failed. Server may lose authentication.");
      }
   }

   class="kw">private boolean refreshGameSession(String var1) {
      if (this.sessionServiceClient == null) {
         this.sessionServiceClient = new SessionServiceClient("https://sessions.hytale.com");
      }

      SessionServiceClient.GameSessionResponse var2 = this.sessionServiceClient.refreshSessionAsync(var1).join();
      if (var2 == null) {
         LOGGER.at(Level.WARNING).log("Game session refresh rejected by server");
         class="kw">return false;
      }

      this.gameSession.set(var2);
      Instant var3 = this.getEffectiveExpiry(var2);
      if (var3 != null) {
         this.setExpiryAndScheduleRefresh(var3);
      }

      LOGGER.at(Level.INFO).log("Game session refresh successful");
      class="kw">return true;
   }

   class="kw">private boolean refreshGameSessionViaOAuth() {
      boolean var1 = class="kw">switch (this.authMode) {
         case OAUTH_BROWSER, OAUTH_DEVICE, OAUTH_STORE -> true;
         class="kw">default -> false;
      };
      if (!var1) {
         LOGGER.at(Level.WARNING).log("Refresh via OAuth not supported for current Auth Mode");
         class="kw">return false;
      }

      UUID var2 = this.credentialStore.get().getProfile();
      if (var2 == null) {
         LOGGER.at(Level.WARNING).log("No current profile, cannot refresh game session");
         class="kw">return false;
      }

      SessionServiceClient.GameSessionResponse var3 = this.createGameSession(var2);
      if (var3 == null) {
         LOGGER.at(Level.WARNING).log("Failed to create new game session");
         class="kw">return false;
      }

      this.gameSession.set(var3);
      Instant var4 = this.getEffectiveExpiry(var3);
      if (var4 != null) {
         this.setExpiryAndScheduleRefresh(var4);
      }

      LOGGER.at(Level.INFO).log("New game session created via OAuth refresh");
      class="kw">return true;
   }

   class="kw">static {
      AuthCredentialStoreProvider.CODEC.register("Memory", MemoryAuthCredentialStoreProvider.class, MemoryAuthCredentialStoreProvider.CODEC);
      AuthCredentialStoreProvider.CODEC
         .register(Priority.DEFAULT, "Encrypted", EncryptedAuthCredentialStoreProvider.class, EncryptedAuthCredentialStoreProvider.CODEC);
   }

   class="kw">public enum AuthMode {
      NONE,
      SINGLEPLAYER,
      EXTERNAL_SESSION,
      OAUTH_BROWSER,
      OAUTH_DEVICE,
      OAUTH_STORE;

      AuthMode() {
      }
   }

   class="kw">public enum AuthResult {
      SUCCESS,
      PENDING_PROFILE_SELECTION,
      FAILED;

      AuthResult() {
      }
   }
}