copy()->addSeconds($ttl); $payload = [ 'iss' => config('jwt.issuer', config('app.name')), 'sub' => (string) $user->id, 'iat' => $issuedAt->timestamp, 'nbf' => $issuedAt->timestamp, 'exp' => $expiresAt->timestamp, 'character_id' => $user->character_id, 'character_name' => $user->character_name, 'character_owner_hash' => $user->character_owner_hash, //Critical for privileg-aware JWT invalidation/regeneration 'privileges_version' => (int) $user->privileges_version, ]; $jwt = JWT::encode($payload, $secret, 'HS256'); if ($save) { $user->forceFill([ 'user_jwt' => $jwt, 'user_jwt_issued_at' => $issuedAt, 'user_jwt_expires_at' => $expiresAt, ])->save(); } return $jwt; } public function refreshIfNeeded(User $user): string { $refreshInterval = (int) config('jwt.refresh_interval', 3600); if ($user->jwtNeedsRefresh($refreshInterval)) { return $this->issue($user); } return $user->user_jwt; } public function decode(string $token): object { $secret = config('jwt.secret'); if (! $secret) { throw new RuntimeException('JWT secret is not configured.'); } return JWT::decode($token, new Key($secret, 'HS256')); } }