tripwire/auth.go

236 lines
5.4 KiB
Go

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")
}