Skip to content
This repository has been archived by the owner on May 29, 2022. It is now read-only.

Authentication implementation. #321

Closed
wants to merge 36 commits into from
Closed
Show file tree
Hide file tree
Changes from 14 commits
Commits
Show all changes
36 commits
Select commit Hold shift + click to select a range
bf45491
auth
sadcenter Nov 17, 2021
ffe3898
auth
sadcenter Nov 17, 2021
8e7d64f
auth skull
sadcenter Nov 18, 2021
a18ed72
Improvements, Ignore case in map, 1 save/tick, fixed code-style, code…
sadcenter Nov 18, 2021
a777e51
Improvements, Ignore case in map, 1 save/tick, fixed code-style, code…
sadcenter Nov 18, 2021
c352589
Improvements, UUID storage deletion, Add config support, Import optim…
sadcenter Nov 18, 2021
f37d76e
Code style fixes
sadcenter Nov 18, 2021
2632d90
Removes useless abstract types
sadcenter Nov 18, 2021
70946b2
Package name change
sadcenter Nov 18, 2021
8c528c7
Cache storage fix.
sadcenter Nov 19, 2021
312c0a7
final commit
sadcenter Jan 3, 2022
d4e2b9b
method rename
sadcenter Jan 3, 2022
6513ee1
useless async deletion
sadcenter Jan 3, 2022
a8e01fe
comments fix
sadcenter Jan 9, 2022
af1db4e
Optimize imports, name changes, exception logging & useless utils del…
sadcenter Jan 18, 2022
fbfe538
minor code style fixes, using mojang api if ashcon's api fails
sadcenter Jan 19, 2022
c0cb883
auth
sadcenter Nov 17, 2021
a35e135
auth
sadcenter Nov 17, 2021
453bbce
auth skull
sadcenter Nov 18, 2021
278ac2c
Improvements, Ignore case in map, 1 save/tick, fixed code-style, code…
sadcenter Nov 18, 2021
976e51b
Improvements, Ignore case in map, 1 save/tick, fixed code-style, code…
sadcenter Nov 18, 2021
61189ca
Improvements, UUID storage deletion, Add config support, Import optim…
sadcenter Nov 18, 2021
d78218f
Code style fixes
sadcenter Nov 18, 2021
adb8667
Removes useless abstract types
sadcenter Nov 18, 2021
acbd54c
Package name change
sadcenter Nov 18, 2021
8f6ef1a
Cache storage fix.
sadcenter Nov 19, 2021
729cd4e
final commit
sadcenter Jan 3, 2022
6eb2219
method rename
sadcenter Jan 3, 2022
bfe289f
useless async deletion
sadcenter Jan 3, 2022
94f022d
comments fix
sadcenter Jan 9, 2022
70e5743
Optimize imports, name changes, exception logging & useless utils del…
sadcenter Jan 18, 2022
b006697
minor code style fixes, using mojang api if ashcon's api fails
sadcenter Jan 19, 2022
ac984c2
conflict resolved, config features
sadcenter Jan 20, 2022
d426c3a
Merge remote-tracking branch 'origin/auth-v2' into auth-v2
sadcenter Jan 20, 2022
f837b67
Merge branch 'master' into auth-v2
sadcenter Feb 23, 2022
6b46a29
duh
sadcenter Feb 23, 2022
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
16 changes: 8 additions & 8 deletions NachoSpigot-API/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -51,18 +51,18 @@
<version>17.0</version>
<scope>compile</scope>
</dependency>
<!-- &lt;!&ndash; https://mvnrepository.com/artifact/com.github.ben-manes.caffeine/caffeine &ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>com.github.ben-manes.caffeine</groupId>-->
<!-- <artifactId>caffeine</artifactId>-->
<!-- <version>2.8.5</version>-->
<!-- </dependency>-->
<!-- &lt;!&ndash; https://mvnrepository.com/artifact/com.github.ben-manes.caffeine/caffeine &ndash;&gt;-->
<!-- <dependency>-->
<!-- <groupId>com.github.ben-manes.caffeine</groupId>-->
<!-- <artifactId>caffeine</artifactId>-->
<!-- <version>2.8.5</version>-->
<!-- </dependency>-->

<!-- bundled with Minecraft, should be kept in sync -->
<dependency>
<groupId>com.google.code.gson</groupId>
<artifactId>gson</artifactId>
<version>2.2.4</version>
<version>2.2.4</version>
</dependency>
<dependency>
<groupId>org.avaje</groupId>
Expand Down Expand Up @@ -146,4 +146,4 @@
</plugin>
</plugins>
</build>
</project>
</project>
4 changes: 2 additions & 2 deletions NachoSpigot-Server/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -101,7 +101,7 @@
<version>2.17.1</version>
<scope>compile</scope>
</dependency>
<dependency>
<dependency>
<groupId>org.apache.logging.log4j</groupId>
<artifactId>log4j-api</artifactId>
<version>2.17.1</version>
Expand Down Expand Up @@ -328,4 +328,4 @@
</plugin>
</plugins>
</build>
</project>
</project>
Original file line number Diff line number Diff line change
@@ -0,0 +1,191 @@
package com.github.sadcenter.auth;

