Authentication
1. Add the Dependency
Section titled “1. Add the Dependency”Add minified-auth to your build.gradle file:
repositories { mavenCentral() maven { url 'https://jitpack.io' }}
dependencies { implementation 'com.github.dervarex.minified:minified-auth:3.0.0'}2. Initialize the Auth Manager
Section titled “2. Initialize the Auth Manager”Before using authentication, initialize the AuthManager with a directory where sessions can be stored.
Path authDirectory = Path.of("<auth-directory>");
AuthManager.init(authDirectory);val authDirectory = Path.of("<auth-directory>")
AuthManager.init(authDirectory)3. Check for a Saved Session
Section titled “3. Check for a Saved Session”Before starting a new login, check whether a saved session already exists.
User user = null;
if (AuthManager.hasSessionSaved()) { user = AuthManager.loginWithSavedSession();}var user: User? = null
if (AuthManager.hasSessionSaved()) { user = AuthManager.loginWithSavedSession()}If the saved session is valid, loginWithSavedSession() returns the authenticated user.
4. Start Device Code Login
Section titled “4. Start Device Code Login”If no saved session exists, start the Microsoft device code login:
AuthManager.startDeviceCodeLoginAsync();AuthManager.startDeviceCodeLoginAsync()The login runs asynchronously. You can access its current state using:
LoginState state = AuthManager.getLoginState();val state = AuthManager.getLoginState()Wait until the user code becomes available:
while (true) { LoginState state = AuthManager.getLoginState();
if (state.userCode != null && state.verificationUri != null) { System.out.println( "Go to " + state.verificationUri + " and enter code " + state.userCode ); break; }
Thread.sleep(500);}while (true) { val state = AuthManager.getLoginState()
if (state.userCode != null && state.verificationUri != null) { println( "Go to ${state.verificationUri} and enter code ${state.userCode}" ) break }
Thread.sleep(500)}The user can then open the verification URL and enter the displayed code.
5. Wait for Authentication
Section titled “5. Wait for Authentication”After displaying the login instructions, wait until authentication succeeds or fails:
while (true) { LoginState state = AuthManager.getLoginState();
if (state.status == AuthManager.LoginStatus.SUCCESS) { break; }
if (state.status == AuthManager.LoginStatus.ERROR) { throw new IllegalStateException(state.message); }
Thread.sleep(500);}while (true) { val state = AuthManager.getLoginState()
if (state.status == AuthManager.LoginStatus.SUCCESS) { break }
if (state.status == AuthManager.LoginStatus.ERROR) { throw IllegalStateException(state.message) }
Thread.sleep(500)}After a successful login, get the authenticated user:
User user = AuthManager.getUser();
System.out.println( "Logged in as " + user.username() + " (" + user.uuid() + ")");val user = AuthManager.getUser()
println( "Logged in as ${user.username} (${user.uuid})")6. Listening for State Changes
Section titled “6. Listening for State Changes”Polling getLoginState() in a loop works, but AuthManager also supports pushing state changes directly to a listener - useful if your UI framework already has a callback/observer pattern instead of a poll loop:
LoginStateChangeListener listener = newState -> { System.out.println("New login state: " + newState.status);};
AuthManager.addStateChangeListener(listener);val listener = LoginStateChangeListener { newState -> println("New login state: ${newState.status}")}
AuthManager.addStateChangeListener(listener)The listener is invoked with the same LoginState snapshot every time AuthManager updates it - during login(), startDeviceCodeLoginAsync(), and resetLoginState(). Remove it once you no longer need updates (e.g. when the login UI closes), the same way you would unsubscribe from any other event in Minified:
AuthManager.removeStateChangeListener(listener);AuthManager.removeStateChangeListener(listener)Complete Example
Section titled “Complete Example”Path authDirectory = Path.of("<auth-directory>");
AuthManager.init(authDirectory);
User user = null;
if (AuthManager.hasSessionSaved()) { user = AuthManager.loginWithSavedSession();}
if (user == null) { AuthManager.startDeviceCodeLoginAsync();
boolean loginInstructionsShown = false;
while (true) { LoginState state = AuthManager.getLoginState();
if (!loginInstructionsShown && state.userCode != null && state.verificationUri != null) {
System.out.println( "Go to " + state.verificationUri + " and enter code " + state.userCode );
loginInstructionsShown = true; }
if (state.status == AuthManager.LoginStatus.SUCCESS) { user = AuthManager.getUser(); break; }
if (state.status == AuthManager.LoginStatus.ERROR) { throw new IllegalStateException(state.message); }
Thread.sleep(500); }}
System.out.println( "Logged in as " + user.username() + " (" + user.uuid() + ")");val authDirectory = Path.of("<auth-directory>")
AuthManager.init(authDirectory)
var user: User? = null
if (AuthManager.hasSessionSaved()) { user = AuthManager.loginWithSavedSession()}
if (user == null) { AuthManager.startDeviceCodeLoginAsync()
var loginInstructionsShown = false
while (true) { val state = AuthManager.getLoginState()
if (!loginInstructionsShown && state.userCode != null && state.verificationUri != null ) { println( "Go to ${state.verificationUri} and enter code ${state.userCode}" )
loginInstructionsShown = true }
if (state.status == AuthManager.LoginStatus.SUCCESS) { user = AuthManager.getUser() break }
if (state.status == AuthManager.LoginStatus.ERROR) { throw IllegalStateException(state.message) }
Thread.sleep(500) }}
println( "Logged in as ${user!!.username} (${user.uuid})")The returned User can now be passed directly to Minified Launch:
Launcher.launchMinecraft( user, config);Launcher.launchMinecraft( user, config)Encryption Events
Section titled “Encryption Events”Sessions are stored encrypted on disk using a master key. AuthManager.init() accepts an EventBus that these events are dispatched on, see CreateMasterKeyEvent, SaveEncryptedSessionEvent and LoadEncryptedSessionEvent in the Events section for details on exactly when each one fires.