├── .travis.yml
├── LICENSE
├── README.md
├── pom.xml
├── renovate.json
├── settings.xml
└── src
└── main
├── java
└── org
│ └── inventivetalent
│ └── mcauth
│ ├── DatabaseClient.java
│ ├── MCAuthServer.java
│ └── data
│ ├── Request.java
│ └── Status.java
└── resources
├── bungee.yml
└── config.yml
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: java
2 | jdk:
3 | - oraclejdk8
4 |
5 | script: "mvn deploy --settings settings.xml"
6 | env:
7 | global:
8 | - secure: "gFrKLMjIuyw0kaMAiHaYeuwTVbhboSTVyzsN152MNNwCydjXKsgIAuFRngRtQvXcXAfInBp+2nlI/NuyfQ7Ztfwos9SmsI4dX0cXYtnmICfRCIDOEGHzRnoJAXb9D7ASa3JvipHCOXCQ+xi68XecsfihJy771u7C4OOs+Lz/0NHrEab7TFmiDkCEFWSTyB78+pfLtkZnKzbArkQHc7MVQBiiPs+wObYd1mZpktCwiBlx/zDfFy+WLV+R7iQZC0mVSzWdK4YZRdIpFg3gTyj+QF9pDSE096HqeBMDe1XNWiLNz7DJqGkp5yMtPjUp2IZnTGqj3Xrt7f388ftBFZqxpdITNn4JmdS9XH3KOBMp7js1zkS8sLN6C5aFPFrhOaPnmPLibUThomFUb9OuXVr7WGcHq+nZvZYuiAU51nJJOGjCy4POtc1qan3OZ+3ax7iiuLmh1ZmfJYwT3T4KuPztjzVQMm6xX9aAVYtQ/nKhuFioKEf/7bNOGuoSAjQaRqjmCyQxx2ZvC7ukX+wS7YhDIQ4xxsKnZw1J5uQMPUfyZjeSEIilruNlIDxwZfifGxPwQkQibLA+XrQ7x0v472fX3mwENyP/Hm97ZxNpgslU6eTFErYZXcyampFrWqQPBCpMbI4GTdHJWAasz9aR9kzSafbJCYQiUvMrOFEGClcK4HI="
9 | - secure: "ptv3CRQyQ2xiJ4utQLPPKHCD4TiXsXWJ1WK87AcDw5mbO8w4YixeZyGEK3H5lLjmPGYdLKSQIPN+0ouTm/kkmI2Bjf0MJtZtmvpTs1TYCVGJ8GdyUGyIB3treRlM/HEfyV68fCRzPDdcpDq3IUShTi+Lu8V8noueL8TK9VJWL2eWjdprw8Tg724vZpOheXwneYRSUKJyrWyPs9M/gAtCD655AqpDOBtj3aHL1OvSTVNaqFtD1mcxXezgjMBVgXAzbEFRUm5e+hpTmAr69kd1WmNhBHNVRxOMZN7D0TqMrieyd3vinuvHZz3S3K9aXa4wYHWWDdSOLpWxqUCg0bADeTDWmkDo3rHUMcvgVseB0R/PQFIi4woQqYyjEE3tN4RzCrPHUyCb6/5qy3gzcAXm6JrtBIwyHqLCidR9+HZp2wPBUlHlMr+8Ezwn3sJgwFd76OUtKEhcYLbPzpPYrm3uRG7mjvYAAk6mkn8taRmEZXhx2BL+zDdI9GB1rYyicb5rk+jyaJ5KGlTPNNUrZxMPcGHC9dXL7kTUhajgzOKkt8Hi+frTXwRSu1jyk2wBUXNQUOMmiK23SBLMCnlVMo6jCUd7YJv30czzdgtBvrvnN3iuLrxMpXr+Y43OCT4BNCwFzAYH9hWHDTpIN2YWunvBiOm/kXNrTkyFUPqesapvK6c="
10 | deploy:
11 | provider: releases
12 | api_key:
13 | secure: "BXT0k69K89PL50YyoTOVqDzB/HiRuXb5Em/vAEpZmNF3ldZTj1B/j/GsqI8RHQdP5S9yDapO/s0ets4muOx3Sbsb+qoB0J79wUhRswxhlJMV/6U3JuNs1BcA6cc4elkuIo7d2/PTQr9WV8ivlodfTJdUdDP6ZFcjLbNX9nvkZHOOXxWoSBci9qJ/ZX1Qx7ASa07a2iBjTjr4XWDKN2zqLynJ0mQG/oBWcPa7XJOVDMxPgWssXEA99i5y4JGhQNW3LatS8MC4HBJtqXQwKYCoXxTtG+JfwzeQe26GYdRqLKr5xKy7AyGjCWtUe9C1cxy3npYhDOYsEpvoXJ45DY02wwE6AbniYb5e7rHYRpglFlBsFtlcoJx5M9beuxcfmDHeLbtCb2CDHau5zffxqreO6WBVKAaBomyoW9R/tdeBsVQAOPOy9CozIAJBJGZm6XW8Id6Ic9w/OPC56nJgxjk09cLc9ltec/oH4HCBZKp8w9hcQpjCCb/oHmsaBjBfgiFgFx7afpsjtZGdQp8A+l6wSE5iuMBW3TaPc0ekcqjU3l2XViwEgSZPL+slvgSYAWqLPj5wwE5TuieIjmLd3bf/hF9kLVqtsWd06tB1S2VWgZ+ilpertQUAynG4ErxuQ/5ndGqpY4ziK6lXMN1QbF5i0kU6CSQT8eRfRX1XuJfLeW0="
14 | file_glob: true
15 | file:
16 | - "target/mcauth-server-*.jar"
17 | skip_cleanup: true
18 | on:
19 | tags: true
20 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Copyright (c) 2017 Haylee Schäfer
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy
6 | of this software and associated documentation files (the "Software"), to deal
7 | in the Software without restriction, including without limitation the rights
8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9 | copies of the Software, and to permit persons to whom the Software is
10 | furnished to do so, subject to the following conditions:
11 |
12 | The above copyright notice and this permission notice shall be included in all
13 | copies or substantial portions of the Software.
14 |
15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21 | SOFTWARE.
22 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # MinecraftID Server
2 |
3 | [](https://travis-ci.org/MC-Auth/MinecraftID-Server)
4 |
--------------------------------------------------------------------------------
/pom.xml:
--------------------------------------------------------------------------------
1 |
2 |
5 | 4.0.0
6 |
7 | org.inventivetalent
8 | mcauth-server
9 | 1.1.0-SNAPSHOT
10 |
11 |
12 |
13 |
14 | src/main/java
15 |
16 | **/*.java
17 |
18 |
19 |
20 | src/main/resources
21 | true
22 |
23 | bungee.yml
24 | config.yml
25 |
26 |
27 |
28 |
29 |
30 | maven-compiler-plugin
31 | 3.3
32 |
33 | 1.8
34 | 1.8
35 |
36 |
37 |
38 | org.apache.maven.plugins
39 | maven-shade-plugin
40 | 2.4
41 |
42 |
43 | package
44 |
45 | shade
46 |
47 |
48 |
49 |
50 | org.inventivetalent:mcauth**
51 | org.mongodb:**
52 | org.jongo:**
53 | com.fasterxml.jackson.core:**
54 | de.undercouch:**
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 | net.md-5
67 | bungeecord-api
68 | 1.10-SNAPSHOT
69 |
70 |
71 | net.md-5
72 | bungeecord-event
73 | 1.10-SNAPSHOT
74 |
75 |
76 | net.md-5
77 | bungeecord-config
78 | 1.10-SNAPSHOT
79 |
80 |
81 |
82 | org.mongodb
83 | mongodb-driver
84 | 3.12.0
85 |
86 |
87 | org.mongodb
88 | bson
89 | 3.12.0
90 |
91 |
92 | org.jongo
93 | jongo
94 | 1.3.0
95 |
96 |
97 | com.google.code.gson
98 | gson
99 | 2.6.2
100 |
101 |
102 |
103 | org.projectlombok
104 | lombok
105 | 1.16.8
106 |
107 |
108 |
109 |
110 |
111 | bungeecord-repo
112 | https://oss.sonatype.org/content/repositories/snapshots
113 |
114 |
115 |
116 |
117 |
118 | sonatype-nexus-releases
119 | http://repo.inventivetalent.org/content/repositories/releases/
120 |
121 |
122 | sonatype-nexus-snapshots
123 | http://repo.inventivetalent.org/content/repositories/snapshots/
124 |
125 |
126 |
127 |
128 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": [
3 | "config:base"
4 | ],
5 | "maven": {
6 | "enabled": true
7 | },
8 | "ignoreUnstable": false,
9 | "hostRules": [{
10 | "hostType": "maven",
11 | "endpoint": "https://repo.inventivetalent.org/content/groups/public/"
12 | }]
13 | }
14 |
--------------------------------------------------------------------------------
/settings.xml:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | sonatype-nexus-releases
5 | ${env.CI_DEPLOY_USERNAME}
6 | ${env.CI_DEPLOY_PASSWORD}
7 |
8 |
9 | sonatype-nexus-snapshots
10 | ${env.CI_DEPLOY_USERNAME}
11 | ${env.CI_DEPLOY_PASSWORD}
12 |
13 |
14 |
15 |
--------------------------------------------------------------------------------
/src/main/java/org/inventivetalent/mcauth/DatabaseClient.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mcauth;
2 |
3 | import com.mongodb.MongoClient;
4 | import com.mongodb.MongoClientOptions;
5 | import com.mongodb.MongoCredential;
6 | import com.mongodb.ServerAddress;
7 | import com.mongodb.client.MongoDatabase;
8 | import org.jongo.Jongo;
9 |
10 | import java.io.IOException;
11 | import java.util.Collections;
12 | import java.util.List;
13 |
14 | public class DatabaseClient {
15 |
16 | private List hosts;
17 |
18 | private String dbName;
19 | private String host;
20 | private int port;
21 | private MongoCredential credential;
22 |
23 | private MongoClient mongoClient;
24 | private MongoDatabase mongoDatabase;
25 |
26 | public DatabaseClient(String dbName, List hosts, String user, char[] pass, String authDatabase) {
27 | this.dbName = dbName;
28 | this.hosts = hosts;
29 | this.credential = MongoCredential.createScramSha1Credential(user, authDatabase, pass);
30 | }
31 |
32 | public DatabaseClient(String dbName, String host, int port, String user, char[] pass, String authDatabase) {
33 | this.dbName = dbName;
34 | this.host = host;
35 | this.port = port;
36 | this.credential = MongoCredential.createScramSha1Credential(user, authDatabase, pass);
37 | }
38 |
39 | public int collectionCount() {
40 | int c = 0;
41 | for (String ignored : db().listCollectionNames()) {
42 | c++;
43 | }
44 | return c;
45 | }
46 |
47 | public ServerAddress connect(int timeout) throws IOException {
48 | if (mongoClient == null) {
49 | if (hosts != null && !hosts.isEmpty()) {
50 | System.out.println("Connecting to MongoDB...");
51 | mongoClient = new MongoClient(this.hosts, this.credential, MongoClientOptions.builder().connectTimeout(timeout).build());
52 | } else {
53 | System.out.println("Connecting to MongoDB " + this.host + ":" + this.port + "...");
54 | mongoClient = new MongoClient(new ServerAddress(this.host, this.port), Collections.singletonList(this.credential), MongoClientOptions.builder().connectTimeout(timeout).build());
55 | }
56 | }
57 | return mongoClient.getAddress();
58 | }
59 |
60 | public void disconnect() throws IOException {
61 | if (mongoClient != null) {
62 | mongoClient.close();
63 | }
64 | }
65 |
66 | public MongoDatabase db() {
67 | if (mongoDatabase == null) {
68 | System.out.println("Initializing database '" + dbName + "'");
69 | mongoDatabase = mongoClient.getDatabase(dbName);
70 | }
71 | return mongoDatabase;
72 | }
73 |
74 | public Jongo jongo() {
75 | return new Jongo(mongoClient.getDB(dbName));
76 | }
77 |
78 | }
79 |
--------------------------------------------------------------------------------
/src/main/java/org/inventivetalent/mcauth/MCAuthServer.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mcauth;
2 |
3 | import com.mongodb.ServerAddress;
4 | import net.md_5.bungee.api.ProxyServer;
5 | import net.md_5.bungee.api.ServerPing;
6 | import net.md_5.bungee.api.chat.TextComponent;
7 | import net.md_5.bungee.api.event.LoginEvent;
8 | import net.md_5.bungee.api.event.ProxyPingEvent;
9 | import net.md_5.bungee.api.plugin.Listener;
10 | import net.md_5.bungee.api.plugin.Plugin;
11 | import net.md_5.bungee.config.Configuration;
12 | import net.md_5.bungee.config.ConfigurationProvider;
13 | import net.md_5.bungee.config.YamlConfiguration;
14 | import net.md_5.bungee.event.EventHandler;
15 | import org.inventivetalent.mcauth.data.Request;
16 | import org.inventivetalent.mcauth.data.Status;
17 | import org.jongo.Jongo;
18 | import org.jongo.MongoCollection;
19 |
20 | import java.io.File;
21 | import java.io.IOException;
22 | import java.io.InputStream;
23 | import java.nio.file.Files;
24 | import java.util.ArrayList;
25 | import java.util.List;
26 | import java.util.Random;
27 |
28 | public class MCAuthServer extends Plugin implements Listener {
29 |
30 | Configuration config;
31 | DatabaseClient databaseClient;
32 |
33 | Jongo jongo;
34 | MongoCollection requestsCollection;
35 | MongoCollection accountsCollection;
36 |
37 | @Override
38 | public void onEnable() {
39 | ProxyServer.getInstance().getPluginManager().registerListener(this, this);
40 |
41 | if (!getDataFolder().exists()) { getDataFolder().mkdir(); }
42 | File file = new File(getDataFolder(), "config.yml");
43 | if (!file.exists()) {
44 | try (InputStream in = getResourceAsStream("config.yml")) {
45 | Files.copy(in, file.toPath());
46 | throw new RuntimeException("Default config saved! Please edit and restart.");
47 | } catch (IOException e) {
48 | throw new RuntimeException("Failed to save default config", e);
49 | }
50 | }
51 |
52 | try {
53 | config = ConfigurationProvider.getProvider(YamlConfiguration.class).load(new File(getDataFolder(), "config.yml"));
54 | } catch (IOException e) {
55 | throw new RuntimeException("Failed to load config", e);
56 | }
57 |
58 | if (config.contains("mongodb.hosts")) {
59 | List hosts = new ArrayList<>();
60 | for (String s : config.getStringList("mongodb.hosts")) {
61 | String[] split = s.split(":");
62 | hosts.add(new ServerAddress(split[0], split.length > 1 ? Integer.parseInt(split[1]) : ServerAddress.defaultPort()));
63 | }
64 | databaseClient = new DatabaseClient(config.getString("mongodb.database"), hosts, config.getString("mongodb.login.user"), config.getString("mongodb.login.pass").toCharArray(), config.getString("mongodb.login.db"));
65 | } else {
66 | databaseClient = new DatabaseClient(config.getString("mongodb.database"), config.getString("mongodb.host"), config.getInt("mongodb.port"), config.getString("mongodb.login.user"), config.getString("mongodb.login.pass").toCharArray(), config.getString("mongodb.login.db"));
67 | }
68 | try {
69 | databaseClient.connect(10000);
70 | databaseClient.collectionCount();
71 | } catch (IOException e) {
72 | throw new RuntimeException("Failed to connect to database", e);
73 | }
74 |
75 | jongo = databaseClient.jongo();
76 | requestsCollection = jongo.getCollection("requests");
77 | accountsCollection = jongo.getCollection("accounts");
78 | }
79 |
80 | @EventHandler
81 | public void onLogin(LoginEvent event) {
82 | String ip = event.getConnection().getAddress().getAddress().getHostAddress();
83 | getLogger().info(event.getConnection().getName() + " connecting (" + ip + ")...");
84 |
85 | event.registerIntent(this);
86 | getProxy().getScheduler().runAsync(this, () -> {
87 | Request request = requestsCollection.findOne("{username: #, status: #}", event.getConnection().getName(), Status.REQUESTED).as(Request.class);
88 |
89 | if (request == null) {
90 | event.setCancelReason("§cThere is no authentication request for your username");
91 | getLogger().info("No request found");
92 | event.setCancelled(true);
93 | event.completeIntent(this);
94 | return;
95 | }
96 | getLogger().info("Handling request #" + request.get_id());
97 |
98 | if (System.currentTimeMillis() - (request.getCreated().getTime()) > 5 * 60 * 1000) {// 5 Minutes timeout
99 | event.setCancelReason("§cYour authentication request timed out");
100 | event.setCancelled(true);
101 |
102 | request.setStatus(Status.TIMEOUT_LOGIN);
103 | requestsCollection.update("{_id: #}", request.get_id()).upsert().with(request);
104 |
105 | event.completeIntent(this);
106 | return;
107 | }
108 |
109 | request.setToken(generateToken(6));
110 | request.setTokenTime(System.currentTimeMillis());
111 | request.setStatus(Status.TOKEN_GENERATED);
112 | request.setUuid(event.getConnection().getUniqueId().toString().replaceAll("-", ""));
113 |
114 | requestsCollection.update("{_id: #}", request.get_id()).upsert().with(request);
115 |
116 | // Kick player
117 | event.setCancelReason("§aYour account has been authenticated.\n"
118 | + "§aPlease enter this code on the website: §b" + request.getToken());
119 | event.setCancelled(true);
120 | event.completeIntent(this);
121 | });
122 | }
123 |
124 | @EventHandler
125 | public void onPing(ProxyPingEvent event) {
126 | String ip = event.getConnection().getAddress().getAddress().getHostAddress();
127 |
128 | event.registerIntent(this);
129 | getProxy().getScheduler().runAsync(this, () -> {
130 | Request request = requestsCollection.findOne("{request_ip: #, status: #}", ip, Status.REQUESTED).as(Request.class);
131 |
132 | if (request != null) {
133 | if (System.currentTimeMillis() - (request.getCreated().getTime()) < 5 * 60 * 1000) {
134 | event.setResponse(new ServerPing(
135 | new ServerPing.Protocol("MinecraftID", event.getConnection().getVersion()),
136 | new ServerPing.Players(1, 0, new ServerPing.PlayerInfo[0]),
137 | new TextComponent("§8MinecraftID Server - §7minecraft.id\n"
138 | + "§aJoin now to verify your account!"),
139 | getProxy().getConfig().getFaviconObject()
140 | ));
141 | event.completeIntent(this);
142 | return;
143 | }
144 | }
145 | event.setResponse(new ServerPing(
146 | new ServerPing.Protocol("MinecraftID", event.getConnection().getVersion()),
147 | new ServerPing.Players(0, 0, new ServerPing.PlayerInfo[0]),
148 | new TextComponent("§8MinecraftID Server - §7minecraft.id\n"
149 | + "§6There is no request to verify your account"),
150 | getProxy().getConfig().getFaviconObject()
151 | ));
152 | event.completeIntent(this);
153 | });
154 | }
155 |
156 | String generateToken(int length) {
157 | String string = "";
158 | Random random = new Random();
159 | for (int i = 0; i < length; i++) {
160 | string += String.valueOf(random.nextInt(10));
161 | }
162 | return string;
163 | }
164 |
165 | }
166 |
--------------------------------------------------------------------------------
/src/main/java/org/inventivetalent/mcauth/data/Request.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mcauth.data;
2 |
3 | import lombok.Data;
4 |
5 | import java.util.Date;
6 |
7 | @Data
8 | public class Request {
9 |
10 | String _id;
11 | String request_id;
12 | String request_ip;
13 | String username;
14 | String uuid;
15 | Status status;
16 | String token;
17 | long tokenTime;
18 | Date created;
19 |
20 | }
21 |
--------------------------------------------------------------------------------
/src/main/java/org/inventivetalent/mcauth/data/Status.java:
--------------------------------------------------------------------------------
1 | package org.inventivetalent.mcauth.data;
2 |
3 | public enum Status {
4 | STARTED,
5 | REQUESTED,
6 | TOKEN_GENERATED,
7 | TIMEOUT_LOGIN,
8 | TIMEOUT_TOKEN,
9 | VERIFIED,
10 | FAILED;
11 | }
12 |
--------------------------------------------------------------------------------
/src/main/resources/bungee.yml:
--------------------------------------------------------------------------------
1 | name: MCAuth-Server
2 | main: org.inventivetalent.mcauth.MCAuthServer
3 | version: 1.1.0
4 | author: inventivetalent
5 |
--------------------------------------------------------------------------------
/src/main/resources/config.yml:
--------------------------------------------------------------------------------
1 | mongodb:
2 | host: "127.0.0.1"
3 | port: 27017
4 | database: ""
5 | login:
6 | user: ""
7 | pass: ""
8 | db: ""
--------------------------------------------------------------------------------