import com.github.sadcenter.auth.profile.NachoGameProfileRepository;
import com.github.sadcenter.auth.serializer.GameProfileSerializer;
import com.github.sadcenter.auth.session.NachoSessionService;
import com.github.sadcenter.auth.storage.CachedProfile;
import com.github.sadcenter.auth.storage.ProfileCache;
import com.github.sadcenter.auth.utils.ProfileUtil;
sadcenter marked this conversation as resolved.
Show resolved Hide resolved
import com.google.common.cache.CacheBuilder;
import com.google.common.cache.CacheLoader;
import com.google.common.cache.LoadingCache;
import com.google.gson.Gson;
import com.google.gson.GsonBuilder;
import com.mojang.authlib.*;
import com.mojang.authlib.minecraft.MinecraftSessionService;
import com.mojang.authlib.properties.PropertyMap;
import com.mojang.authlib.yggdrasil.YggdrasilAuthenticationService;
import com.mojang.authlib.yggdrasil.YggdrasilGameProfileRepository;
import net.minecraft.server.EntityPlayer;
import net.minecraft.server.MinecraftServer;
import org.apache.commons.io.Charsets;
import org.apache.commons.io.IOUtils;

import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.net.HttpURLConnection;
import java.net.MalformedURLException;
import java.net.URL;
import java.nio.charset.StandardCharsets;
import java.util.Map;
import java.util.UUID;
import java.util.concurrent.CompletableFuture;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;

