diff --git a/src/github.com/turt2live/matrix-media-repo/client/r0/upload.go b/src/github.com/turt2live/matrix-media-repo/client/r0/upload.go
index 91251d295a8f5b547ccbd7f8be9a2470b685edb6..a7a33f89b8d9bb1490cb1aca44874fab6c42f395 100644
--- a/src/github.com/turt2live/matrix-media-repo/client/r0/upload.go
+++ b/src/github.com/turt2live/matrix-media-repo/client/r0/upload.go
@@ -8,6 +8,7 @@ import (
 	"github.com/turt2live/matrix-media-repo/config"
 	"github.com/turt2live/matrix-media-repo/media_handler"
 	"github.com/turt2live/matrix-media-repo/storage"
+	"github.com/turt2live/matrix-media-repo/util"
 )
 
 // Request:
@@ -23,7 +24,14 @@ type MediaUploadedResponse struct {
 }
 
 func UploadMedia(w http.ResponseWriter, r *http.Request, db storage.Database, c config.MediaRepoConfig) interface{} {
-	// TODO: Validate access_token
+	if !util.IsServerOurs(r.Host, c) {
+		return client.AuthFailed()
+	}
+	accessToken := util.GetAccessTokenFromRequest(r)
+	userId, err := util.GetUserIdFromToken(r.Context(), r.Host, accessToken, c)
+	if err != nil || userId == "" {
+		return client.AuthFailed()
+	}
 
 	filename := r.URL.Query().Get("filename")
 	if filename == "" {
@@ -42,7 +50,7 @@ func UploadMedia(w http.ResponseWriter, r *http.Request, db storage.Database, c
 	}
 
 	request := &media_handler.MediaUploadRequest{
-		UploadedBy:      "",
+		UploadedBy:      userId,
 		ContentType:     contentType,
 		DesiredFilename: filename,
 		Host:            r.Host,
diff --git a/src/github.com/turt2live/matrix-media-repo/client/responses.go b/src/github.com/turt2live/matrix-media-repo/client/responses.go
index d09b99957fe255ddbbee93243a0921b07253c08c..bb3f1fc8812230c85dca8b632a1e7b61c4909d67 100644
--- a/src/github.com/turt2live/matrix-media-repo/client/responses.go
+++ b/src/github.com/turt2live/matrix-media-repo/client/responses.go
@@ -16,4 +16,8 @@ func NotFoundError() *ErrorResponse {
 
 func RequestTooLarge() *ErrorResponse {
 	return &ErrorResponse{"M_UNKNOWN", "Too Large", "M_MEDIA_TOO_LARGE"}
+}
+
+func AuthFailed() *ErrorResponse {
+	return &ErrorResponse{"M_UNKNOWN_TOKEN", "Authenticaton Failed", "M_UNKNOWN_TOKEN"}
 }
\ No newline at end of file
diff --git a/src/github.com/turt2live/matrix-media-repo/config/config.go b/src/github.com/turt2live/matrix-media-repo/config/config.go
index 8f7961d803fb88ebb69d0e04380bb966f94fef6c..5d52f4f3e3a25617940456caf1789a841f7ff18a 100644
--- a/src/github.com/turt2live/matrix-media-repo/config/config.go
+++ b/src/github.com/turt2live/matrix-media-repo/config/config.go
@@ -7,12 +7,14 @@ import (
 	"gopkg.in/yaml.v2"
 )
 
+type HomeserverConfig struct {
+	Name string `yaml:"name"`
+	DownloadRequiresAuth bool `yaml:"downloadRequiresAuth"`
+	ClientServerApi string `yaml:"csApi"`
+}
+
 type MediaRepoConfig struct {
-	Homeservers []struct {
-		Name string `yaml:"name"`
-		DownloadRequiresAuth bool `yaml:"downloadRequiresAuth"`
-		ClientServerApi string `yaml:"csApi"`
-	} `yaml:"homeservers,flow"`
+	Homeservers []HomeserverConfig `yaml:"homeservers,flow"`
 
 	Database struct {
 		Postgres string `yaml:"postgres"`
diff --git a/src/github.com/turt2live/matrix-media-repo/media_repo.go b/src/github.com/turt2live/matrix-media-repo/media_repo.go
index c0411f4c00bbf6a3c8aa5982e1973019999790a7..1d4b12ffddd194fec3766621d174ea7478c8c630 100644
--- a/src/github.com/turt2live/matrix-media-repo/media_repo.go
+++ b/src/github.com/turt2live/matrix-media-repo/media_repo.go
@@ -59,6 +59,9 @@ func main() {
 	rtr.Handle("/_matrix/media/v1/download/{server:[a-zA-Z0-9.:-_]+}/{mediaId:[a-zA-Z0-9]+}/{filename:[a-zA-Z0-9._-]+}", downloadHandler).Methods("GET")
 	rtr.Handle("/_matrix/media/v1/thumbnail/{server:[a-zA-Z0-9.:-_]+}/{mediaId:[a-zA-Z0-9]+}", thumbnailHandler).Methods("GET")
 
+	// TODO: Intercept 404, 500, and 400 to respond with M_NOT_FOUND and M_UNKNOWN
+	// TODO: Rate limiting (429 M_LIMIT_EXCEEDED)
+
 	http.Handle("/", rtr)
 	http.ListenAndServe(":8000", nil)
 }
@@ -88,18 +91,19 @@ func (h Handler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
 
 	switch result := res.(type) {
 	case *client.ErrorResponse:
+		w.Header().Set("Content-Type", "application/json")
 		switch result.InternalCode {
+		case "M_UNKNOWN_TOKEN":
+			http.Error(w, jsonStr, http.StatusForbidden)
+			break
 		case "M_NOT_FOUND":
-			w.Header().Set("Content-Type", "application/json")
 			http.Error(w, jsonStr, http.StatusNotFound)
 			break
 		case "M_MEDIA_TOO_LARGE":
-			w.Header().Set("Content-Type", "application/json")
 			http.Error(w, jsonStr, http.StatusRequestEntityTooLarge)
 			break
 		//case "M_UNKNOWN":
 		default:
-			w.Header().Set("Content-Type", "application/json")
 			http.Error(w, jsonStr, http.StatusInternalServerError)
 			break
 		}
diff --git a/src/github.com/turt2live/matrix-media-repo/util/config.go b/src/github.com/turt2live/matrix-media-repo/util/config.go
index 6e38a2cee0e0d823dc72cf6f7b332745c9e68ec5..feeab34037db5db8bf0e8c8cfb3bc67206859fe7 100644
--- a/src/github.com/turt2live/matrix-media-repo/util/config.go
+++ b/src/github.com/turt2live/matrix-media-repo/util/config.go
@@ -3,12 +3,17 @@ package util
 import "github.com/turt2live/matrix-media-repo/config"
 
 func IsServerOurs(server string, c config.MediaRepoConfig) (bool) {
+	hs := GetHomeserverConfig(server, c)
+	return hs != nil
+}
+
+func GetHomeserverConfig(server string, c config.MediaRepoConfig) (*config.HomeserverConfig) {
 	for i := 0; i < len(c.Homeservers); i++ {
 		hs := c.Homeservers[i]
 		if hs.Name == server {
-			return true
+			return &hs
 		}
 	}
 
-	return false
-}
+	return nil
+}
\ No newline at end of file
diff --git a/src/github.com/turt2live/matrix-media-repo/util/http.go b/src/github.com/turt2live/matrix-media-repo/util/http.go
new file mode 100644
index 0000000000000000000000000000000000000000..1161672b26884631db863ccee773760f562a0457
--- /dev/null
+++ b/src/github.com/turt2live/matrix-media-repo/util/http.go
@@ -0,0 +1,15 @@
+package util
+
+import (
+	"net/http"
+)
+
+func GetAccessTokenFromRequest(request *http.Request) (string) {
+	token := request.Header.Get("Authorization")
+	if token != "" && len(token) > 7 {
+		// "Bearer <token>"
+		return token[7:]
+	}
+
+	return request.URL.Query().Get("access_token")
+}
\ No newline at end of file
diff --git a/src/github.com/turt2live/matrix-media-repo/util/matrix.go b/src/github.com/turt2live/matrix-media-repo/util/matrix.go
new file mode 100644
index 0000000000000000000000000000000000000000..fc47f26c7d39717659c224458f9f05fddcb1dd6e
--- /dev/null
+++ b/src/github.com/turt2live/matrix-media-repo/util/matrix.go
@@ -0,0 +1,29 @@
+package util
+
+import (
+	"context"
+
+	"github.com/matrix-org/gomatrix"
+	"github.com/turt2live/matrix-media-repo/config"
+)
+
+type userIdResponse struct {
+	UserId string `json:"user_id"`
+}
+
+func GetUserIdFromToken(ctx context.Context, serverName string, accessToken string, c config.MediaRepoConfig) (string, error) {
+	hs := GetHomeserverConfig(serverName, c)
+	mtxClient, err := gomatrix.NewClient(hs.ClientServerApi, "", accessToken)
+	if err != nil {
+		return "", err
+	}
+
+	response := &userIdResponse{}
+	url := mtxClient.BuildURL("/account/whoami")
+	_, err = mtxClient.MakeRequest("GET", url, nil, response)
+	if err != nil {
+		return "", err
+	}
+
+	return response.UserId, nil
+}
\ No newline at end of file