From 806f6e2dbf763906fdb162bdfd1f6c7ac83ee481 Mon Sep 17 00:00:00 2001 From: Samuel Walker Date: Wed, 30 Oct 2024 16:41:03 -0600 Subject: [PATCH] initial microsoft auth --- fclauncher/app.go | 31 ++++++++- fclauncher/auth.go | 153 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 fclauncher/auth.go diff --git a/fclauncher/app.go b/fclauncher/app.go index d4f6f05..0d9d9c5 100644 --- a/fclauncher/app.go +++ b/fclauncher/app.go @@ -2,7 +2,11 @@ package main import ( "context" + "encoding/json" "fmt" + "io" + "os" + "path/filepath" "github.com/wailsapp/wails/v2/pkg/runtime" ) @@ -17,6 +21,7 @@ type App struct { Java JavaManager Instance InstanceManager Modpacks ModpackManager + Auth authenticationResp } @@ -64,7 +69,31 @@ func (a *App) CheckPrerequisites() { a.Java.InstallJavaVer(21) a.Status("Java 21 Installed") } - + dir, _ := os.UserConfigDir() + authenticated := false + if _, err := os.Stat(filepath.Join(dir, "FCLauncher", "authentication.json")); err == nil { + f, _ := os.OpenFile(filepath.Join(dir, "FCLauncher", "authentication.json"), os.O_RDONLY, 0755) + defer f.Close() + data, _ := io.ReadAll(f) + json.Unmarshal(data, &a.Auth) + a.Auth, err = TokenRefresh(*a, a.Auth) + if err == nil { + authenticated = true + } + } + if !authenticated { + var err error + a.Auth, err = AuthCode(*a) + if err != nil { + fmt.Printf("Authentication Error: %s\n", err) + return + } + } + os.MkdirAll(filepath.Join(dir, "FCLauncher"), 0755) + f, _ := os.OpenFile(filepath.Join(dir, "FCLauncher", "authentication.json"), os.O_CREATE|os.O_RDWR, 0755) + defer f.Close() + data, _ := json.Marshal(a.Auth) + f.Write(data) } func (App) GetVersions() ([]string, error) { diff --git a/fclauncher/auth.go b/fclauncher/auth.go new file mode 100644 index 0000000..5ec10f3 --- /dev/null +++ b/fclauncher/auth.go @@ -0,0 +1,153 @@ +package main + +import ( + "encoding/json" + "fmt" + "io" + "net/http" + "net/url" + "time" + + wruntime "github.com/wailsapp/wails/v2/pkg/runtime" +) + + +type LauncherAuth struct { + Id string + Name string + token string +} + +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 +}