2024-10-30 16:41:03 -06:00
|
|
|
package main
|
|
|
|
|
|
|
|
import (
|
2024-10-30 17:16:10 -06:00
|
|
|
"bytes"
|
2024-10-30 16:41:03 -06:00
|
|
|
"encoding/json"
|
|
|
|
"fmt"
|
|
|
|
"io"
|
|
|
|
"net/http"
|
|
|
|
"net/url"
|
|
|
|
"time"
|
|
|
|
|
|
|
|
wruntime "github.com/wailsapp/wails/v2/pkg/runtime"
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
|
|
type LauncherAuth struct {
|
|
|
|
Id string
|
|
|
|
Name string
|
2024-10-30 17:16:10 -06:00
|
|
|
Token string
|
2024-10-30 16:41:03 -06:00
|
|
|
}
|
|
|
|
|
|
|
|
type McProfile struct {
|
|
|
|
Id string
|
|
|
|
Name string
|
|
|
|
}
|
|
|
|
|
|
|
|
type devcodeResp struct {
|
|
|
|
User_code string
|
|
|
|
Device_code string
|
|
|
|
Verification_url string
|
|
|
|
Expires_in string
|
|
|
|
Interval int
|
|
|
|
Message string
|
|
|
|
}
|
|
|
|
|
|
|
|
type authenticationResp struct {
|
|
|
|
Access_token string
|
|
|
|
Token_type string
|
|
|
|
Refresh_token string
|
|
|
|
Expires_in string
|
|
|
|
Error string
|
|
|
|
Error_description string
|
|
|
|
}
|
|
|
|
|
|
|
|
type xboxAuthProperties struct {
|
|
|
|
AuthMethod string
|
|
|
|
SiteName string
|
|
|
|
RpsTicket string
|
|
|
|
}
|
|
|
|
|
|
|
|
type xboxAuthRequest struct {
|
|
|
|
Properties xboxAuthProperties
|
|
|
|
RelyingParty string
|
|
|
|
TokenType string
|
|
|
|
}
|
|
|
|
|
|
|
|
type xboxDisplayClaim struct {
|
|
|
|
Uhs string
|
|
|
|
Gtg string
|
|
|
|
Xid string
|
|
|
|
Agg string
|
|
|
|
Usr string
|
|
|
|
Utr string
|
|
|
|
Prv string
|
|
|
|
}
|
|
|
|
|
|
|
|
type xboxDisplayClaims struct {
|
|
|
|
Xui []xboxDisplayClaim
|
|
|
|
}
|
|
|
|
|
|
|
|
type xboxAuthResponse struct {
|
|
|
|
IssueInstant time.Time
|
|
|
|
NotAfter time.Time
|
|
|
|
Token string
|
|
|
|
DisplayClaims xboxDisplayClaims
|
|
|
|
}
|
|
|
|
|
|
|
|
type XSTSProperties struct {
|
|
|
|
SandboxId string
|
|
|
|
UserTokens []string
|
|
|
|
}
|
|
|
|
|
|
|
|
type XSTSRequest struct {
|
|
|
|
Properties XSTSProperties
|
|
|
|
RelyingParty string
|
|
|
|
TokenType string
|
|
|
|
}
|
|
|
|
|
|
|
|
type McAuthRequest struct {
|
|
|
|
Xtoken string `json:"xtoken"`
|
|
|
|
Platform string `json:"platform"`
|
|
|
|
}
|
|
|
|
|
|
|
|
type McAuthResponse struct {
|
|
|
|
Username string
|
|
|
|
Access_token string
|
|
|
|
Expires_in string
|
|
|
|
Token_type string
|
|
|
|
}
|
|
|
|
|
|
|
|
func AuthCode(a App) (authenticationResp, error) {
|
|
|
|
authentication := authenticationResp{}
|
|
|
|
resp, err := http.PostForm("https://login.microsoftonline.com/consumers/oauth2/v2.0/devicecode", url.Values{
|
|
|
|
"client_id": {client_id},
|
|
|
|
"scope": {"XboxLive.SignIn XboxLive.offline_access"},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return authentication, fmt.Errorf("Unable to request device code: %e\n", err)
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 {
|
|
|
|
return authentication, fmt.Errorf("Unable to request device code: %s\n", resp.Status)
|
|
|
|
}
|
|
|
|
data, _ := io.ReadAll(resp.Body)
|
|
|
|
codeResp := devcodeResp{}
|
|
|
|
json.Unmarshal(data, &codeResp)
|
|
|
|
//display message
|
|
|
|
wruntime.MessageDialog(a.Ctx, wruntime.MessageDialogOptions{Type: wruntime.InfoDialog, Title: "Authentication", Message: codeResp.Message})
|
|
|
|
ticker := time.NewTicker(time.Second * time.Duration(codeResp.Interval))
|
|
|
|
for range ticker.C {
|
|
|
|
resp, err := http.PostForm("https://login.microsoftonline.com/consumers/oauth2/v2.0/token", url.Values{
|
|
|
|
"client_id": {client_id},
|
|
|
|
"grant_type": {"urn:ietf:params:oauth:grant-type:device_code"},
|
|
|
|
"device_code": {codeResp.Device_code},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return authentication, fmt.Errorf("Authentication request error %e\n", err)
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
data, _ := io.ReadAll(resp.Body)
|
|
|
|
json.Unmarshal(data, &authentication)
|
|
|
|
if authentication.Error == "" {
|
|
|
|
return authentication, nil
|
|
|
|
}
|
|
|
|
}
|
|
|
|
return authentication, fmt.Errorf("Unknown error")
|
|
|
|
}
|
|
|
|
|
|
|
|
func TokenRefresh(app App, auth authenticationResp) (authenticationResp, error) {
|
|
|
|
resp, err := http.PostForm("https://login.microsoftonline.com/consumers/oauth2/v2.0/token", url.Values{
|
|
|
|
"client_id": {client_id},
|
|
|
|
"grant_type": {"refresh_token"},
|
|
|
|
"refresh_token": {auth.Refresh_token},
|
|
|
|
"scope": {"XboxLive.SignIn XboxLive.offline_access"},
|
|
|
|
})
|
|
|
|
if err != nil {
|
|
|
|
return authenticationResp{}, fmt.Errorf("unable to request refresh token: %e\n", err)
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
data, _ := io.ReadAll(resp.Body)
|
|
|
|
authResp := authenticationResp{}
|
|
|
|
json.Unmarshal(data, &authResp)
|
|
|
|
return authResp, nil
|
|
|
|
}
|
2024-10-30 17:16:10 -06:00
|
|
|
|
|
|
|
func MicrosoftAuth(auth authenticationResp) (LauncherAuth, error) {
|
|
|
|
//Xbox Live Auth
|
|
|
|
req, _ := json.Marshal(xboxAuthRequest{Properties: xboxAuthProperties{AuthMethod: "RPS", SiteName: "user.auth.xboxlive.com", RpsTicket: "d="+auth.Access_token}, RelyingParty: "http://auth.xboxlive.com", TokenType: "JWT"})
|
|
|
|
client := http.Client{}
|
|
|
|
httpreq, _ := http.NewRequest("POST", "https://user.auth.xboxlive.com/user/authenticate", bytes.NewBuffer(req))
|
|
|
|
httpreq.Header.Add("x-xbl-contract-version", "1")
|
|
|
|
httpreq.Header.Add("Content-Type", "application/json")
|
|
|
|
httpreq.Header.Add("Accept", "application/json")
|
|
|
|
httpResp, err := client.Do(httpreq)
|
|
|
|
if err != nil {
|
|
|
|
return LauncherAuth{}, fmt.Errorf("unable to obtain xbox live token: %e\n", err)
|
|
|
|
}
|
|
|
|
defer httpResp.Body.Close()
|
|
|
|
if httpResp.StatusCode != 200 {
|
|
|
|
return LauncherAuth{}, fmt.Errorf("unable to obtain xbox live token: %s\n", httpResp.Status)
|
|
|
|
}
|
|
|
|
d,_ := io.ReadAll(httpResp.Body)
|
|
|
|
xblAuth := xboxAuthResponse{}
|
|
|
|
json.Unmarshal(d, &xblAuth)
|
|
|
|
xstsData, _ := json.Marshal(XSTSRequest{Properties: XSTSProperties{SandboxId: "RETAIL", UserTokens: []string{xblAuth.Token}}, RelyingParty: "rp://api.minecraftservices.com/", TokenType: "JWT"})
|
|
|
|
httpXstsReq, _ := http.NewRequest("POST", "https://xsts.auth.xboxlive.com/xsts/authorize", bytes.NewBuffer(xstsData))
|
|
|
|
httpXstsReq.Header.Add("Content-Type", "application/json")
|
|
|
|
httpResp, err = client.Do(httpXstsReq)
|
|
|
|
if err != nil {
|
|
|
|
return LauncherAuth{}, fmt.Errorf("unable to obtain minecraft sts token: %e\n", err)
|
|
|
|
}
|
|
|
|
defer httpResp.Body.Close()
|
|
|
|
if httpResp.StatusCode != 200 {
|
|
|
|
return LauncherAuth{}, fmt.Errorf("unable to obtain minecraft sts token: %s\n", httpResp.Status)
|
|
|
|
}
|
|
|
|
d, _ = io.ReadAll(httpResp.Body)
|
|
|
|
mcApi := xboxAuthResponse{}
|
|
|
|
json.Unmarshal(d, &mcApi)
|
|
|
|
mcAuthData, _ := json.Marshal(McAuthRequest{Xtoken: "XBL 3.0 x=" + mcApi.DisplayClaims.Xui[0].Uhs+ ";" + mcApi.Token, Platform: "PC_LAUNCHER"})
|
|
|
|
httpReqMC, _ := http.NewRequest("POST", "https://api.minecraftservices.com/launcher/login", bytes.NewBuffer(mcAuthData))
|
|
|
|
httpReqMC.Header.Add("Content-Type", "application/json")
|
|
|
|
httpReqMC.Header.Add("Accept", "application/json")
|
|
|
|
resp, err := client.Do(httpReqMC)
|
|
|
|
if err != nil {
|
|
|
|
return LauncherAuth{}, fmt.Errorf("unable to obtain mojang auth token: %e\n", err)
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 {
|
|
|
|
return LauncherAuth{}, fmt.Errorf("unable to obtain mojang auth token: %s\n", resp.Status)
|
|
|
|
}
|
|
|
|
d, _ = io.ReadAll(resp.Body)
|
|
|
|
mcAuth := McAuthResponse{}
|
|
|
|
json.Unmarshal(d, &mcAuth)
|
|
|
|
httpreq, err = http.NewRequest("GET", "https://api.minecraftservices.com/minecraft/profile", new(bytes.Buffer))
|
|
|
|
httpreq.Header.Add("Content-Type", "application/json")
|
|
|
|
httpreq.Header.Add("Accept", "application/json")
|
|
|
|
httpreq.Header.Add("Authorization", "Bearer "+mcAuth.Access_token)
|
|
|
|
resp, _ = client.Do(httpreq)
|
|
|
|
if err != nil {
|
|
|
|
return LauncherAuth{}, fmt.Errorf("unable to get profile data: %e\n", err)
|
|
|
|
}
|
|
|
|
defer resp.Body.Close()
|
|
|
|
if resp.StatusCode != 200 {
|
|
|
|
return LauncherAuth{}, fmt.Errorf("unable to get profile data: %s\n", resp.Status)
|
|
|
|
}
|
|
|
|
data, _ := io.ReadAll(resp.Body)
|
|
|
|
profile := McProfile{}
|
|
|
|
json.Unmarshal(data, &profile)
|
|
|
|
return LauncherAuth{Id: profile.Id, Name: profile.Name, Token: mcAuth.Access_token}, nil
|
|
|
|
}
|