@@ -3,7 +3,6 @@ package util
5 "encoding/base64"
5 "encoding/base64"
@@ -12,17 +11,17 @@ import (
13 "github.com/bitly/go-simplejson"
12 "github.com/bitly/go-simplejson"
14 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests"
13 "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/requests"
15− "github.com/spf13/cast"
14+ "github.com/oauth2-proxy/oauth2-proxy/v7/pkg/util"
18// ClaimExtractor is used to extract claim values from an ID Token, or, if not
17// ClaimExtractor is used to extract claim values from an ID Token, or, if not
19// present, from the profile URL.
18// present, from the profile URL.
20type ClaimExtractor interface {
19type ClaimExtractor interface {
21 // GetClaim fetches a named claim and returns the value.
20 // GetClaim fetches a named claim and returns the value.
22− GetClaim(claim string) (interface{}, bool, error)
21+ GetClaim(claim string) (any, bool, error)
24 // GetClaimInto fetches a named claim and puts the value into the destination.
23 // GetClaimInto fetches a named claim and puts the value into the destination.
25− GetClaimInto(claim string, dst interface{}) (bool, error)
24+ GetClaimInto(claim string, dst any) (bool, error)
28// NewClaimExtractor constructs a new ClaimExtractor from the raw ID Token.
27// NewClaimExtractor constructs a new ClaimExtractor from the raw ID Token.
@@ -31,12 +30,12 @@ type ClaimExtractor interface {
31func NewClaimExtractor(ctx context.Context, idToken string, profileURL *url.URL, profileRequestHeaders http.Header) (ClaimExtractor, error) {
30func NewClaimExtractor(ctx context.Context, idToken string, profileURL *url.URL, profileRequestHeaders http.Header) (ClaimExtractor, error) {
32 payload, err := parseJWT(idToken)
31 payload, err := parseJWT(idToken)
33 if err != nil {
32 if err != nil {
34− return nil, fmt.Errorf("failed to parse ID Token: %v", err)
33+ return nil, fmt.Errorf("failed to parse ID Token: %w", err)
37 tokenClaims, err := simplejson.NewJson(payload)
36 tokenClaims, err := simplejson.NewJson(payload)
38 if err != nil {
37 if err != nil {
39− return nil, fmt.Errorf("failed to parse ID Token payload: %v", err)
38+ return nil, fmt.Errorf("failed to parse ID Token payload: %w", err)
42 return &claimExtractor{
41 return &claimExtractor{
@@ -59,7 +58,7 @@ type claimExtractor struct {
59// GetClaim will return the value claim if it exists.
58// GetClaim will return the value claim if it exists.
60// It will only return an error if the profile URL needs to be fetched due to
59// It will only return an error if the profile URL needs to be fetched due to
61// the claim not being present in the ID Token.
60// the claim not being present in the ID Token.
62−func (c *claimExtractor) GetClaim(claim string) (interface{}, bool, error) {
61+func (c *claimExtractor) GetClaim(claim string) (any, bool, error) {
63 if claim == "" {
62 if claim == "" {
64 return nil, false, nil
63 return nil, false, nil
@@ -124,7 +123,7 @@ func (c *claimExtractor) loadProfileClaims() (*simplejson.Json, error) {
124// GetClaimInto loads a claim and places it into the destination interface.
123// GetClaimInto loads a claim and places it into the destination interface.
125// This will attempt to coerce the claim into the specified type.
124// This will attempt to coerce the claim into the specified type.
126// If it cannot be coerced, an error may be returned.
125// If it cannot be coerced, an error may be returned.
127−func (c *claimExtractor) GetClaimInto(claim string, dst interface{}) (bool, error) {
126+func (c *claimExtractor) GetClaimInto(claim string, dst any) (bool, error) {
128 value, exists, err := c.GetClaim(claim)
127 value, exists, err := c.GetClaim(claim)
129 if err != nil {
128 if err != nil {
130 return false, fmt.Errorf("could not get claim %q: %v", claim, err)
129 return false, fmt.Errorf("could not get claim %q: %v", claim, err)
@@ -132,8 +131,8 @@ func (c *claimExtractor) GetClaimInto(claim string, dst interface{}) (bool, erro
132 if !exists {
131 if !exists {
133 return false, nil
132 return false, nil
135− if err := coerceClaim(value, dst); err != nil {
134+ if err := util.CoerceClaim(value, dst); err != nil {
136− return false, fmt.Errorf("could no coerce claim: %v", err)
135+ return false, fmt.Errorf("could not coerce claim: %v", err)
139 return true, nil
138 return true, nil
@@ -156,73 +155,10 @@ func parseJWT(p string) ([]byte, error) {
156// getClaimFrom gets a claim from a Json object.
155// getClaimFrom gets a claim from a Json object.
157// It can accept either a single claim name or a json path. The claim is always evaluated first as a single claim name.
156// It can accept either a single claim name or a json path. The claim is always evaluated first as a single claim name.
158// Paths with indexes are not supported.
157// Paths with indexes are not supported.
159−func getClaimFrom(claim string, src *simplejson.Json) interface{} {
158+func getClaimFrom(claim string, src *simplejson.Json) any {
160 if value, ok := src.CheckGet(claim); ok {
159 if value, ok := src.CheckGet(claim); ok {
161 return value.Interface()
160 return value.Interface()
163 claimParts := strings.Split(claim, ".")
162 claimParts := strings.Split(claim, ".")
164 return src.GetPath(claimParts...).Interface()
163 return src.GetPath(claimParts...).Interface()
167−// coerceClaim tries to convert the value into the destination interface type.
168−// If it can convert the value, it will then store the value in the destination
170−func coerceClaim(value, dst interface{}) error {
171− switch d := dst.(type) {
173− str, err := toString(value)
175− return fmt.Errorf("could not convert value to string: %v", err)
179− strSlice, err := toStringSlice(value)
181− return fmt.Errorf("could not convert value to string slice: %v", err)
185− *d = cast.ToBool(value)
187− return fmt.Errorf("unknown type for destination: %T", dst)
192−// toStringSlice converts an interface (either a slice or single value) into
193−// a slice of strings.
194−func toStringSlice(value interface{}) ([]string, error) {
195− var sliceValues []interface{}
196− switch v := value.(type) {
200− sliceValues = []interface{}{v}
202− sliceValues = cast.ToSlice(value)
206− for _, v := range sliceValues {
207− str, err := toString(v)
209− return nil, fmt.Errorf("could not convert slice entry to string %v: %v", v, err)
211− out = append(out, str)
216−// toString coerces a value into a string.
217−// If it is non-string, marshal it into JSON.
218−func toString(value interface{}) (string, error) {
219− if str, err := cast.ToStringE(value); err == nil {
223− jsonStr, err := json.Marshal(value)
227− return string(jsonStr), nil