1
21
22 package openid
23
24 import (
25 "context"
26 "fmt"
27 "net/url"
28 "testing"
29 "time"
30
31 "github.com/stretchr/testify/assert"
32 "github.com/stretchr/testify/require"
33
34 "github.com/ory/fosite"
35 "github.com/ory/fosite/token/jwt"
36 )
37
38 func TestValidatePrompt(t *testing.T) {
39 var j = &DefaultStrategy{
40 JWTStrategy: &jwt.RS256JWTStrategy{
41 PrivateKey: key,
42 },
43 MinParameterEntropy: fosite.MinParameterEntropy,
44 }
45
46 v := NewOpenIDConnectRequestValidator(nil, j)
47
48 var genIDToken = func(c jwt.IDTokenClaims) string {
49 s, _, err := j.Generate(context.TODO(), c.ToMapClaims(), jwt.NewHeaders())
50 require.NoError(t, err)
51 return s
52 }
53
54 for k, tc := range []struct {
55 d string
56 prompt string
57 redirectURL string
58 isPublic bool
59 expectErr bool
60 idTokenHint string
61 s *DefaultSession
62 }{
63 {
64 d: "should fail because prompt=none should not work together with public clients and http non-localhost",
65 prompt: "none",
66 isPublic: true,
67 expectErr: true,
68 redirectURL: "http://foo-bar/",
69 s: &DefaultSession{
70 Subject: "foo",
71 Claims: &jwt.IDTokenClaims{
72 Subject: "foo",
73 RequestedAt: time.Now().UTC(),
74 AuthTime: time.Now().UTC().Add(-time.Minute),
75 },
76 },
77 },
78 {
79 d: "should pass because prompt=none works for public clients and http localhost",
80 prompt: "none",
81 isPublic: true,
82 expectErr: false,
83 redirectURL: "http://localhost/",
84 s: &DefaultSession{
85 Subject: "foo",
86 Claims: &jwt.IDTokenClaims{
87 Subject: "foo",
88 RequestedAt: time.Now().UTC(),
89 AuthTime: time.Now().UTC().Add(-time.Minute),
90 },
91 },
92 },
93 {
94 d: "should pass",
95 prompt: "none",
96 isPublic: true,
97 expectErr: false,
98 redirectURL: "https://foo-bar/",
99 s: &DefaultSession{
100 Subject: "foo",
101 Claims: &jwt.IDTokenClaims{
102 Subject: "foo",
103 RequestedAt: time.Now().UTC(),
104 AuthTime: time.Now().UTC().Add(-time.Minute),
105 },
106 },
107 },
108 {
109 d: "should fail because prompt=none requires an auth time being set",
110 prompt: "none",
111 isPublic: false,
112 expectErr: true,
113 s: &DefaultSession{
114 Subject: "foo",
115 Claims: &jwt.IDTokenClaims{
116 Subject: "foo",
117 RequestedAt: time.Now().UTC(),
118 },
119 },
120 },
121 {
122 d: "should fail because prompt=none and auth time is recent (after requested at)",
123 prompt: "none",
124 isPublic: false,
125 expectErr: true,
126 s: &DefaultSession{
127 Subject: "foo",
128 Claims: &jwt.IDTokenClaims{
129 Subject: "foo",
130 RequestedAt: time.Now().UTC().Add(-time.Minute),
131 AuthTime: time.Now().UTC(),
132 },
133 },
134 },
135 {
136 d: "should pass because prompt=none and auth time is in the past (before requested at)",
137 prompt: "none",
138 isPublic: false,
139 expectErr: false,
140 s: &DefaultSession{
141 Subject: "foo",
142 Claims: &jwt.IDTokenClaims{
143 Subject: "foo",
144 RequestedAt: time.Now().UTC(),
145 AuthTime: time.Now().UTC().Add(-time.Minute),
146 },
147 },
148 },
149 {
150 d: "should fail because prompt=none can not be used together with other prompts",
151 prompt: "none login",
152 isPublic: false,
153 expectErr: true,
154 s: &DefaultSession{
155 Subject: "foo",
156 Claims: &jwt.IDTokenClaims{
157 Subject: "foo",
158 RequestedAt: time.Now().UTC(),
159 AuthTime: time.Now().UTC(),
160 },
161 },
162 },
163 {
164 d: "should fail because prompt=foo is an unknown value",
165 prompt: "foo",
166 isPublic: false,
167 expectErr: true,
168 s: &DefaultSession{
169 Subject: "foo",
170 Claims: &jwt.IDTokenClaims{
171 Subject: "foo",
172 RequestedAt: time.Now().UTC(),
173 AuthTime: time.Now().UTC(),
174 },
175 },
176 },
177 {
178 d: "should pass because requesting consent and login works with public clients",
179 prompt: "login consent",
180 isPublic: true,
181 expectErr: false,
182 s: &DefaultSession{
183 Subject: "foo",
184 Claims: &jwt.IDTokenClaims{
185 Subject: "foo",
186 RequestedAt: time.Now().UTC().Add(-time.Second * 5),
187 AuthTime: time.Now().UTC().Add(-time.Second),
188 },
189 },
190 },
191 {
192 d: "should pass because requesting consent and login works with confidential clients",
193 prompt: "login consent",
194 isPublic: false,
195 expectErr: false,
196 s: &DefaultSession{
197 Subject: "foo",
198 Claims: &jwt.IDTokenClaims{
199 Subject: "foo",
200 RequestedAt: time.Now().UTC().Add(-time.Second * 5),
201 AuthTime: time.Now().UTC().Add(-time.Second),
202 },
203 },
204 },
205 {
206 d: "should fail subject from ID token does not match subject from session",
207 prompt: "login",
208 isPublic: false,
209 expectErr: true,
210 s: &DefaultSession{
211 Subject: "foo",
212 Claims: &jwt.IDTokenClaims{
213 Subject: "foo",
214 RequestedAt: time.Now().UTC(),
215 AuthTime: time.Now().UTC().Add(-time.Second),
216 },
217 },
218 idTokenHint: genIDToken(jwt.IDTokenClaims{
219 Subject: "bar",
220 RequestedAt: time.Now(),
221 ExpiresAt: time.Now().Add(time.Hour),
222 }),
223 },
224 {
225 d: "should pass subject from ID token matches subject from session",
226 prompt: "",
227 isPublic: false,
228 expectErr: false,
229 s: &DefaultSession{
230 Subject: "foo",
231 Claims: &jwt.IDTokenClaims{
232 Subject: "foo",
233 RequestedAt: time.Now().UTC(),
234 AuthTime: time.Now().UTC().Add(-time.Second),
235 },
236 },
237 idTokenHint: genIDToken(jwt.IDTokenClaims{
238 Subject: "foo",
239 RequestedAt: time.Now(),
240 ExpiresAt: time.Now().Add(time.Hour),
241 }),
242 },
243 {
244 d: "should pass subject from ID token matches subject from session even though id token is expired",
245 prompt: "",
246 isPublic: false,
247 expectErr: false,
248 s: &DefaultSession{
249 Subject: "foo",
250 Claims: &jwt.IDTokenClaims{
251 Subject: "foo",
252 RequestedAt: time.Now().UTC(),
253 AuthTime: time.Now().UTC().Add(-time.Second),
254 ExpiresAt: time.Now().UTC().Add(-time.Second),
255 },
256 },
257 idTokenHint: genIDToken(jwt.IDTokenClaims{
258 Subject: "foo",
259 RequestedAt: time.Now(),
260 ExpiresAt: time.Now().Add(time.Hour),
261 }),
262 },
263 } {
264 t.Run(fmt.Sprintf("case=%d/description=%s", k, tc.d), func(t *testing.T) {
265 t.Logf("%s", tc.idTokenHint)
266 err := v.ValidatePrompt(context.TODO(), &fosite.AuthorizeRequest{
267 Request: fosite.Request{
268 Form: url.Values{"prompt": {tc.prompt}, "id_token_hint": {tc.idTokenHint}},
269 Client: &fosite.DefaultClient{Public: tc.isPublic},
270 Session: tc.s,
271 },
272 RedirectURI: parse(tc.redirectURL),
273 })
274 if tc.expectErr {
275 assert.Error(t, err)
276 } else {
277 assert.NoError(t, err)
278 }
279 })
280 }
281 }
282
283 func parse(u string) *url.URL {
284 o, _ := url.Parse(u)
285 return o
286 }
287
View as plain text