@bobschlowinskii/clicraft 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +191 -0
- package/CONTRIBUTING.md +261 -0
- package/LICENSE.md +7 -0
- package/README.md +55 -0
- package/commands/auth.js +427 -0
- package/commands/config.js +356 -0
- package/commands/create.js +534 -0
- package/commands/info.js +113 -0
- package/commands/install.js +130 -0
- package/commands/launch.js +296 -0
- package/commands/search.js +50 -0
- package/commands/uninstall.js +94 -0
- package/commands/upgrade.js +343 -0
- package/commands/version.js +21 -0
- package/docs/README.md +119 -0
- package/docs/_config.yml +63 -0
- package/docs/commands/auth.md +376 -0
- package/docs/commands/config.md +294 -0
- package/docs/commands/create.md +276 -0
- package/docs/commands/info.md +460 -0
- package/docs/commands/install.md +320 -0
- package/docs/commands/launch.md +427 -0
- package/docs/commands/search.md +276 -0
- package/docs/commands/uninstall.md +128 -0
- package/docs/commands/upgrade.md +515 -0
- package/docs/commands.md +266 -0
- package/docs/configuration.md +410 -0
- package/docs/contributing.md +114 -0
- package/docs/index.md +82 -0
- package/docs/installation.md +162 -0
- package/helpers/auth/index.js +20 -0
- package/helpers/auth/microsoft.js +329 -0
- package/helpers/auth/storage.js +260 -0
- package/helpers/config.js +258 -0
- package/helpers/constants.js +38 -0
- package/helpers/getv.js +5 -0
- package/helpers/minecraft.js +308 -0
- package/helpers/modrinth.js +141 -0
- package/helpers/utils.js +334 -0
- package/helpers/versions.js +21 -0
- package/index.js +89 -0
- package/package.json +30 -0
|
@@ -0,0 +1,114 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Contributing
|
|
4
|
+
nav_order: 10
|
|
5
|
+
description: "How to contribute to CLIcraft"
|
|
6
|
+
permalink: /contributing
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Contributing to CLIcraft
|
|
10
|
+
|
|
11
|
+
Thank you for your interest in contributing to CLIcraft! We welcome contributions from the community and appreciate your help in making this tool better.
|
|
12
|
+
|
|
13
|
+
## 🚀 Quick Links
|
|
14
|
+
|
|
15
|
+
- **Full Contributing Guide**: [CONTRIBUTING.md](https://github.com/theinfamousben/clicraft/blob/main/CONTRIBUTING.md)
|
|
16
|
+
- **Issue Tracker**: [GitHub Issues](https://github.com/theinfamousben/clicraft/issues)
|
|
17
|
+
- **Pull Requests**: [GitHub PRs](https://github.com/theinfamousben/clicraft/pulls)
|
|
18
|
+
- **Repository**: [theinfamousben/clicraft](https://github.com/theinfamousben/clicraft)
|
|
19
|
+
|
|
20
|
+
## 📋 Ways to Contribute
|
|
21
|
+
|
|
22
|
+
There are many ways you can contribute to CLIcraft:
|
|
23
|
+
|
|
24
|
+
### 🐛 Report Bugs
|
|
25
|
+
|
|
26
|
+
Found a bug? Please [open an issue](https://github.com/theinfamousben/clicraft/issues/new) with:
|
|
27
|
+
- A clear description of the problem
|
|
28
|
+
- Steps to reproduce the issue
|
|
29
|
+
- Your environment (OS, Node.js version, etc.)
|
|
30
|
+
- Expected vs actual behavior
|
|
31
|
+
|
|
32
|
+
### ✨ Suggest Features
|
|
33
|
+
|
|
34
|
+
Have an idea for a new feature? We'd love to hear it! [Open an issue](https://github.com/theinfamousben/clicraft/issues/new) and describe:
|
|
35
|
+
- What problem the feature would solve
|
|
36
|
+
- How you envision it working
|
|
37
|
+
- Any examples or mockups (if applicable)
|
|
38
|
+
|
|
39
|
+
### 📝 Improve Documentation
|
|
40
|
+
|
|
41
|
+
Documentation improvements are always welcome:
|
|
42
|
+
- Fix typos or clarify existing docs
|
|
43
|
+
- Add examples or use cases
|
|
44
|
+
- Write tutorials or guides
|
|
45
|
+
- Improve code comments
|
|
46
|
+
|
|
47
|
+
### 💻 Submit Code
|
|
48
|
+
|
|
49
|
+
Ready to write some code? Here's how to get started:
|
|
50
|
+
|
|
51
|
+
1. **Fork the repository** and clone it locally
|
|
52
|
+
2. **Set up your development environment** (see below)
|
|
53
|
+
3. **Create a branch** for your changes
|
|
54
|
+
4. **Make your changes** following our coding guidelines
|
|
55
|
+
5. **Test your changes** thoroughly
|
|
56
|
+
6. **Submit a pull request** with a clear description
|
|
57
|
+
|
|
58
|
+
## 🛠️ Development Setup
|
|
59
|
+
|
|
60
|
+
### Prerequisites
|
|
61
|
+
|
|
62
|
+
- Node.js 18 or higher
|
|
63
|
+
- Java 21 or higher (for testing Minecraft functionality)
|
|
64
|
+
- Git
|
|
65
|
+
|
|
66
|
+
### Quick Setup
|
|
67
|
+
|
|
68
|
+
```bash
|
|
69
|
+
# Clone your fork
|
|
70
|
+
git clone https://github.com/YOUR-USERNAME/clicraft.git
|
|
71
|
+
cd clicraft
|
|
72
|
+
|
|
73
|
+
# Install dependencies
|
|
74
|
+
npm install
|
|
75
|
+
|
|
76
|
+
# Link globally for testing
|
|
77
|
+
npm link
|
|
78
|
+
|
|
79
|
+
# Verify installation
|
|
80
|
+
clicraft --version
|
|
81
|
+
```
|
|
82
|
+
|
|
83
|
+
## 📖 Detailed Guide
|
|
84
|
+
|
|
85
|
+
For a comprehensive guide on contributing, including:
|
|
86
|
+
- Code style guidelines
|
|
87
|
+
- Pull request process
|
|
88
|
+
- Testing procedures
|
|
89
|
+
- Best practices
|
|
90
|
+
|
|
91
|
+
Please read our full [Contributing Guide](https://github.com/theinfamousben/clicraft/blob/main/CONTRIBUTING.md).
|
|
92
|
+
|
|
93
|
+
## 🤝 Code of Conduct
|
|
94
|
+
|
|
95
|
+
We are committed to providing a welcoming and inclusive environment. Please:
|
|
96
|
+
- Be respectful and constructive
|
|
97
|
+
- Welcome and help newcomers
|
|
98
|
+
- Provide helpful feedback
|
|
99
|
+
- Be patient and understanding
|
|
100
|
+
|
|
101
|
+
## 💬 Getting Help
|
|
102
|
+
|
|
103
|
+
Need help getting started?
|
|
104
|
+
- Check the [Installation Guide](installation.md)
|
|
105
|
+
- Review the [Commands Documentation](commands.md)
|
|
106
|
+
- Ask questions in [GitHub Issues](https://github.com/theinfamousben/clicraft/issues)
|
|
107
|
+
|
|
108
|
+
## 📜 License
|
|
109
|
+
|
|
110
|
+
By contributing to CLIcraft, you agree that your contributions will be licensed under the ISC License.
|
|
111
|
+
|
|
112
|
+
---
|
|
113
|
+
|
|
114
|
+
**Ready to contribute?** Check out our [full Contributing Guide](https://github.com/theinfamousben/clicraft/blob/main/CONTRIBUTING.md) to get started!
|
package/docs/index.md
ADDED
|
@@ -0,0 +1,82 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Home
|
|
4
|
+
nav_order: 1
|
|
5
|
+
description: "CLIcraft - A simple Minecraft Mod Package Manager"
|
|
6
|
+
permalink: /
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# CLIcraft Documentation
|
|
10
|
+
|
|
11
|
+
Welcome to **CLIcraft** - A simple, powerful Minecraft Mod Package Manager written in Node.js.
|
|
12
|
+
|
|
13
|
+
## 🎮 What is CLIcraft?
|
|
14
|
+
|
|
15
|
+
CLIcraft is a command-line tool that simplifies managing Minecraft instances and mods. Create instances with Fabric or Forge, search and install mods from Modrinth, and launch the game directly from your terminal.
|
|
16
|
+
|
|
17
|
+
## ✨ Key Features
|
|
18
|
+
|
|
19
|
+
- **🎮 Create Instances** - Set up new Minecraft client or server instances with Fabric or Forge
|
|
20
|
+
- **🔍 Search Mods** - Find mods on Modrinth with filters for version, loader, and more
|
|
21
|
+
- **📦 Install Mods** - Download and install mods directly to your instance
|
|
22
|
+
- **🔄 Upgrade** - Update mods, mod loader, or Minecraft version
|
|
23
|
+
- **ℹ️ Instance Info** - View detailed information about your instances
|
|
24
|
+
- **🔐 Microsoft Login** - Authenticate with your Microsoft account to play online
|
|
25
|
+
- **🚀 Launch Game** - Start Minecraft directly from the terminal
|
|
26
|
+
|
|
27
|
+
## 🚀 Quick Start
|
|
28
|
+
|
|
29
|
+
```bash
|
|
30
|
+
# Create a new instance
|
|
31
|
+
clicraft create
|
|
32
|
+
|
|
33
|
+
# Search for mods
|
|
34
|
+
clicraft search sodium
|
|
35
|
+
|
|
36
|
+
# Install a mod
|
|
37
|
+
cd my-instance
|
|
38
|
+
clicraft install sodium
|
|
39
|
+
|
|
40
|
+
# Launch the game
|
|
41
|
+
clicraft launch
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## 📖 Documentation
|
|
45
|
+
|
|
46
|
+
### Getting Started
|
|
47
|
+
- [Installation](installation.md) - Install CLIcraft on your system
|
|
48
|
+
- [Commands Overview](commands.md) - List of all available commands
|
|
49
|
+
|
|
50
|
+
### Commands
|
|
51
|
+
- [`create`](commands/create.md) - Create a new Minecraft instance
|
|
52
|
+
- [`search`](commands/search.md) - Search for mods on Modrinth
|
|
53
|
+
- [`install`](commands/install.md) - Install mods to your instance
|
|
54
|
+
- [`login`](commands/login.md) - Authenticate with Microsoft account
|
|
55
|
+
- [`launch`](commands/launch.md) - Launch your Minecraft instance
|
|
56
|
+
- [`info`](commands/info.md) - View instance information
|
|
57
|
+
- [`upgrade`](commands/upgrade.md) - Upgrade mods and loaders
|
|
58
|
+
|
|
59
|
+
### Configuration
|
|
60
|
+
- [Configuration Guide](configuration.md) - Instance and authentication configuration
|
|
61
|
+
|
|
62
|
+
### Contributing
|
|
63
|
+
- [Contributing Guide](contributing.md) - How to contribute to CLIcraft
|
|
64
|
+
|
|
65
|
+
## 🎯 Requirements
|
|
66
|
+
|
|
67
|
+
- **Node.js** 18 or higher
|
|
68
|
+
- **Java** 21 or higher (for running Minecraft)
|
|
69
|
+
|
|
70
|
+
## 🌐 Supported Platforms
|
|
71
|
+
|
|
72
|
+
- ✅ Linux
|
|
73
|
+
- ✅ macOS
|
|
74
|
+
- ✅ Windows
|
|
75
|
+
|
|
76
|
+
## 🤝 Contributing
|
|
77
|
+
|
|
78
|
+
Contributions are welcome! Please read our [Contributing Guide](https://github.com/theinfamousben/clicraft/blob/main/CONTRIBUTING.md) to learn about our development process, how to propose bugfixes and improvements, and how to build and test your changes. You can also visit our [GitHub repository](https://github.com/theinfamousben/clicraft) to open issues or submit pull requests.
|
|
79
|
+
|
|
80
|
+
## 📄 License
|
|
81
|
+
|
|
82
|
+
ISC - See the [LICENSE](https://github.com/theinfamousben/clicraft/blob/main/LICENSE) file for details.
|
|
@@ -0,0 +1,162 @@
|
|
|
1
|
+
---
|
|
2
|
+
layout: default
|
|
3
|
+
title: Installation
|
|
4
|
+
nav_order: 2
|
|
5
|
+
description: "Install CLIcraft on your system"
|
|
6
|
+
permalink: /installation
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# Installation
|
|
10
|
+
|
|
11
|
+
This guide will help you install CLIcraft on your system.
|
|
12
|
+
|
|
13
|
+
## 📋 Prerequisites
|
|
14
|
+
|
|
15
|
+
Before installing CLIcraft, ensure you have the following installed:
|
|
16
|
+
|
|
17
|
+
- **Node.js** 18 or higher - [Download Node.js](https://nodejs.org/)
|
|
18
|
+
- **Java** 21 or higher - Required for running Minecraft - [Download Java](https://adoptium.net/)
|
|
19
|
+
|
|
20
|
+
You can verify your installations by running:
|
|
21
|
+
|
|
22
|
+
```bash
|
|
23
|
+
node --version
|
|
24
|
+
java --version
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## 📦 Installation Methods
|
|
28
|
+
|
|
29
|
+
### Method 1: NPM Package (Coming Soon!)
|
|
30
|
+
|
|
31
|
+
**Coming Soon!** The npm package will be available soon. Once released, you'll be able to install globally with:
|
|
32
|
+
|
|
33
|
+
```bash
|
|
34
|
+
npm install -g clicraft
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
### Method 2: Clone from Source
|
|
38
|
+
|
|
39
|
+
This is currently the recommended way to install CLIcraft:
|
|
40
|
+
|
|
41
|
+
```bash
|
|
42
|
+
# Clone the repository
|
|
43
|
+
git clone https://github.com/theinfamousben/clicraft.git
|
|
44
|
+
cd clicraft
|
|
45
|
+
|
|
46
|
+
# Install dependencies
|
|
47
|
+
npm install
|
|
48
|
+
|
|
49
|
+
# Link globally (allows you to use 'clicraft' command anywhere)
|
|
50
|
+
npm link
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
After linking, you can use the `clicraft` command from any directory:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
clicraft --version
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## 🔧 Verifying Installation
|
|
60
|
+
|
|
61
|
+
To verify that CLIcraft is installed correctly, run:
|
|
62
|
+
|
|
63
|
+
```bash
|
|
64
|
+
clicraft --version
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
This should display the current version of CLIcraft.
|
|
68
|
+
|
|
69
|
+
## 📁 Configuration Files
|
|
70
|
+
|
|
71
|
+
CLIcraft stores configuration files in the following locations:
|
|
72
|
+
|
|
73
|
+
- **Authentication tokens**: `~/.mcpkg/auth.json`
|
|
74
|
+
- **Instance configuration**: `mcconfig.json` (in each instance directory)
|
|
75
|
+
|
|
76
|
+
These files are created automatically when you use CLIcraft for the first time.
|
|
77
|
+
|
|
78
|
+
## ⬆️ Updating CLIcraft
|
|
79
|
+
|
|
80
|
+
### If installed from source:
|
|
81
|
+
|
|
82
|
+
```bash
|
|
83
|
+
cd clicraft
|
|
84
|
+
git pull origin main
|
|
85
|
+
npm install
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
### If installed via npm (when available):
|
|
89
|
+
|
|
90
|
+
```bash
|
|
91
|
+
npm update -g clicraft
|
|
92
|
+
```
|
|
93
|
+
|
|
94
|
+
## 🗑️ Uninstalling
|
|
95
|
+
|
|
96
|
+
### If installed from source:
|
|
97
|
+
|
|
98
|
+
```bash
|
|
99
|
+
npm unlink clicraft
|
|
100
|
+
```
|
|
101
|
+
|
|
102
|
+
Then delete the cloned repository folder.
|
|
103
|
+
|
|
104
|
+
### If installed via npm (when available):
|
|
105
|
+
|
|
106
|
+
```bash
|
|
107
|
+
npm uninstall -g clicraft
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
To also remove configuration files:
|
|
111
|
+
|
|
112
|
+
```bash
|
|
113
|
+
rm -rf ~/.mcpkg
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## 🐛 Troubleshooting
|
|
117
|
+
|
|
118
|
+
### "command not found: clicraft"
|
|
119
|
+
|
|
120
|
+
If you see this error after installation:
|
|
121
|
+
|
|
122
|
+
1. Make sure you ran `npm link` in the clicraft directory
|
|
123
|
+
2. Check that your npm global bin directory is in your PATH
|
|
124
|
+
3. Try running `npm config get prefix` to see where global packages are installed
|
|
125
|
+
|
|
126
|
+
### Permission errors on Linux/macOS
|
|
127
|
+
|
|
128
|
+
If you encounter permission errors during `npm link`, you may need to use:
|
|
129
|
+
|
|
130
|
+
```bash
|
|
131
|
+
sudo npm link
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Node.js version issues
|
|
135
|
+
|
|
136
|
+
Make sure you're using Node.js 18 or higher. You can use [nvm](https://github.com/nvm-sh/nvm) to manage multiple Node.js versions:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
nvm install 18
|
|
140
|
+
nvm use 18
|
|
141
|
+
```
|
|
142
|
+
|
|
143
|
+
## 🆘 Getting Help
|
|
144
|
+
|
|
145
|
+
If you encounter any issues during installation:
|
|
146
|
+
|
|
147
|
+
1. Check the [GitHub Issues](https://github.com/theinfamousben/clicraft/issues) page
|
|
148
|
+
2. Open a new issue with details about your problem
|
|
149
|
+
3. Include your operating system, Node.js version, and any error messages
|
|
150
|
+
|
|
151
|
+
## ⏭️ Next Steps
|
|
152
|
+
|
|
153
|
+
Once CLIcraft is installed, you're ready to:
|
|
154
|
+
|
|
155
|
+
1. [Create your first instance](commands/create.md)
|
|
156
|
+
2. [Search for mods](commands/search.md)
|
|
157
|
+
3. [Install mods](commands/install.md)
|
|
158
|
+
4. [Launch the game](commands/launch.md)
|
|
159
|
+
|
|
160
|
+
---
|
|
161
|
+
|
|
162
|
+
[← Back to Home](index.md) | [View All Commands →](commands.md)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
// Re-export everything from auth modules
|
|
2
|
+
export {
|
|
3
|
+
loadAccounts,
|
|
4
|
+
saveAccounts,
|
|
5
|
+
getCurrentAccount,
|
|
6
|
+
getAccount,
|
|
7
|
+
addAccount,
|
|
8
|
+
removeAccount,
|
|
9
|
+
switchAccount,
|
|
10
|
+
getAllAccounts,
|
|
11
|
+
getAccountCount,
|
|
12
|
+
accountExists,
|
|
13
|
+
updateAccount,
|
|
14
|
+
migrateFromLegacy
|
|
15
|
+
} from './storage.js';
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
performAuthentication,
|
|
19
|
+
refreshAccountAuth
|
|
20
|
+
} from './microsoft.js';
|
|
@@ -0,0 +1,329 @@
|
|
|
1
|
+
import readline from 'readline';
|
|
2
|
+
|
|
3
|
+
// Using the legacy live.com OAuth with the special desktop redirect URI
|
|
4
|
+
// This is the official approach for native/desktop applications
|
|
5
|
+
const CLIENT_ID = '00000000402b5328'; // Official Minecraft launcher client ID
|
|
6
|
+
const REDIRECT_URI = 'https://login.live.com/oauth20_desktop.srf';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Prompt user for input
|
|
10
|
+
*/
|
|
11
|
+
export function prompt(question) {
|
|
12
|
+
const rl = readline.createInterface({
|
|
13
|
+
input: process.stdin,
|
|
14
|
+
output: process.stdout
|
|
15
|
+
});
|
|
16
|
+
|
|
17
|
+
return new Promise((resolve) => {
|
|
18
|
+
rl.question(question, (answer) => {
|
|
19
|
+
rl.close();
|
|
20
|
+
resolve(answer.trim());
|
|
21
|
+
});
|
|
22
|
+
});
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
/**
|
|
26
|
+
* Extract authorization code from redirect URL
|
|
27
|
+
*/
|
|
28
|
+
export function extractCodeFromUrl(url) {
|
|
29
|
+
try {
|
|
30
|
+
if (url.startsWith('http')) {
|
|
31
|
+
const parsed = new URL(url);
|
|
32
|
+
return parsed.searchParams.get('code');
|
|
33
|
+
}
|
|
34
|
+
return url;
|
|
35
|
+
} catch {
|
|
36
|
+
return url;
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
/**
|
|
41
|
+
* Open URL in browser
|
|
42
|
+
*/
|
|
43
|
+
export async function openBrowser(url) {
|
|
44
|
+
const { exec } = await import('child_process');
|
|
45
|
+
const { promisify } = await import('util');
|
|
46
|
+
const execAsync = promisify(exec);
|
|
47
|
+
|
|
48
|
+
const platform = process.platform;
|
|
49
|
+
let command;
|
|
50
|
+
|
|
51
|
+
if (platform === 'darwin') {
|
|
52
|
+
command = `open "${url}"`;
|
|
53
|
+
} else if (platform === 'win32') {
|
|
54
|
+
command = `start "" "${url}"`;
|
|
55
|
+
} else {
|
|
56
|
+
command = `xdg-open "${url}" || sensible-browser "${url}" || x-www-browser "${url}" || gnome-open "${url}"`;
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
try {
|
|
60
|
+
await execAsync(command);
|
|
61
|
+
return true;
|
|
62
|
+
} catch {
|
|
63
|
+
return false;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/**
|
|
68
|
+
* Get OAuth URL for Microsoft login
|
|
69
|
+
*/
|
|
70
|
+
export function getOAuthUrl() {
|
|
71
|
+
return `https://login.live.com/oauth20_authorize.srf?` +
|
|
72
|
+
`client_id=${CLIENT_ID}` +
|
|
73
|
+
`&response_type=code` +
|
|
74
|
+
`&redirect_uri=${encodeURIComponent(REDIRECT_URI)}` +
|
|
75
|
+
`&scope=${encodeURIComponent('service::user.auth.xboxlive.com::MBI_SSL')}`;
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
/**
|
|
79
|
+
* Exchange code for Microsoft token
|
|
80
|
+
*/
|
|
81
|
+
export async function getMicrosoftToken(code) {
|
|
82
|
+
const response = await fetch('https://login.live.com/oauth20_token.srf', {
|
|
83
|
+
method: 'POST',
|
|
84
|
+
headers: {
|
|
85
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
86
|
+
},
|
|
87
|
+
body: new URLSearchParams({
|
|
88
|
+
client_id: CLIENT_ID,
|
|
89
|
+
code: code,
|
|
90
|
+
grant_type: 'authorization_code',
|
|
91
|
+
redirect_uri: REDIRECT_URI,
|
|
92
|
+
scope: 'service::user.auth.xboxlive.com::MBI_SSL'
|
|
93
|
+
})
|
|
94
|
+
});
|
|
95
|
+
|
|
96
|
+
if (!response.ok) {
|
|
97
|
+
const text = await response.text();
|
|
98
|
+
throw new Error(`Failed to get Microsoft token: ${text}`);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
return await response.json();
|
|
102
|
+
}
|
|
103
|
+
|
|
104
|
+
/**
|
|
105
|
+
* Refresh Microsoft token
|
|
106
|
+
*/
|
|
107
|
+
export async function refreshMicrosoftToken(refreshToken) {
|
|
108
|
+
const response = await fetch('https://login.live.com/oauth20_token.srf', {
|
|
109
|
+
method: 'POST',
|
|
110
|
+
headers: {
|
|
111
|
+
'Content-Type': 'application/x-www-form-urlencoded'
|
|
112
|
+
},
|
|
113
|
+
body: new URLSearchParams({
|
|
114
|
+
client_id: CLIENT_ID,
|
|
115
|
+
refresh_token: refreshToken,
|
|
116
|
+
grant_type: 'refresh_token',
|
|
117
|
+
scope: 'service::user.auth.xboxlive.com::MBI_SSL'
|
|
118
|
+
})
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
if (!response.ok) {
|
|
122
|
+
return null;
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
return await response.json();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
/**
|
|
129
|
+
* Get Xbox Live token
|
|
130
|
+
*/
|
|
131
|
+
export async function getXboxLiveToken(msAccessToken) {
|
|
132
|
+
const response = await fetch('https://user.auth.xboxlive.com/user/authenticate', {
|
|
133
|
+
method: 'POST',
|
|
134
|
+
headers: {
|
|
135
|
+
'Content-Type': 'application/json',
|
|
136
|
+
'Accept': 'application/json'
|
|
137
|
+
},
|
|
138
|
+
body: JSON.stringify({
|
|
139
|
+
Properties: {
|
|
140
|
+
AuthMethod: 'RPS',
|
|
141
|
+
SiteName: 'user.auth.xboxlive.com',
|
|
142
|
+
RpsTicket: msAccessToken
|
|
143
|
+
},
|
|
144
|
+
RelyingParty: 'http://auth.xboxlive.com',
|
|
145
|
+
TokenType: 'JWT'
|
|
146
|
+
})
|
|
147
|
+
});
|
|
148
|
+
|
|
149
|
+
if (!response.ok) {
|
|
150
|
+
const text = await response.text();
|
|
151
|
+
throw new Error(`Failed to get Xbox Live token: ${text}`);
|
|
152
|
+
}
|
|
153
|
+
|
|
154
|
+
return await response.json();
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
/**
|
|
158
|
+
* Get XSTS token
|
|
159
|
+
*/
|
|
160
|
+
export async function getXSTSToken(xblToken) {
|
|
161
|
+
const response = await fetch('https://xsts.auth.xboxlive.com/xsts/authorize', {
|
|
162
|
+
method: 'POST',
|
|
163
|
+
headers: {
|
|
164
|
+
'Content-Type': 'application/json',
|
|
165
|
+
'Accept': 'application/json'
|
|
166
|
+
},
|
|
167
|
+
body: JSON.stringify({
|
|
168
|
+
Properties: {
|
|
169
|
+
SandboxId: 'RETAIL',
|
|
170
|
+
UserTokens: [xblToken]
|
|
171
|
+
},
|
|
172
|
+
RelyingParty: 'rp://api.minecraftservices.com/',
|
|
173
|
+
TokenType: 'JWT'
|
|
174
|
+
})
|
|
175
|
+
});
|
|
176
|
+
|
|
177
|
+
if (!response.ok) {
|
|
178
|
+
const error = await response.json().catch(() => ({}));
|
|
179
|
+
if (error.XErr === 2148916233) {
|
|
180
|
+
throw new Error('This Microsoft account does not have an Xbox account. Please create one at xbox.com');
|
|
181
|
+
} else if (error.XErr === 2148916238) {
|
|
182
|
+
throw new Error('This account belongs to someone under 18 and needs to be added to a family');
|
|
183
|
+
}
|
|
184
|
+
throw new Error(`Failed to get XSTS token: ${JSON.stringify(error)}`);
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
return await response.json();
|
|
188
|
+
}
|
|
189
|
+
|
|
190
|
+
/**
|
|
191
|
+
* Get Minecraft token
|
|
192
|
+
*/
|
|
193
|
+
export async function getMinecraftToken(xstsToken, userHash) {
|
|
194
|
+
const response = await fetch('https://api.minecraftservices.com/authentication/login_with_xbox', {
|
|
195
|
+
method: 'POST',
|
|
196
|
+
headers: {
|
|
197
|
+
'Content-Type': 'application/json'
|
|
198
|
+
},
|
|
199
|
+
body: JSON.stringify({
|
|
200
|
+
identityToken: `XBL3.0 x=${userHash};${xstsToken}`
|
|
201
|
+
})
|
|
202
|
+
});
|
|
203
|
+
|
|
204
|
+
if (!response.ok) {
|
|
205
|
+
const text = await response.text();
|
|
206
|
+
throw new Error(`Failed to get Minecraft token: ${text}`);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
return await response.json();
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/**
|
|
213
|
+
* Get Minecraft profile
|
|
214
|
+
*/
|
|
215
|
+
export async function getMinecraftProfile(mcAccessToken) {
|
|
216
|
+
const response = await fetch('https://api.minecraftservices.com/minecraft/profile', {
|
|
217
|
+
headers: {
|
|
218
|
+
'Authorization': `Bearer ${mcAccessToken}`
|
|
219
|
+
}
|
|
220
|
+
});
|
|
221
|
+
|
|
222
|
+
if (!response.ok) {
|
|
223
|
+
if (response.status === 404) {
|
|
224
|
+
throw new Error('You do not own Minecraft Java Edition. Please purchase it at minecraft.net');
|
|
225
|
+
}
|
|
226
|
+
throw new Error('Failed to get Minecraft profile');
|
|
227
|
+
}
|
|
228
|
+
|
|
229
|
+
return await response.json();
|
|
230
|
+
}
|
|
231
|
+
|
|
232
|
+
/**
|
|
233
|
+
* Full authentication flow - returns account data
|
|
234
|
+
*/
|
|
235
|
+
export async function performAuthentication(onStatus) {
|
|
236
|
+
const log = onStatus || (() => {});
|
|
237
|
+
|
|
238
|
+
const authUrl = getOAuthUrl();
|
|
239
|
+
|
|
240
|
+
log('open_url', authUrl);
|
|
241
|
+
|
|
242
|
+
const opened = await openBrowser(authUrl);
|
|
243
|
+
log('browser_opened', opened);
|
|
244
|
+
|
|
245
|
+
const redirectUrl = await prompt('\nPaste the redirect URL here: ');
|
|
246
|
+
|
|
247
|
+
if (!redirectUrl) {
|
|
248
|
+
throw new Error('No URL provided');
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
const code = extractCodeFromUrl(redirectUrl);
|
|
252
|
+
if (!code) {
|
|
253
|
+
throw new Error('Could not extract authorization code from URL');
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
log('status', 'Getting Microsoft token...');
|
|
257
|
+
const msToken = await getMicrosoftToken(code);
|
|
258
|
+
|
|
259
|
+
log('status', 'Getting Xbox Live token...');
|
|
260
|
+
const xblResponse = await getXboxLiveToken(msToken.access_token);
|
|
261
|
+
const xblToken = xblResponse.Token;
|
|
262
|
+
const userHash = xblResponse.DisplayClaims.xui[0].uhs;
|
|
263
|
+
|
|
264
|
+
log('status', 'Getting XSTS token...');
|
|
265
|
+
const xstsResponse = await getXSTSToken(xblToken);
|
|
266
|
+
const xstsToken = xstsResponse.Token;
|
|
267
|
+
|
|
268
|
+
log('status', 'Getting Minecraft token...');
|
|
269
|
+
const mcToken = await getMinecraftToken(xstsToken, userHash);
|
|
270
|
+
|
|
271
|
+
log('status', 'Getting Minecraft profile...');
|
|
272
|
+
const profile = await getMinecraftProfile(mcToken.access_token);
|
|
273
|
+
|
|
274
|
+
return {
|
|
275
|
+
accessToken: mcToken.access_token,
|
|
276
|
+
refreshToken: msToken.refresh_token,
|
|
277
|
+
expiresAt: Date.now() + (mcToken.expires_in * 1000),
|
|
278
|
+
uuid: profile.id,
|
|
279
|
+
username: profile.name,
|
|
280
|
+
authenticatedAt: new Date().toISOString()
|
|
281
|
+
};
|
|
282
|
+
}
|
|
283
|
+
|
|
284
|
+
/**
|
|
285
|
+
* Refresh authentication for an account
|
|
286
|
+
*/
|
|
287
|
+
export async function refreshAccountAuth(accountData) {
|
|
288
|
+
if (!accountData.refreshToken) {
|
|
289
|
+
return null;
|
|
290
|
+
}
|
|
291
|
+
|
|
292
|
+
const msToken = await refreshMicrosoftToken(accountData.refreshToken);
|
|
293
|
+
if (!msToken) {
|
|
294
|
+
return null;
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
const xblResponse = await getXboxLiveToken(msToken.access_token);
|
|
298
|
+
const xstsResponse = await getXSTSToken(xblResponse.Token);
|
|
299
|
+
const mcToken = await getMinecraftToken(
|
|
300
|
+
xstsResponse.Token,
|
|
301
|
+
xblResponse.DisplayClaims.xui[0].uhs
|
|
302
|
+
);
|
|
303
|
+
const profile = await getMinecraftProfile(mcToken.access_token);
|
|
304
|
+
|
|
305
|
+
return {
|
|
306
|
+
accessToken: mcToken.access_token,
|
|
307
|
+
refreshToken: msToken.refresh_token,
|
|
308
|
+
expiresAt: Date.now() + (mcToken.expires_in * 1000),
|
|
309
|
+
uuid: profile.id,
|
|
310
|
+
username: profile.name,
|
|
311
|
+
authenticatedAt: accountData.authenticatedAt,
|
|
312
|
+
refreshedAt: new Date().toISOString()
|
|
313
|
+
};
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
export default {
|
|
317
|
+
prompt,
|
|
318
|
+
extractCodeFromUrl,
|
|
319
|
+
openBrowser,
|
|
320
|
+
getOAuthUrl,
|
|
321
|
+
getMicrosoftToken,
|
|
322
|
+
refreshMicrosoftToken,
|
|
323
|
+
getXboxLiveToken,
|
|
324
|
+
getXSTSToken,
|
|
325
|
+
getMinecraftToken,
|
|
326
|
+
getMinecraftProfile,
|
|
327
|
+
performAuthentication,
|
|
328
|
+
refreshAccountAuth
|
|
329
|
+
};
|