server: add a WIP middleware example

This commit is contained in:
Michael Muré
2024-09-18 12:10:08 +02:00
committed by Michael Muré
parent 3ec8f56412
commit 1178e51b18
2 changed files with 108 additions and 0 deletions

28
toolkit/server/context.go Normal file
View File

@@ -0,0 +1,28 @@
package server
import "context"
type contextKey string
var ctxUserId = contextKey("userId")
var ctxProjectId = contextKey("projectId")
// ContextGetUserId return the UserId stored in the context, if it exists.
func ContextGetUserId(ctx context.Context) (string, bool) {
val, ok := ctx.Value(ctxUserId).(string)
return val, ok
}
func addUserIdToContext(ctx context.Context, userId string) context.Context {
return context.WithValue(ctx, ctxUserId, userId)
}
// ContextGetProjectId return the ProjectID stored in the context, if it exists.
func ContextGetProjectId(ctx context.Context) (string, bool) {
val, ok := ctx.Value(ctxProjectId).(string)
return val, ok
}
func addProjectIdToContext(ctx context.Context, projectId string) context.Context {
return context.WithValue(ctx, ctxProjectId, projectId)
}

View File

@@ -0,0 +1,80 @@
package server
import (
"encoding/base64"
"io"
"net/http"
"strings"
"github.com/ucan-wg/go-ucan/delegation"
"github.com/ucan-wg/go-ucan/did"
)
type Middleware func(http.Handler) http.Handler
func ExampleMiddleware(serviceDID did.DID) Middleware {
return func(next http.Handler) http.Handler {
return http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) {
ctx := r.Context()
tokenReader, ok := extractBearerToken(r)
if !ok {
http.Error(w, "missing or malformed UCAN HTTP Bearer token", http.StatusUnauthorized)
return
}
// decode
// TODO: ultimately, this token should be a container with one invocation and 1+ delegations.
// We are doing something simpler for now.
dlg, err := delegation.FromDagCborReader(tokenReader)
if err != nil {
http.Error(w, "malformed UCAN delegation", http.StatusBadRequest)
return
}
// optional: http-bearer
// validate
if dlg.Subject() != serviceDID {
http.Error(w, "invalid UCAN delegation", http.StatusBadRequest)
return
}
// TODO: policies check + inject in context
// extract values
userId, err := dlg.Meta().GetString("userId")
if err != nil {
http.Error(w, "missing or malformed userId", http.StatusBadRequest)
return
}
projectId, err := dlg.Meta().GetString("projectId")
if err != nil {
http.Error(w, "missing or malformed projectId", http.StatusBadRequest)
return
}
// inject into context
ctx = addUserIdToContext(ctx, userId)
ctx = addProjectIdToContext(ctx, projectId)
next.ServeHTTP(w, r.WithContext(ctx))
})
}
}
func extractBearerToken(r *http.Request) (io.Reader, bool) {
header := r.Header.Get("Authorization")
if header == "" {
return nil, false
}
if !strings.HasPrefix(header, "Bearer ") {
return nil, false
}
// skip prefix
reader := strings.NewReader(header[len("Bearer "):])
// base64 decode
return base64.NewDecoder(base64.StdEncoding, reader), true
}