Source file
src/github.com/ory/fosite/authorize_request_handler_oidc_request_test.go
1
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
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