This commit is contained in:
Nipuni Paaris 2025-05-21 08:12:27 +00:00 committed by GitHub
commit 5e1ea3aaf1
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
15 changed files with 609 additions and 100 deletions

View file

@ -0,0 +1,24 @@
package authz
import (
"net/http"
"github.com/golang-jwt/jwt/v4"
"github.com/wso2/open-mcp-auth-proxy/internal/config"
)
type Decision int
const (
DecisionAllow Decision = iota
DecisionDeny
)
type AccessControlResult struct {
Decision Decision
Message string
}
type AccessControl interface {
ValidateAccess(r *http.Request, claims *jwt.MapClaims, config *config.Config) AccessControlResult
}

View file

@ -350,3 +350,23 @@ func randomString(n int) string {
}
return string(b)
}
func (p *asgardeoProvider) ProtectedResourceMetadataHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
meta := map[string]interface{}{
"resource": p.cfg.ResourceIdentifier,
"scopes_supported": p.cfg.ScopesSupported,
"authorization_servers": p.cfg.AuthorizationServers,
}
if p.cfg.JwksURI != "" {
meta["jwks_uri"] = p.cfg.JwksURI
}
if len(p.cfg.BearerMethodsSupported) > 0 {
meta["bearer_methods_supported"] = p.cfg.BearerMethodsSupported
}
if err := json.NewEncoder(w).Encode(meta); err != nil {
http.Error(w, "failed to encode metadata", http.StatusInternalServerError)
}
}
}

View file

@ -94,3 +94,27 @@ func (p *defaultProvider) WellKnownHandler() http.HandlerFunc {
func (p *defaultProvider) RegisterHandler() http.HandlerFunc {
return nil
}
func (p *defaultProvider) ProtectedResourceMetadataHandler() http.HandlerFunc {
return func(w http.ResponseWriter, r *http.Request) {
w.Header().Set("Content-Type", "application/json")
meta := map[string]interface{}{
"audience": p.cfg.Audience,
"resource": p.cfg.ResourceIdentifier,
"scopes_supported": p.cfg.ScopesSupported,
"authorization_servers": p.cfg.AuthorizationServers,
}
if p.cfg.JwksURI != "" {
meta["jwks_uri"] = p.cfg.JwksURI
}
if len(p.cfg.BearerMethodsSupported) > 0 {
meta["bearer_methods_supported"] = p.cfg.BearerMethodsSupported
}
if err := json.NewEncoder(w).Encode(meta); err != nil {
http.Error(w, "failed to encode metadata", http.StatusInternalServerError)
}
}
}

View file

@ -7,4 +7,5 @@ import "net/http"
type Provider interface {
WellKnownHandler() http.HandlerFunc
RegisterHandler() http.HandlerFunc
ProtectedResourceMetadataHandler() http.HandlerFunc
}

View file

@ -0,0 +1,71 @@
package authz
import (
"fmt"
"net/http"
"strings"
"github.com/golang-jwt/jwt/v4"
"github.com/wso2/open-mcp-auth-proxy/internal/config"
"github.com/wso2/open-mcp-auth-proxy/internal/util"
)
type ScopeValidator struct{}
// Evaluate and checks the token claims against one or more required scopes.
func (d *ScopeValidator) ValidateAccess(
r *http.Request,
claims *jwt.MapClaims,
config *config.Config,
) AccessControlResult {
env, err := util.ParseRPCRequest(r)
if err != nil {
return AccessControlResult{DecisionDeny, "bad JSON-RPC request"}
}
requiredScopes := util.GetRequiredScopes(config, env.Method)
if len(requiredScopes) == 0 {
return AccessControlResult{DecisionAllow, ""}
}
required := make(map[string]struct{}, len(requiredScopes))
for _, s := range requiredScopes {
s = strings.TrimSpace(s)
if s != "" {
required[s] = struct{}{}
}
}
var tokenScopes []string
if claims, ok := (*claims)["scope"]; ok {
switch v := claims.(type) {
case string:
tokenScopes = strings.Fields(v)
case []interface{}:
for _, x := range v {
if s, ok := x.(string); ok && s != "" {
tokenScopes = append(tokenScopes, s)
}
}
}
}
tokenScopeSet := make(map[string]struct{}, len(tokenScopes))
for _, s := range tokenScopes {
tokenScopeSet[s] = struct{}{}
}
var missing []string
for s := range required {
if _, ok := tokenScopeSet[s]; !ok {
missing = append(missing, s)
}
}
if len(missing) == 0 {
return AccessControlResult{DecisionAllow, ""}
}
return AccessControlResult{
DecisionDeny,
fmt.Sprintf("missing required scope(s): %s", strings.Join(missing, ", ")),
}
}