...

Source file src/github.com/ory/fosite/audience_strategy.go

Documentation: github.com/ory/fosite

     1  package fosite
     2  
     3  import (
     4  	"net/http"
     5  	"net/url"
     6  	"strings"
     7  
     8  	"github.com/ory/x/errorsx"
     9  )
    10  
    11  type AudienceMatchingStrategy func(haystack []string, needle []string) error
    12  
    13  func DefaultAudienceMatchingStrategy(haystack []string, needle []string) error {
    14  	if len(needle) == 0 {
    15  		return nil
    16  	}
    17  
    18  	for _, n := range needle {
    19  		nu, err := url.Parse(n)
    20  		if err != nil {
    21  			return errorsx.WithStack(ErrInvalidRequest.WithHintf("Unable to parse requested audience '%s'.", n).WithWrap(err).WithDebug(err.Error()))
    22  		}
    23  
    24  		var found bool
    25  		for _, h := range haystack {
    26  			hu, err := url.Parse(h)
    27  			if err != nil {
    28  				return errorsx.WithStack(ErrInvalidRequest.WithHintf("Unable to parse whitelisted audience '%s'.", h).WithWrap(err).WithDebug(err.Error()))
    29  			}
    30  
    31  			allowedPath := strings.TrimRight(hu.Path, "/")
    32  			if nu.Scheme == hu.Scheme &&
    33  				nu.Host == hu.Host &&
    34  				(nu.Path == hu.Path ||
    35  					nu.Path == allowedPath ||
    36  					len(nu.Path) > len(allowedPath) && strings.TrimRight(nu.Path[:len(allowedPath)+1], "/")+"/" == allowedPath+"/") {
    37  				found = true
    38  			}
    39  		}
    40  
    41  		if !found {
    42  			return errorsx.WithStack(ErrInvalidRequest.WithHintf("Requested audience '%s' has not been whitelisted by the OAuth 2.0 Client.", n))
    43  		}
    44  	}
    45  
    46  	return nil
    47  }
    48  
    49  // ExactAudienceMatchingStrategy does not assume that audiences are URIs, but compares strings as-is and
    50  // does matching with exact string comparison. It requires that all strings in "needle" are present in
    51  // "haystack". Use this strategy when your audience values are not URIs (e.g., you use client IDs for
    52  // audience and they are UUIDs or random strings).
    53  func ExactAudienceMatchingStrategy(haystack []string, needle []string) error {
    54  	if len(needle) == 0 {
    55  		return nil
    56  	}
    57  
    58  	for _, n := range needle {
    59  		var found bool
    60  		for _, h := range haystack {
    61  			if n == h {
    62  				found = true
    63  			}
    64  		}
    65  
    66  		if !found {
    67  			return errorsx.WithStack(ErrInvalidRequest.WithHintf(`Requested audience "%s" has not been whitelisted by the OAuth 2.0 Client.`, n))
    68  		}
    69  	}
    70  
    71  	return nil
    72  }
    73  
    74  // GetAudiences allows audiences to be provided as repeated "audience" form parameter,
    75  // or as a space-delimited "audience" form parameter if it is not repeated.
    76  // RFC 8693 in section 2.1 specifies that multiple audience values should be multiple
    77  // query parameters, while RFC 6749 says that that request parameter must not be included
    78  // more than once (and thus why we use space-delimited value). This function tries to satisfy both.
    79  // If "audience" form parameter is repeated, we do not split the value by space.
    80  func GetAudiences(form url.Values) []string {
    81  	audiences := form["audience"]
    82  	if len(audiences) > 1 {
    83  		return RemoveEmpty(audiences)
    84  	} else if len(audiences) == 1 {
    85  		return RemoveEmpty(strings.Split(audiences[0], " "))
    86  	} else {
    87  		return []string{}
    88  	}
    89  }
    90  
    91  func (f *Fosite) validateAuthorizeAudience(r *http.Request, request *AuthorizeRequest) error {
    92  	audience := GetAudiences(request.Form)
    93  
    94  	if err := f.AudienceMatchingStrategy(request.Client.GetAudience(), audience); err != nil {
    95  		return err
    96  	}
    97  
    98  	request.SetRequestedAudience(Arguments(audience))
    99  	return nil
   100  }
   101  

View as plain text