...

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

Documentation: github.com/ory/fosite

     1  /*
     2   * Copyright © 2017-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
     3   *
     4   * Licensed under the Apache License, Version 2.0 (the "License");
     5   * you may not use this file except in compliance with the License.
     6   * You may obtain a copy of the License at
     7   *
     8   *     http://www.apache.org/licenses/LICENSE-2.0
     9   *
    10   * Unless required by applicable law or agreed to in writing, software
    11   * distributed under the License is distributed on an "AS IS" BASIS,
    12   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
    13   * See the License for the specific language governing permissions and
    14   * limitations under the License.
    15   *
    16   * @author		Aeneas Rekkas <aeneas+oss@aeneas.io>
    17   * @Copyright 	2017-2018 Aeneas Rekkas <aeneas+oss@aeneas.io>
    18   * @license 	Apache-2.0
    19   *
    20   */
    21  
    22  package fosite
    23  
    24  import (
    25  	"crypto/rand"
    26  	"crypto/rsa"
    27  	"encoding/json"
    28  	"fmt"
    29  	"net/http"
    30  	"net/http/httptest"
    31  	"net/url"
    32  	"testing"
    33  
    34  	"github.com/pkg/errors"
    35  
    36  	"github.com/ory/fosite/token/jwt"
    37  	"github.com/stretchr/testify/assert"
    38  	"github.com/stretchr/testify/require"
    39  	jose "gopkg.in/square/go-jose.v2"
    40  )
    41  
    42  func mustGenerateAssertion(t *testing.T, claims jwt.MapClaims, key *rsa.PrivateKey, kid string) string {
    43  	token := jwt.NewWithClaims(jose.RS256, claims)
    44  	if kid != "" {
    45  		token.Header["kid"] = kid
    46  	}
    47  	tokenString, err := token.SignedString(key)
    48  	require.NoError(t, err)
    49  	return tokenString
    50  }
    51  
    52  func mustGenerateHSAssertion(t *testing.T, claims jwt.MapClaims) string {
    53  	token := jwt.NewWithClaims(jose.HS256, claims)
    54  	tokenString, err := token.SignedString([]byte("aaaaaaaaaaaaaaabbbbbbbbbbbbbbbbbbbbbbbcccccccccccccccccccccddddddddddddddddddddddd"))
    55  	require.NoError(t, err)
    56  	return tokenString
    57  }
    58  
    59  func mustGenerateNoneAssertion(t *testing.T, claims jwt.MapClaims) string {
    60  	token := jwt.NewWithClaims(jwt.SigningMethodNone, claims)
    61  	tokenString, err := token.SignedString(jwt.UnsafeAllowNoneSignatureType)
    62  	require.NoError(t, err)
    63  	return tokenString
    64  }
    65  
    66  func TestAuthorizeRequestParametersFromOpenIDConnectRequest(t *testing.T) {
    67  	key, err := rsa.GenerateKey(rand.Reader, 1024)
    68  	if err != nil {
    69  		panic(err)
    70  	}
    71  	jwks := &jose.JSONWebKeySet{
    72  		Keys: []jose.JSONWebKey{
    73  			{
    74  				KeyID: "kid-foo",
    75  				Use:   "sig",
    76  				Key:   &key.PublicKey,
    77  			},
    78  		},
    79  	}
    80  
    81  	validRequestObject := mustGenerateAssertion(t, jwt.MapClaims{"scope": "foo", "foo": "bar", "baz": "baz", "response_type": "token", "response_mode": "post_form"}, key, "kid-foo")
    82  	validRequestObjectWithoutKid := mustGenerateAssertion(t, jwt.MapClaims{"scope": "foo", "foo": "bar", "baz": "baz"}, key, "")
    83  	validNoneRequestObject := mustGenerateNoneAssertion(t, jwt.MapClaims{"scope": "foo", "foo": "bar", "baz": "baz", "state": "some-state"})
    84  
    85  	var reqH http.HandlerFunc = func(rw http.ResponseWriter, r *http.Request) {
    86  		rw.Write([]byte(validRequestObject))
    87  	}
    88  	reqTS := httptest.NewServer(reqH)
    89  	defer reqTS.Close()
    90  
    91  	var hJWK http.HandlerFunc = func(rw http.ResponseWriter, r *http.Request) {
    92  		require.NoError(t, json.NewEncoder(rw).Encode(jwks))
    93  	}
    94  	reqJWK := httptest.NewServer(hJWK)
    95  	defer reqJWK.Close()
    96  
    97  	f := &Fosite{JWKSFetcherStrategy: NewDefaultJWKSFetcherStrategy()}
    98  	for k, tc := range []struct {
    99  		client Client
   100  		form   url.Values
   101  		d      string
   102  
   103  		expectErr       error
   104  		expectErrReason string
   105  		expectForm      url.Values
   106  	}{
   107  		{
   108  			d:          "should pass because no request context given and not openid",
   109  			form:       url.Values{},
   110  			expectErr:  nil,
   111  			expectForm: url.Values{},
   112  		},
   113  		{
   114  			d:          "should pass because no request context given",
   115  			form:       url.Values{"scope": {"openid"}},
   116  			expectErr:  nil,
   117  			expectForm: url.Values{"scope": {"openid"}},
   118  		},
   119  		{
   120  			d:          "should pass because request context given but not openid",
   121  			form:       url.Values{"request": {"foo"}},
   122  			expectErr:  nil,
   123  			expectForm: url.Values{"request": {"foo"}},
   124  		},
   125  		{
   126  			d:          "should fail because not an OpenIDConnect compliant client",
   127  			form:       url.Values{"scope": {"openid"}, "request": {"foo"}},
   128  			expectErr:  ErrRequestNotSupported,
   129  			expectForm: url.Values{"scope": {"openid"}},
   130  		},
   131  		{
   132  			d:          "should fail because not an OpenIDConnect compliant client",
   133  			form:       url.Values{"scope": {"openid"}, "request_uri": {"foo"}},
   134  			expectErr:  ErrRequestURINotSupported,
   135  			expectForm: url.Values{"scope": {"openid"}},
   136  		},
   137  		{
   138  			d:          "should fail because token invalid an no keys set",
   139  			form:       url.Values{"scope": {"openid"}, "request_uri": {"foo"}},
   140  			client:     &DefaultOpenIDConnectClient{RequestObjectSigningAlgorithm: "RS256"},
   141  			expectErr:  ErrInvalidRequest,
   142  			expectForm: url.Values{"scope": {"openid"}},
   143  		},
   144  		{
   145  			d:          "should fail because token invalid",
   146  			form:       url.Values{"scope": {"openid"}, "request": {"foo"}},
   147  			client:     &DefaultOpenIDConnectClient{JSONWebKeys: jwks, RequestObjectSigningAlgorithm: "RS256"},
   148  			expectErr:  ErrInvalidRequestObject,
   149  			expectForm: url.Values{"scope": {"openid"}},
   150  		},
   151  		{
   152  			d:               "should fail because kid does not exist",
   153  			form:            url.Values{"scope": {"openid"}, "request": {mustGenerateAssertion(t, jwt.MapClaims{}, key, "does-not-exists")}},
   154  			client:          &DefaultOpenIDConnectClient{JSONWebKeys: jwks, RequestObjectSigningAlgorithm: "RS256"},
   155  			expectErr:       ErrInvalidRequestObject,
   156  			expectErrReason: "Unable to retrieve RSA signing key from OAuth 2.0 Client. The JSON Web Token uses signing key with kid 'does-not-exists', which could not be found.",
   157  			expectForm:      url.Values{"scope": {"openid"}},
   158  		},
   159  		{
   160  			d:               "should fail because not RS256 token",
   161  			form:            url.Values{"scope": {"openid"}, "request": {mustGenerateHSAssertion(t, jwt.MapClaims{})}},
   162  			client:          &DefaultOpenIDConnectClient{JSONWebKeys: jwks, RequestObjectSigningAlgorithm: "RS256"},
   163  			expectErr:       ErrInvalidRequestObject,
   164  			expectErrReason: "The request object uses signing algorithm 'HS256', but the requested OAuth 2.0 Client enforces signing algorithm 'RS256'.",
   165  			expectForm:      url.Values{"scope": {"openid"}},
   166  		},
   167  		{
   168  			d:      "should pass and set request parameters properly",
   169  			form:   url.Values{"scope": {"openid"}, "response_type": {"code"}, "response_mode": {"none"}, "request": {validRequestObject}},
   170  			client: &DefaultOpenIDConnectClient{JSONWebKeys: jwks, RequestObjectSigningAlgorithm: "RS256"},
   171  			// The values from form are overwritten by the request object.
   172  			expectForm: url.Values{"response_type": {"token"}, "response_mode": {"post_form"}, "scope": {"foo openid"}, "request": {validRequestObject}, "foo": {"bar"}, "baz": {"baz"}},
   173  		},
   174  		{
   175  			d:          "should pass even if kid is unset",
   176  			form:       url.Values{"scope": {"openid"}, "request": {validRequestObjectWithoutKid}},
   177  			client:     &DefaultOpenIDConnectClient{JSONWebKeys: jwks, RequestObjectSigningAlgorithm: "RS256"},
   178  			expectForm: url.Values{"scope": {"foo openid"}, "request": {validRequestObjectWithoutKid}, "foo": {"bar"}, "baz": {"baz"}},
   179  		},
   180  		{
   181  			d:          "should fail because request uri is not whitelisted",
   182  			form:       url.Values{"scope": {"openid"}, "request_uri": {reqTS.URL}},
   183  			client:     &DefaultOpenIDConnectClient{JSONWebKeysURI: reqJWK.URL, RequestObjectSigningAlgorithm: "RS256"},
   184  			expectForm: url.Values{"scope": {"foo openid"}, "request_uri": {reqTS.URL}, "foo": {"bar"}, "baz": {"baz"}},
   185  			expectErr:  ErrInvalidRequestURI,
   186  		},
   187  		{
   188  			d:          "should pass and set request_uri parameters properly and also fetch jwk from remote",
   189  			form:       url.Values{"scope": {"openid"}, "request_uri": {reqTS.URL}},
   190  			client:     &DefaultOpenIDConnectClient{JSONWebKeysURI: reqJWK.URL, RequestObjectSigningAlgorithm: "RS256", RequestURIs: []string{reqTS.URL}},
   191  			expectForm: url.Values{"response_type": {"token"}, "response_mode": {"post_form"}, "scope": {"foo openid"}, "request_uri": {reqTS.URL}, "foo": {"bar"}, "baz": {"baz"}},
   192  		},
   193  		{
   194  			d:          "should pass when request object uses algorithm none",
   195  			form:       url.Values{"scope": {"openid"}, "request": {validNoneRequestObject}},
   196  			client:     &DefaultOpenIDConnectClient{JSONWebKeysURI: reqJWK.URL, RequestObjectSigningAlgorithm: "none"},
   197  			expectForm: url.Values{"state": {"some-state"}, "scope": {"foo openid"}, "request": {validNoneRequestObject}, "foo": {"bar"}, "baz": {"baz"}},
   198  		},
   199  		{
   200  			d:          "should pass when request object uses algorithm none and the client did not explicitly allow any algorithm",
   201  			form:       url.Values{"scope": {"openid"}, "request": {validNoneRequestObject}},
   202  			client:     &DefaultOpenIDConnectClient{JSONWebKeysURI: reqJWK.URL},
   203  			expectForm: url.Values{"state": {"some-state"}, "scope": {"foo openid"}, "request": {validNoneRequestObject}, "foo": {"bar"}, "baz": {"baz"}},
   204  		},
   205  	} {
   206  		t.Run(fmt.Sprintf("case=%d/description=%s", k, tc.d), func(t *testing.T) {
   207  			req := &AuthorizeRequest{
   208  				Request: Request{
   209  					Client: tc.client,
   210  					Form:   tc.form,
   211  				},
   212  			}
   213  
   214  			err := f.authorizeRequestParametersFromOpenIDConnectRequest(req)
   215  			if tc.expectErr != nil {
   216  				require.EqualError(t, err, tc.expectErr.Error(), "%+v", err)
   217  				if tc.expectErrReason != "" {
   218  					real := new(RFC6749Error)
   219  					require.True(t, errors.As(err, &real))
   220  					assert.EqualValues(t, tc.expectErrReason, real.Reason())
   221  				}
   222  			} else {
   223  				if err != nil {
   224  					real := new(RFC6749Error)
   225  					errors.As(err, &real)
   226  					require.NoErrorf(t, err, "Hint: %v\nDebug:%v", real.HintField, real.DebugField)
   227  				}
   228  				require.NoErrorf(t, err, "%+v", err)
   229  				require.Equal(t, len(tc.expectForm), len(req.Form))
   230  				for k, v := range tc.expectForm {
   231  					assert.EqualValues(t, v, req.Form[k])
   232  				}
   233  			}
   234  		})
   235  	}
   236  }
   237  

View as plain text