Extract access tokens from HTTP requests
ADD
src/github.com/matrix-org/dendrite/clientapi/auth/auth.go
+package auth
+
+import (
+ "fmt"
+ "net/http"
+ "strings"
+
+ "github.com/matrix-org/dendrite/clientapi/jsonerror"
+ "github.com/matrix-org/util"
+)
+
+// VerifyAccessToken verifies that an access token was supplied in the given HTTP request
+// and returns the user ID it corresponds to. Returns resErr (an error response which can be
+// sent to the client) if the token is invalid or there was a problem querying the database.
+func VerifyAccessToken(req *http.Request) (userID string, resErr *util.JSONResponse) {
+ token, tokenErr := extractAccessToken(req)
+ if tokenErr != nil {
+ resErr = &util.JSONResponse{
+ Code: 401,
+ JSON: jsonerror.MissingToken(tokenErr.Error()),
+ }
+ return
+ }
+ if token == "fail" {
+ res := util.ErrorResponse(fmt.Errorf("Fatal error"))
+ resErr = &res
+ }
+ // TODO: Check the token against the database
+ return
+}
+
+// extractAccessToken from a request, or return an error detailing what went wrong. The
+// error message MUST be human-readable and comprehensible to the client.
+func extractAccessToken(req *http.Request) (string, error) {
+ // cf https://github.com/matrix-org/synapse/blob/v0.19.2/synapse/api/auth.py#L631
+ authBearer := req.Header.Get("Authorization")
+ queryToken := req.URL.Query().Get("access_token")
+ if authBearer != "" && queryToken != "" {
+ return "", fmt.Errorf("mixing Authorization headers and access_token query parameters")
+ }
+
+ if queryToken != "" {
+ return queryToken, nil
+ }
+
+ if authBearer != "" {
+ parts := strings.SplitN(authBearer, " ", 2)
+ if len(parts) != 2 || parts[0] != "Bearer" {
+ return "", fmt.Errorf("invalid Authorization header")
+ }
+ return parts[1], nil
+ }
+
+ return "", fmt.Errorf("missing access token")
+}

MODIFY
src/github.com/matrix-org/dendrite/clientapi/jsonerror/jsonerror.go
src/github.com/matrix-org/dendrite/clientapi/jsonerror/jsonerror.go
package jsonerror
-import "fmt"
+import (
+ "fmt"
+ "github.com/matrix-org/util"
+)
// MatrixError represents the "standard error response" in Matrix.
// http://matrix.org/docs/spec/client_server/r0.2.0.html#api-standards
@@ -13,6 +16,20 @@ func (e *MatrixError) Error() string {
return fmt.Sprintf("%s: %s", e.ErrCode, e.Err)
}
+// InternalServerError returns a 500 Internal Server Error in a matrix-compliant
+// format.
+func InternalServerError() util.JSONResponse {
+ return util.JSONResponse{
+ Code: 500,
+ JSON: Unknown("Internal Server Error"),
+ }
+}
+
+// Unknown is an unexpected error
+func Unknown(msg string) *MatrixError {
+ return &MatrixError{"M_UNKNOWN", msg}
+}
+
// Forbidden is an error when the client tries to access a resource
// they are not allowed to access.
func Forbidden(msg string) *MatrixError {

MODIFY
src/github.com/matrix-org/dendrite/clientapi/readers/sync.go
src/github.com/matrix-org/dendrite/clientapi/readers/sync.go
@@ -3,12 +3,18 @@ package readers
import (
"net/http"
+ "github.com/matrix-org/dendrite/clientapi/auth"
"github.com/matrix-org/util"
)
// Sync implements /sync
func Sync(req *http.Request) util.JSONResponse {
logger := util.GetLogger(req.Context())
- logger.Info("Doing stuff...")
+ userID, resErr := auth.VerifyAccessToken(req)
+ if resErr != nil {
+ return *resErr
+ }
+
+ logger.WithField("userID", userID).Info("Doing stuff...")
return util.MessageResponse(404, "Not implemented yet")
}

MODIFY
src/github.com/matrix-org/dendrite/clientapi/writers/sendmessage.go
src/github.com/matrix-org/dendrite/clientapi/writers/sendmessage.go
@@ -3,12 +3,22 @@ package writers
import (
"net/http"
+ log "github.com/Sirupsen/logrus"
+ "github.com/matrix-org/dendrite/clientapi/auth"
"github.com/matrix-org/util"
)
// SendMessage implements /rooms/{roomID}/send/{eventType}
func SendMessage(req *http.Request, roomID, eventType string) util.JSONResponse {
logger := util.GetLogger(req.Context())
- logger.WithField("roomID", roomID).WithField("eventType", eventType).Info("Doing stuff...")
+ userID, resErr := auth.VerifyAccessToken(req)
+ if resErr != nil {
+ return *resErr
+ }
+ logger.WithFields(log.Fields{
+ "roomID": roomID,
+ "eventType": eventType,
+ "userID": userID,
+ }).Info("Doing stuff...")
return util.MessageResponse(404, "Not implemented yet")
}

Extract access tokens from HTTP requests (#15)