Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
d80213c
feat: consume trusted Bedrock identity sessions
robinbraemer Jul 15, 2026
3491568
no-mistakes(review): Harden Bedrock identity admission and claim binding
robinbraemer Jul 15, 2026
c06f5b8
no-mistakes(review): Hide raw Bedrock envelopes behind admission claims
robinbraemer Jul 15, 2026
3dfb431
no-mistakes(review): Harden private Bedrock identity admission and pa…
robinbraemer Jul 15, 2026
97395b6
no-mistakes(review): Clean passthrough channels and bound JSON errors
robinbraemer Jul 15, 2026
790428b
no-mistakes(document): Refresh Bedrock identity docs and lint
robinbraemer Jul 15, 2026
ddefa99
fix: harden Bedrock identity readiness
robinbraemer Jul 15, 2026
d48fa66
no-mistakes(review): Harden Bedrock identity readiness, verification,…
robinbraemer Jul 15, 2026
c56613f
no-mistakes(review): Fix Bedrock metadata timing and cleanup lifecycle
robinbraemer Jul 15, 2026
0ac61d4
no-mistakes(review): Restore ConnectPlatform and Spigot constructor c…
robinbraemer Jul 15, 2026
845fd06
no-mistakes(document): Refresh Bedrock identity documentation
robinbraemer Jul 15, 2026
59e0349
fix: harden Bedrock identity configuration
robinbraemer Jul 15, 2026
b2d6c08
test: use HTTPS metadata fixture
robinbraemer Jul 15, 2026
992bfd2
no-mistakes(review): fix Bedrock identity contracts and constructor c…
robinbraemer Jul 15, 2026
a07d509
no-mistakes(review): fix legacy Bedrock admission ownership and resto…
robinbraemer Jul 15, 2026
702b08a
no-mistakes(review): hide Bedrock registry behind one-shot admission …
robinbraemer Jul 15, 2026
360cca2
no-mistakes(review): isolate staged Bedrock admission from player ser…
robinbraemer Jul 15, 2026
c8adff7
no-mistakes(review): avoid blocking registry during Bedrock verification
robinbraemer Jul 15, 2026
2164bd6
no-mistakes(document): Document hardened Bedrock identity configuration
robinbraemer Jul 15, 2026
473676e
fix(core): isolate bedrock admission sessions
robinbraemer Jul 15, 2026
aef250c
no-mistakes(review): Harden Bedrock admission generation and claims i…
robinbraemer Jul 15, 2026
4941293
no-mistakes(review): Tokenize all admissions and clean displaced players
robinbraemer Jul 15, 2026
a3411e1
no-mistakes(review): Synchronize displaced-admission test on coordina…
robinbraemer Jul 15, 2026
0545ab5
no-mistakes(document): Verify Bedrock documentation and lint
robinbraemer Jul 15, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
17 changes: 16 additions & 1 deletion api/src/main/java/com/minekube/connect/api/ConnectApi.java
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,9 @@
package com.minekube.connect.api;

import com.minekube.connect.api.player.ConnectPlayer;
import com.minekube.connect.api.player.bedrock.BedrockIdentityClaims;
import java.util.Collection;
import java.util.Optional;
import java.util.UUID;

