Cloud Login
As of v6.3.0
UltiTools-API v6.3.0 uses the cloud-login lifecycle described here. The three commands and their console output are unchanged from earlier versions.
As of v6.3.0, the framework tracks a login differently internally. It does not change what an operator sees. /ulticloud login, /ulticloud logout, and /ulticloud status connect a server to UltiCloud for remote management through the UltiPanel dashboard.
Login sessions
A successful login creates a session. The session holds the credential, the WebSocket connection to UltiPanel, the reconnect schedule, and the token-refresh schedule as one unit.
A logout replaces the current session with a new, empty one. Everything the old session started stops mattering the moment it is replaced: an in-flight reconnect attempt, a credential refresh already in progress, a magic-link poll still waiting for the browser step. None of them can write a result back into a session that logout has already retired, no matter how long they were already running before the logout command reached the server.
This is why logout is unconditional. It tears down the connection, the reconnect schedule, and the refresh schedule even if the saved credential has already expired — an expired credential is exactly the case where an operator most needs logout to actually stop things, and the previous implementation could leave a connection open past the point where the credential backing it was no longer valid.
Operator commands
The three commands and their console output are unchanged:
| Command | What it does |
|---|---|
/ulticloud login | Requests a magic link, prints it to the console, waits for the browser step to complete |
/ulticloud logout | Tears down the connection and clears the saved credential |
/ulticloud status | Reports whether the server is currently connected, and to which account |
An operator who ran these commands on an earlier version sees the same messages. Nothing about the session change is visible from the console.
Module API compatibility
CloudAuthManager, the class implementing this lifecycle, is internal to the framework (@ApiStatus.Internal) and was never part of the public API surface module authors write against. The session-lifecycle change requires no module API migration; server-associated link requests must follow the authentication requirements below.
Authenticated requests on a module's behalf (as of v6.3.0)
The framework never gives a module the server's UltiCloud credential. As of v6.3.0 a module that needs UltiCloud to recognise a request as coming from this server asks the framework to send it: com.ultikits.ultitools.utils.UltiCloudRequests attaches the credential to one connection it opens itself and returns only the HTTP status and response body. UltiLogin's /panel web login link is the first module that uses it.
The helper sends exactly two requests:
| Method | Path | Call |
|---|---|---|
POST | /auth/magic-link | UltiCloudRequests.post("/auth/magic-link", jsonBody) |
GET | /auth/magic-link/poll | UltiCloudRequests.get("/auth/magic-link/poll", query) |
Each call returns a UltiCloudRequests.Result. Its getOutcome() is one of four values:
| Outcome | Meaning | Request made |
|---|---|---|
OK | An HTTP exchange completed, whatever its status. Read getStatusCode() and getBody(). | yes |
NOT_CONNECTED | The server has no valid UltiCloud session: /ulticloud login was never run, /ulticloud logout was run, or the saved credential has expired. | no |
PATH_NOT_ALLOWED | The method and path are not one of the two above. | no |
IO_ERROR | The exchange could not complete (connection refused, timeout, read failure), or no UltiCloud API address is configured. | attempted, or none when no address is configured |
The path must match exactly. Another path, an allowed path with the other method, or a path that contains .., //, \, %, ? or # returns PATH_NOT_ALLOWED. Query parameters go only through the query map, which the helper URL-encodes as UTF-8. The list grows only through a documented change in a framework release; a module cannot add to it.
Handle NOT_CONNECTED as an ordinary state: an operator may run the server without UltiCloud. A module must not retry link creation anonymously; ask the server owner to connect the server with /ulticloud login before using the server-associated /panel link.
Server-owner authentication (as of v6.3.0)
Authenticated link creation
As of v6.3.0, only an authenticated server owner can create a server-associated /panel magic link through the plugin. There is no anonymous fallback, no grace period and no polling by player UUID.
POST /auth/magic-link requires the server owner's bearer credential, attached by UltiCloudRequests. For an otherwise valid request, a missing or invalid credential returns 401 invalid_credential; an authenticated account that does not own the server returns 403 not_server_owner. Neither refusal creates a link. This requirement concerns the server owner authorizing the plugin's request, not a requirement for every player opening a link to own the server.
Poll the created request with GET /auth/magic-link/poll?requestId=... through the helper, using the same server-owner authentication. The old playerUuid-only poll is not supported; omitting requestId returns 400 invalid_request. The caller cannot read requests for a server that the authenticated account does not own.
Ordinary website account login, browser magic-link verification and the in-game /login command remain unchanged by the removal of anonymous plugin creation and polling. These are separate from /ulticloud login, which connects the server to UltiCloud.
Both methods block on network I/O, with a 10-second connect timeout and a 30-second read timeout, and throw IllegalStateException when called on the server's primary thread. Call them from an asynchronous task and return to the primary thread to act on the result.
The credential stays inside the framework. It is not returned, the framework never logs it, and it is not placed in an exception message that reaches your module or in Result.toString(), and no public member of the helper is typed TokenEntity. The helper does not follow redirects, so a Location header can never make it send the credential to another host; a 3xx response comes back as an OK result with that status. The helper writes no file and no log line.
One limit lies outside the framework: the JDK's own HTTP client logger, sun.net.www.protocol.http.HttpURLConnection, prints request headers, the credential included, when it is set to FINE or lower. Its default level does not. Do not enable that logger on a production server.
Ling Bao