package main import ( "errors" "io/ioutil" "net/http" "os" "github.com/gorilla/mux" ) func rootEndpoint(w http.ResponseWriter, r *http.Request) { sendJSON(w, YggdrasilInfo{ Status: "OK", RuntimeMode: "productionMode", AppAuthor: "Tripwire Team", AppDescription: "Custom Yggdrasil authentication server", SpecVersion: "5.2.0", AppName: "tripwire.yggdrasil", ImplVersion: "5.2.0", AppOwner: "Who knows?", }) } func authenticateEndpoint(w http.ResponseWriter, r *http.Request) { var authPayload AuthPayload err := unmarshalTo(r, &authPayload) if err != nil { handleError(w, err) return } // checks username and password authToken, err := getAuthToken(authPayload.Username, authPayload.Password) if err != nil { err := YggError{Code: 401, Error: "Unauthorized", ErrorMessage: "The username or password is incorrect"} sendError(w, err) return } // authenticated at this point clientToken, err := checkClientToken(authPayload.ClientToken, authPayload.Username) if err != nil { handleError(w, err) return } playeruuid, err := getPlayerUUID(authPayload.Username) if err != nil { handleError(w, err) return } profile := MCProfile{authPayload.Username, playeruuid} authResponse := AuthResponse{ ClientToken: clientToken, AccessToken: authToken, AvailableProfiles: []MCProfile{ profile, }, SelectedProfile: profile, } sendJSON(w, authResponse) } func addUserEndpoint(w http.ResponseWriter, r *http.Request) { var user UserCredentials err := unmarshalTo(r, &user) if err != nil { handleError(w, err) return } // add user to db newPassword, err := createUser(user.Username, user.Password) if err != nil { handleError(w, err) return } // in this case, password is the admin token, not the password to assign // send response respAccount := UserCredentials{ Username: user.Username, Password: newPassword, } sendJSON(w, respAccount) } func refreshTokenEndpoint(w http.ResponseWriter, r *http.Request) { var refreshPayload RefreshPayload err := unmarshalTo(r, &refreshPayload) if err != nil { handleError(w, err) return } responsePayload, err := refreshTokens(refreshPayload) if err != nil { handleError(w, err) return } if refreshPayload == responsePayload { err := YggError{Code: 400, Error: "Bad Request", ErrorMessage: "The access token is invalid or has expired"} sendError(w, err) return } sendJSON(w, responsePayload) } func validateEndpoint(w http.ResponseWriter, r *http.Request) { var refreshPayload RefreshPayload err := unmarshalTo(r, &refreshPayload) if err != nil { handleError(w, err) return } isValid, err := validateTokens(refreshPayload.AccessToken, refreshPayload.ClientToken) if err != nil { handleError(w, err) return } if !isValid { err := YggError{Code: 403, Error: "Bad Request", ErrorMessage: "The access token is invalid or has expired"} sendError(w, err) return } sendEmpty(w) } func signoutEndpoint(w http.ResponseWriter, r *http.Request) { var creds UserCredentials err := unmarshalTo(r, &creds) if err != nil { handleError(w, err) return } err = invalidateTokensWithLogin(creds.Username, creds.Password) if err != nil { handleError(w, err) return } sendEmpty(w) } func invalidateEndpoint(w http.ResponseWriter, r *http.Request) { var refreshPayload RefreshPayload err := unmarshalTo(r, &refreshPayload) if err != nil { handleError(w, err) return } err = invalidateTokens(refreshPayload.AccessToken, refreshPayload.ClientToken) if err != nil { handleError(w, err) return } sendEmpty(w) } func getSkinEndpoint(w http.ResponseWriter, r *http.Request) { uuid := mux.Vars(r)["uuid"] skin, err := getPlayerSkinByUUID(uuid) if err != nil { if !errors.Is(err, &NotFoundError{}) { handleError(w, err) return } skin, err = os.ReadFile("default.png") if err != nil { handleError(w, err) return } } w.Header().Set("Content-Type", "image/png") w.Write(skin) } func setSkinEndpoint(w http.ResponseWriter, r *http.Request) { uuid := mux.Vars(r)["uuid"] r.ParseMultipartForm(int64(config.MaxSkinSize)) pass := r.FormValue("password") correct, err := checkPlayerPassByUUID(uuid, pass) if err != nil { handleError(w, err) return } if !correct { sendError(w, YggError{Code: 400, Error: "Bad Request", ErrorMessage: "Invalid credentials."}) return } file, _, err := r.FormFile("skinfile") if err != nil { handleError(w, err) return } skin, err := ioutil.ReadAll(file) if err != nil { handleError(w, err) return } if len(skin) < 4 || skin[0] != 0x89 || skin[1] != 0x50 || skin[2] != 0x4e || skin[3] != 0x47 { sendError(w, YggError{Code: 400, Error: "Bad Request", ErrorMessage: "The request data is malformed."}) return } err = setPlayerSkinByUUID(uuid, skin) if err != nil { handleError(w, err) return } sendEmpty(w) } func registerAuthEndpoints(r *mux.Router) { r.HandleFunc("/", rootEndpoint) r.HandleFunc("/authenticate", authenticateEndpoint).Methods("POST") r.HandleFunc("/refresh", refreshTokenEndpoint).Methods("POST") r.HandleFunc("/signout", signoutEndpoint).Methods("POST") r.HandleFunc("/invalidate", invalidateEndpoint).Methods("POST") r.HandleFunc("/validate", validateEndpoint).Methods("POST") r.HandleFunc("/admin/addUser", addUserEndpoint).Methods("POST") r.HandleFunc("/getSkin/{uuid}", getSkinEndpoint).Methods("GET") r.HandleFunc("/setSkin/{uuid}", setSkinEndpoint).Methods("POST") }