public interface ConnectApi {
Expand All @@ -48,7 +50,7 @@ static ConnectApi getInstance() {
int getPlayerCount();

/**
* Method to determine if the given <b>online</b> player is a bedrock player
* Determines whether the given <b>online</b> player is tunneled by Connect.
*
* @param uuid The uuid of the <b>online</b> player
* @return true if the given <b>online</b> player is tunneled by Connect
Expand All @@ -62,4 +64,17 @@ static ConnectApi getInstance() {
* @return ConnectPlayer if the given uuid is a player tunneled by Connect
*/
ConnectPlayer getPlayer(UUID uuid);

/**
* Returns claims from a Moxy-signed Bedrock identity envelope that Connect verified for the
* player's current session. Java, disabled, warn-failed, rejected, and disconnected sessions
* return empty. The claims' XUID accessor is sensitive trusted identity material intended only
* for in-process authorization; callers must not log, serialize, forward, or expose it.
*
* @param player a player object returned by this API for the currently connected session
* @return immutable verified claims, or empty when no verified claims exist for the session
*/
default Optional<BedrockIdentityClaims> getVerifiedBedrockIdentity(ConnectPlayer player) {
return Optional.empty();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,11 @@
import java.time.Instant;
import lombok.Value;

/**
* Immutable claims decoded from a signed Bedrock identity envelope. Claims returned by
* {@code ConnectApi#getVerifiedBedrockIdentity} have passed Connect's admission checks and are
* bound to the current Connect session.
*/
@Value
public class BedrockIdentityClaims {
String issuer;
Expand All @@ -13,12 +18,155 @@ public class BedrockIdentityClaims {
String protocol;
String bedrockAuthPolicy;
String principalType;
String bedrockXuid;
transient String bedrockXuid;
String bedrockUsername;
String bedrockDerivedUuid;
String linkedJavaUuid;
String linkedJavaName;
Instant issuedAt;
Instant expiresAt;
String nonce;

/**
* Creates an immutable Bedrock identity claim set.
*
* @param issuer signed envelope issuer
* @param endpointId signed Connect endpoint ID
* @param endpointName signed Connect endpoint name
* @param orgId signed Connect organization ID
* @param sessionId signed Connect session ID
* @param protocol signed client protocol
* @param bedrockAuthPolicy signed Bedrock authentication policy
* @param principalType signed principal type
* @param bedrockXuid canonical Bedrock XUID
* @param bedrockUsername Bedrock username
* @param bedrockDerivedUuid deterministic UUID derived from the XUID
* @param linkedJavaUuid linked Java UUID, or {@code null} for an unlinked principal
* @param linkedJavaName linked Java name, or {@code null} for an unlinked principal
* @param issuedAt envelope issue time
* @param expiresAt envelope expiration time
*/
public BedrockIdentityClaims(
String issuer,
String endpointId,
String endpointName,
String orgId,
String sessionId,
String protocol,
String bedrockAuthPolicy,
String principalType,
String bedrockXuid,
String bedrockUsername,
String bedrockDerivedUuid,
String linkedJavaUuid,
String linkedJavaName,
Instant issuedAt,
Instant expiresAt) {
this.issuer = issuer;
this.endpointId = endpointId;
this.endpointName = endpointName;
this.orgId = orgId;
this.sessionId = sessionId;
this.protocol = protocol;
this.bedrockAuthPolicy = bedrockAuthPolicy;
this.principalType = principalType;
this.bedrockXuid = bedrockXuid;
this.bedrockUsername = bedrockUsername;
this.bedrockDerivedUuid = bedrockDerivedUuid;
this.linkedJavaUuid = linkedJavaUuid;
this.linkedJavaName = linkedJavaName;
this.issuedAt = issuedAt;
this.expiresAt = expiresAt;
}

/**
* @deprecated The nonce is private replay-protection data and is deliberately not retained.
* This constructor remains only for source compatibility; {@code ignoredNonce} is ignored.
*
* @param issuer signed envelope issuer
* @param endpointId signed Connect endpoint ID
* @param endpointName signed Connect endpoint name
* @param orgId signed Connect organization ID
* @param sessionId signed Connect session ID
* @param protocol signed client protocol
* @param bedrockAuthPolicy signed Bedrock authentication policy
* @param principalType signed principal type
* @param bedrockXuid canonical Bedrock XUID
* @param bedrockUsername Bedrock username
* @param bedrockDerivedUuid deterministic UUID derived from the XUID
* @param linkedJavaUuid linked Java UUID, or {@code null} for an unlinked principal
* @param linkedJavaName linked Java name, or {@code null} for an unlinked principal
* @param issuedAt envelope issue time
* @param expiresAt envelope expiration time
* @param ignoredNonce ignored legacy nonce
*/
@Deprecated
public BedrockIdentityClaims(
String issuer,
String endpointId,
String endpointName,
String orgId,
String sessionId,
String protocol,
String bedrockAuthPolicy,
String principalType,
String bedrockXuid,
String bedrockUsername,
String bedrockDerivedUuid,
String linkedJavaUuid,
String linkedJavaName,
Instant issuedAt,
Instant expiresAt,
String ignoredNonce) {
this(
issuer,
endpointId,
endpointName,
orgId,
sessionId,
protocol,
bedrockAuthPolicy,
principalType,
bedrockXuid,
bedrockUsername,
bedrockDerivedUuid,
linkedJavaUuid,
linkedJavaName,
issuedAt,
expiresAt);
}

/**
* @deprecated The nonce is private replay-protection data and is no longer exposed.
*
* @return always {@code null}
*/
@Deprecated
public String getNonce() {
return null;
}

/**
* Returns the sensitive, trusted Bedrock XUID for in-process plugin authorization only.
* Do not log, serialize, forward, or expose this value to untrusted consumers.
*
* @return canonical XUID from the verified signed envelope
*/
@java.beans.Transient
public String getBedrockXuid() {
return bedrockXuid;
}

@Override
public String toString() {
return "BedrockIdentityClaims(issuer=" + issuer
+ ", endpointId=" + endpointId
+ ", orgId=" + orgId
+ ", sessionId=" + sessionId
+ ", protocol=" + protocol
+ ", bedrockAuthPolicy=" + bedrockAuthPolicy
+ ", principalType=" + principalType
+ ", issuedAt=" + issuedAt
+ ", expiresAt=" + expiresAt
+ ")";
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.minekube.connect.api.player.bedrock;

import com.minekube.connect.api.player.GameProfile;
import java.util.Collections;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

/** Utilities for removing private Bedrock identity admission properties from public profiles. */
public final class BedrockIdentityProfiles {
/** Private transport property carrying endpoint and organization scope for legacy Watch. */
public static final String SCOPE_PROPERTY_NAME = "minekube:bedrock_identity_scope";

private BedrockIdentityProfiles() {
}

/**
* Returns a profile without the signed identity envelope or transport scope property.
*
* @param profile the profile to sanitize
* @return {@code profile} when it contains no private properties, otherwise a sanitized copy
*/
public static GameProfile withoutEnvelope(GameProfile profile) {
Objects.requireNonNull(profile, "profile");
List<GameProfile.Property> properties = profile.getProperties().stream()
.filter(BedrockIdentityProfiles::isPublic)
.collect(Collectors.toList());
if (properties.size() == profile.getProperties().size()) {
return profile;
}
return new GameProfile(
profile.getUsername(),
profile.getUniqueId(),
Collections.unmodifiableList(properties));
}

/**
* Determines whether a profile property may be exposed outside identity admission.
*
* @param property the property to classify
* @return true unless the property is reserved for Bedrock identity admission
*/
public static boolean isPublic(GameProfile.Property property) {
return !BedrockIdentityVerifier.PROPERTY_NAME.equals(property.getName()) &&
!SCOPE_PROPERTY_NAME.equals(property.getName());
}
}
Loading
Loading