public class NachoAuthenticatorService implements AuthenticationService {
sadcenter marked this conversation as resolved.
Show resolved Hide resolved

public static final Gson GSON = new GsonBuilder()
.registerTypeAdapter(GameProfile.class, new GameProfileSerializer())
.registerTypeAdapter(PropertyMap.class, new PropertyMap.Serializer())
.create();
private static final String API = "https://api.ashcon.app/mojang/v2/user/";
private static final String UUID_API = "https://api.ashcon.app/mojang/v2/uuid/";

private final LoadingCache<String, CompletableFuture<GameProfile>> gameProfileCache = CacheBuilder.newBuilder()
.expireAfterWrite(3, TimeUnit.HOURS)
.build(new CacheLoader<String, CompletableFuture<GameProfile>>() {
@Override
public CompletableFuture<GameProfile> load(String key) {
EntityPlayer player = MinecraftServer.getServer().getPlayerList().getPlayer(key);
CachedProfile cachedProfile = profileCache.getCachedProfile(key);
CompletableFuture<GameProfile> gameProfile = player == null ? get(API + key, GameProfile.class) : CompletableFuture.completedFuture(player.getProfile());

if (cachedProfile == null) {
gameProfile
.thenAccept(profile -> profileCache.putAndSave(profile.getName(), CachedProfile.fromGameProfile(profile)));
} else {
GameProfile cachedGameProfile = cachedProfile.toGameProfile(key);
gameProfile.thenAccept(profile -> {
if (!ProfileUtil.equals(profile, cachedGameProfile)) {
sadcenter marked this conversation as resolved.
Show resolved Hide resolved
profileCache.putAndSave(profile.getName(), CachedProfile.fromGameProfile(profile));
gameProfileCache.put(profile.getName(), gameProfile);
}
});

return CompletableFuture.completedFuture(cachedGameProfile);
}

return gameProfile;
}
});
private final ProfileCache profileCache = new ProfileCache();
private final YggdrasilAuthenticationService yggdrasilAuthenticationService;

public NachoAuthenticatorService(YggdrasilAuthenticationService yggdrasilAuthenticationService) {
this.yggdrasilAuthenticationService = yggdrasilAuthenticationService;
}

@Override
public GameProfileRepository createProfileRepository() {
return new NachoGameProfileRepository(this);
}

@Override
public UserAuthentication createUserAuthentication(Agent agent) {
return this.yggdrasilAuthenticationService.createUserAuthentication(agent);
}

@Override
public MinecraftSessionService createMinecraftSessionService() {
return new NachoSessionService(this);
}

public CompletableFuture<GameProfile> getProfile(String name) {
try {
return this.gameProfileCache.get(name);
} catch (ExecutionException e) {
e.printStackTrace();
}
return null;
}

public GameProfile getPresentProfile(String name) {
CompletableFuture<GameProfile> profile = this.gameProfileCache.getIfPresent(name);
return profile == null ? null : profile.join();
}

public CompletableFuture<UUID> getUuid(String name) {
return get(UUID_API + name, UUID.class);
}

public <T> CompletableFuture<T> get(String url, Class<T> type) {
return CompletableFuture.supplyAsync(() -> {
String result = null;
try {
result = this.fetchGet(url(url));
} catch (IOException exception) {
if (!(exception instanceof FileNotFoundException)) {
exception.printStackTrace();
}
}
return GSON.fromJson(result, type);
});
}

public <T> CompletableFuture<T> post(String url, Object content, Class<T> type) {
return CompletableFuture.supplyAsync(() -> {
try {
return GSON.fromJson(this.fetchPost(url(url), GSON.toJson(content)), type);
} catch (IOException exception) {
if (!(exception instanceof FileNotFoundException)) {
exception.printStackTrace();
}
}

return null;
});
}

public String fetchGet(URL url) throws IOException {
return IOUtils.toString(url, StandardCharsets.UTF_8);
}

public String fetchPost(URL url, String content) throws IOException {
HttpURLConnection connection = (HttpURLConnection) url.openConnection();

connection.setRequestMethod("application/json");
connection.setDoOutput(true);

try (OutputStream outputStream = connection.getOutputStream()) {
IOUtils.write(content.getBytes(Charsets.UTF_8), outputStream);
}

try (InputStream inputStream = connection.getInputStream()) {
return IOUtils.toString(inputStream);
}
}

public ProfileCache getProfileCache() {
return this.profileCache;
}

public static URL url(String url) {
try {
return new URL(url);
} catch (MalformedURLException e) {
e.printStackTrace();
return null;
}
}

public static String json(Map<?, ?> map) {
return GSON.toJson(map);
}

public static String query(Map<?, ?> map) {
StringBuilder builder = new StringBuilder();
AtomicBoolean firstPair = new AtomicBoolean(true);

map.forEach((a, b) -> {
builder.append((firstPair.get() ? "?" : "&") + a + "=" + b);

firstPair.set(false);
});

return builder.toString();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.github.sadcenter.auth.profile;

import com.github.sadcenter.auth.NachoAuthenticatorService;
import com.mojang.authlib.Agent;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.GameProfileRepository;
import com.mojang.authlib.ProfileLookupCallback;

public class NachoGameProfileRepository implements GameProfileRepository {

private final NachoAuthenticatorService authenticator;

public NachoGameProfileRepository(NachoAuthenticatorService authenticator) {
this.authenticator = authenticator;
}

@Override
public void findProfilesByNames(String[] names, Agent agent, ProfileLookupCallback profileLookupCallback) {
for (String name : names) {
GameProfile gameProfile = new GameProfile(null, name);

if (name == null || name.isEmpty()) {
profileLookupCallback.onProfileLookupFailed(gameProfile, new Exception("Name isn't provided in the proper way."));
continue;
}

this.authenticator.getUuid(name)
.thenApply(uuid -> new GameProfile(uuid, name))
.thenAccept(profileLookupCallback::onProfileLookupSucceeded)
.exceptionally(throwable -> {
profileLookupCallback.onProfileLookupFailed(gameProfile, (Exception) throwable);
return null;
});
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.github.sadcenter.auth.response;

import com.mojang.authlib.properties.PropertyMap;
import com.mojang.util.UUIDTypeAdapter;

import java.util.UUID;

public class HasJoinedServerResponse {

private final String id;
private final PropertyMap properties;

public HasJoinedServerResponse(String uuid, PropertyMap propertyMap) {
this.id = uuid;
this.properties = propertyMap;
}

public UUID getUuid() {
return UUIDTypeAdapter.fromString(this.id);
}

public PropertyMap getPropertyMap() {
return this.properties;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package com.github.sadcenter.auth.serializer;

import com.google.gson.*;
import com.mojang.authlib.GameProfile;
import com.mojang.authlib.properties.Property;
import com.mojang.authlib.properties.PropertyMap;

import java.lang.reflect.Type;
import java.util.UUID;

public class GameProfileSerializer implements JsonDeserializer<GameProfile> {

private static final String UUID_FIELD = "uuid";
private static final String NAME_FIELD = "username";

@Override
public GameProfile deserialize(JsonElement json, Type type, JsonDeserializationContext context) throws JsonParseException {
JsonObject jsonObject = json.getAsJsonObject();
UUID id = jsonObject.has(UUID_FIELD) ? UUID.fromString(jsonObject.getAsJsonPrimitive(UUID_FIELD).getAsString()) : null;
String name = jsonObject.has(NAME_FIELD) ? jsonObject.getAsJsonPrimitive(NAME_FIELD).getAsString() : null;
GameProfile gameProfile = new GameProfile(id, name);

if (jsonObject.has("textures")) {
JsonObject propertyJson = jsonObject.get("textures").getAsJsonObject().get("raw").getAsJsonObject();
Property property = new Property("textures", propertyJson.getAsJsonPrimitive("value").getAsString());
gameProfile.getProperties().put("textures", property);
}

return gameProfile;
}
}
Loading