From 42e1d462dac74b48c59977dbe82733f7b46a116e Mon Sep 17 00:00:00 2001 From: Denis Mishin Date: Thu, 24 Apr 2025 11:11:50 -0400 Subject: [PATCH] save session in the authorization request --- authorize/evaluator/evaluator.go | 1 + internal/mcp/handler_authorization.go | 32 +++++++++++- internal/oauth21/authorize.go | 3 +- .../oauth21/gen/authorization_request.pb.go | 49 ++++++++++++------- .../oauth21/proto/authorization_request.proto | 4 ++ 5 files changed, 69 insertions(+), 20 deletions(-) diff --git a/authorize/evaluator/evaluator.go b/authorize/evaluator/evaluator.go index e0d5b7026..b6ebe7c58 100644 --- a/authorize/evaluator/evaluator.go +++ b/authorize/evaluator/evaluator.go @@ -292,6 +292,7 @@ var internalPathsNeedingLogin = set.From([]string{ "/.pomerium/webauthn", "/.pomerium/routes", "/.pomerium/api/v1/routes", + "/.pomerium/mcp/authorize", }) func (e *Evaluator) evaluateInternal(_ context.Context, req *Request) (*PolicyResponse, error) { diff --git a/internal/mcp/handler_authorization.go b/internal/mcp/handler_authorization.go index 292930432..e15fdbb00 100644 --- a/internal/mcp/handler_authorization.go +++ b/internal/mcp/handler_authorization.go @@ -2,11 +2,14 @@ package mcp import ( "errors" + "fmt" "net/http" + "github.com/go-jose/go-jose/v3/jwt" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" + "github.com/pomerium/pomerium/internal/httputil" "github.com/pomerium/pomerium/internal/log" "github.com/pomerium/pomerium/internal/oauth21" ) @@ -20,7 +23,14 @@ func (srv *Handler) Authorize(w http.ResponseWriter, r *http.Request) { ctx := r.Context() - v, err := oauth21.ParseCodeGrantAuthorizeRequest(r) + sessionID, err := getSessionFromRequest(r) + if err != nil { + log.Ctx(ctx).Error().Err(err).Msg("session is not present, this is a misconfigured request") + http.Error(w, "internal server error", http.StatusInternalServerError) + return + } + + v, err := oauth21.ParseCodeGrantAuthorizeRequest(r, sessionID) if err != nil { log.Ctx(ctx).Error().Err(err).Msg("failed to parse authorization request") oauth21.ErrorResponse(w, http.StatusBadRequest, oauth21.InvalidRequest) @@ -55,3 +65,23 @@ func (srv *Handler) Authorize(w http.ResponseWriter, r *http.Request) { http.Error(w, "not implemented", http.StatusNotImplemented) } + +func getSessionFromRequest(r *http.Request) (string, error) { + h := r.Header.Get(httputil.HeaderPomeriumJWTAssertion) + if h == "" { + return "", fmt.Errorf("missing %s header", httputil.HeaderPomeriumJWTAssertion) + } + + token, err := jwt.ParseSigned(h) + if err != nil { + return "", fmt.Errorf("failed to parse JWT: %w", err) + } + var m map[string]any + _ = token.UnsafeClaimsWithoutVerification(&m) + sessionID, ok := m["sid"].(string) + if !ok { + return "", fmt.Errorf("missing session ID in JWT") + } + + return sessionID, nil +} diff --git a/internal/oauth21/authorize.go b/internal/oauth21/authorize.go index fce6aceb0..bd0fc0d4d 100644 --- a/internal/oauth21/authorize.go +++ b/internal/oauth21/authorize.go @@ -12,7 +12,7 @@ import ( // ParseCodeGrantAuthorizeRequest parses the authorization request for the code grant flow. // see https://datatracker.ietf.org/doc/html/draft-ietf-oauth-v2-1-12#section-4.1.1 // scopes are ignored -func ParseCodeGrantAuthorizeRequest(r *http.Request) (*gen.AuthorizationRequest, error) { +func ParseCodeGrantAuthorizeRequest(r *http.Request, sessionID string) (*gen.AuthorizationRequest, error) { if err := r.ParseForm(); err != nil { return nil, fmt.Errorf("failed to parse form: %w", err) } @@ -24,6 +24,7 @@ func ParseCodeGrantAuthorizeRequest(r *http.Request) (*gen.AuthorizationRequest, State: optionalFormParam(r, "state"), CodeChallenge: r.Form.Get("code_challenge"), CodeChallengeMethod: optionalFormParam(r, "code_challenge_method"), + SessionId: sessionID, } if err := protovalidate.Validate(v); err != nil { diff --git a/internal/oauth21/gen/authorization_request.pb.go b/internal/oauth21/gen/authorization_request.pb.go index 756e88042..5f465c9e7 100644 --- a/internal/oauth21/gen/authorization_request.pb.go +++ b/internal/oauth21/gen/authorization_request.pb.go @@ -48,8 +48,11 @@ type AuthorizationRequest struct { // OPTIONAL, defaults to plain if not present in the request. Code verifier // transformation method is S256 or plain. CodeChallengeMethod *string `protobuf:"bytes,7,opt,name=code_challenge_method,json=codeChallengeMethod,proto3,oneof" json:"code_challenge_method,omitempty"` - unknownFields protoimpl.UnknownFields - sizeCache protoimpl.SizeCache + // session this authorization request is associated with. + // This is a Pomerium implementation specific field. + SessionId string `protobuf:"bytes,8,opt,name=session_id,json=sessionId,proto3" json:"session_id,omitempty"` + unknownFields protoimpl.UnknownFields + sizeCache protoimpl.SizeCache } func (x *AuthorizationRequest) Reset() { @@ -131,6 +134,13 @@ func (x *AuthorizationRequest) GetCodeChallengeMethod() string { return "" } +func (x *AuthorizationRequest) GetSessionId() string { + if x != nil { + return x.SessionId + } + return "" +} + var File_authorization_request_proto protoreflect.FileDescriptor var file_authorization_request_proto_rawDesc = string([]byte{ @@ -138,7 +148,7 @@ var file_authorization_request_proto_rawDesc = string([]byte{ 0x72, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x2e, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x12, 0x07, 0x6f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x31, 0x1a, 0x1b, 0x62, 0x75, 0x66, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2e, 0x70, 0x72, - 0x6f, 0x74, 0x6f, 0x22, 0x83, 0x03, 0x0a, 0x14, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, + 0x6f, 0x74, 0x6f, 0x22, 0xaa, 0x03, 0x0a, 0x14, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x12, 0x23, 0x0a, 0x09, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x5f, 0x69, 0x64, 0x18, 0x01, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, 0x52, 0x08, 0x63, 0x6c, 0x69, 0x65, 0x6e, 0x74, 0x49, @@ -159,21 +169,24 @@ var file_authorization_request_proto_rawDesc = string([]byte{ 0x18, 0x07, 0x20, 0x01, 0x28, 0x09, 0x42, 0x12, 0xba, 0x48, 0x0f, 0x72, 0x0d, 0x52, 0x04, 0x53, 0x32, 0x35, 0x36, 0x52, 0x05, 0x70, 0x6c, 0x61, 0x69, 0x6e, 0x48, 0x02, 0x52, 0x13, 0x63, 0x6f, 0x64, 0x65, 0x43, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x4d, 0x65, 0x74, 0x68, 0x6f, - 0x64, 0x88, 0x01, 0x01, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, - 0x74, 0x5f, 0x75, 0x72, 0x69, 0x42, 0x08, 0x0a, 0x06, 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x42, - 0x18, 0x0a, 0x16, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x5f, 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, - 0x67, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, 0x42, 0xac, 0x01, 0x0a, 0x0b, 0x63, 0x6f, - 0x6d, 0x2e, 0x6f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x31, 0x42, 0x19, 0x41, 0x75, 0x74, 0x68, 0x6f, - 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, - 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x46, 0x67, 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, - 0x6f, 0x6d, 0x2f, 0x62, 0x75, 0x66, 0x62, 0x75, 0x69, 0x6c, 0x64, 0x2f, 0x62, 0x75, 0x66, 0x2d, - 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x76, 0x61, - 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x71, 0x75, 0x69, 0x63, 0x6b, 0x73, 0x74, 0x61, 0x72, - 0x74, 0x2d, 0x67, 0x6f, 0x2f, 0x73, 0x74, 0x61, 0x72, 0x74, 0x2f, 0x67, 0x65, 0x6e, 0xa2, 0x02, - 0x03, 0x4f, 0x58, 0x58, 0xaa, 0x02, 0x07, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x31, 0xca, 0x02, - 0x07, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x31, 0xe2, 0x02, 0x13, 0x4f, 0x61, 0x75, 0x74, 0x68, - 0x32, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, - 0x07, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x31, 0x62, 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, + 0x64, 0x88, 0x01, 0x01, 0x12, 0x25, 0x0a, 0x0a, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x5f, + 0x69, 0x64, 0x18, 0x08, 0x20, 0x01, 0x28, 0x09, 0x42, 0x06, 0xba, 0x48, 0x03, 0xc8, 0x01, 0x01, + 0x52, 0x09, 0x73, 0x65, 0x73, 0x73, 0x69, 0x6f, 0x6e, 0x49, 0x64, 0x42, 0x0f, 0x0a, 0x0d, 0x5f, + 0x72, 0x65, 0x64, 0x69, 0x72, 0x65, 0x63, 0x74, 0x5f, 0x75, 0x72, 0x69, 0x42, 0x08, 0x0a, 0x06, + 0x5f, 0x73, 0x74, 0x61, 0x74, 0x65, 0x42, 0x18, 0x0a, 0x16, 0x5f, 0x63, 0x6f, 0x64, 0x65, 0x5f, + 0x63, 0x68, 0x61, 0x6c, 0x6c, 0x65, 0x6e, 0x67, 0x65, 0x5f, 0x6d, 0x65, 0x74, 0x68, 0x6f, 0x64, + 0x42, 0xac, 0x01, 0x0a, 0x0b, 0x63, 0x6f, 0x6d, 0x2e, 0x6f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x31, + 0x42, 0x19, 0x41, 0x75, 0x74, 0x68, 0x6f, 0x72, 0x69, 0x7a, 0x61, 0x74, 0x69, 0x6f, 0x6e, 0x52, + 0x65, 0x71, 0x75, 0x65, 0x73, 0x74, 0x50, 0x72, 0x6f, 0x74, 0x6f, 0x50, 0x01, 0x5a, 0x46, 0x67, + 0x69, 0x74, 0x68, 0x75, 0x62, 0x2e, 0x63, 0x6f, 0x6d, 0x2f, 0x62, 0x75, 0x66, 0x62, 0x75, 0x69, + 0x6c, 0x64, 0x2f, 0x62, 0x75, 0x66, 0x2d, 0x65, 0x78, 0x61, 0x6d, 0x70, 0x6c, 0x65, 0x73, 0x2f, + 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x76, 0x61, 0x6c, 0x69, 0x64, 0x61, 0x74, 0x65, 0x2f, 0x71, 0x75, + 0x69, 0x63, 0x6b, 0x73, 0x74, 0x61, 0x72, 0x74, 0x2d, 0x67, 0x6f, 0x2f, 0x73, 0x74, 0x61, 0x72, + 0x74, 0x2f, 0x67, 0x65, 0x6e, 0xa2, 0x02, 0x03, 0x4f, 0x58, 0x58, 0xaa, 0x02, 0x07, 0x4f, 0x61, + 0x75, 0x74, 0x68, 0x32, 0x31, 0xca, 0x02, 0x07, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x31, 0xe2, + 0x02, 0x13, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x31, 0x5c, 0x47, 0x50, 0x42, 0x4d, 0x65, 0x74, + 0x61, 0x64, 0x61, 0x74, 0x61, 0xea, 0x02, 0x07, 0x4f, 0x61, 0x75, 0x74, 0x68, 0x32, 0x31, 0x62, + 0x06, 0x70, 0x72, 0x6f, 0x74, 0x6f, 0x33, }) var ( diff --git a/internal/oauth21/proto/authorization_request.proto b/internal/oauth21/proto/authorization_request.proto index 1402774ea..532167ce2 100644 --- a/internal/oauth21/proto/authorization_request.proto +++ b/internal/oauth21/proto/authorization_request.proto @@ -44,4 +44,8 @@ message AuthorizationRequest { // transformation method is S256 or plain. optional string code_challenge_method = 7 [ (buf.validate.field).string = {in : [ "S256", "plain" ]} ]; + + // session this authorization request is associated with. + // This is a Pomerium implementation specific field. + string session_id = 8 [ (buf.validate.field).required = true ]; }