1
21
22 package openid
23
24 import (
25 "context"
26 "fmt"
27 "testing"
28 "time"
29
30 "github.com/stretchr/testify/assert"
31
32 "github.com/ory/fosite"
33 "github.com/ory/fosite/token/jwt"
34 )
35
36 func TestJWTStrategy_GenerateIDToken(t *testing.T) {
37 var j = &DefaultStrategy{
38 JWTStrategy: &jwt.RS256JWTStrategy{
39 PrivateKey: key,
40 },
41 MinParameterEntropy: fosite.MinParameterEntropy,
42 }
43
44 var req *fosite.AccessRequest
45 for k, c := range []struct {
46 description string
47 setup func()
48 expectErr bool
49 }{
50 {
51 setup: func() {
52 req = fosite.NewAccessRequest(&DefaultSession{
53 Claims: &jwt.IDTokenClaims{
54 Subject: "peter",
55 },
56 Headers: &jwt.Headers{},
57 })
58 req.Form.Set("nonce", "some-secure-nonce-state")
59 },
60 expectErr: false,
61 },
62 {
63 setup: func() {
64 req = fosite.NewAccessRequest(&DefaultSession{
65 Claims: &jwt.IDTokenClaims{
66 Subject: "peter",
67 AuthTime: time.Now().UTC(),
68 RequestedAt: time.Now().UTC(),
69 },
70 Headers: &jwt.Headers{},
71 })
72 req.Form.Set("nonce", "some-secure-nonce-state")
73 req.Form.Set("max_age", "1234")
74 },
75 expectErr: false,
76 },
77 {
78 setup: func() {
79 req = fosite.NewAccessRequest(&DefaultSession{
80 Claims: &jwt.IDTokenClaims{
81 Subject: "peter",
82 ExpiresAt: time.Now().UTC().Add(-time.Hour),
83 },
84 Headers: &jwt.Headers{},
85 })
86 req.Form.Set("nonce", "some-secure-nonce-state")
87 },
88 expectErr: true,
89 },
90 {
91 setup: func() {
92 req = fosite.NewAccessRequest(&DefaultSession{
93 Claims: &jwt.IDTokenClaims{
94 Subject: "peter",
95 },
96 Headers: &jwt.Headers{},
97 })
98 req.Form.Set("nonce", "some-secure-nonce-state")
99 req.Form.Set("max_age", "1234")
100 },
101 expectErr: true,
102 },
103 {
104 setup: func() {
105 req = fosite.NewAccessRequest(&DefaultSession{
106 Claims: &jwt.IDTokenClaims{},
107 Headers: &jwt.Headers{},
108 })
109 req.Form.Set("nonce", "some-secure-nonce-state")
110 },
111 expectErr: true,
112 },
113 {
114 setup: func() {
115 req = fosite.NewAccessRequest(&DefaultSession{
116 Claims: &jwt.IDTokenClaims{
117 Subject: "peter",
118 },
119 Headers: &jwt.Headers{},
120 })
121 },
122 expectErr: false,
123 },
124 {
125 description: "should pass because max_age was requested and auth_time happened after initial request time",
126 setup: func() {
127 req = fosite.NewAccessRequest(&DefaultSession{
128 Claims: &jwt.IDTokenClaims{
129 Subject: "peter",
130 AuthTime: time.Now().UTC(),
131 RequestedAt: time.Now().UTC(),
132 },
133 Headers: &jwt.Headers{},
134 })
135 req.Form.Set("max_age", "60")
136 },
137 expectErr: false,
138 },
139 {
140 description: "should fail because max_age was requested and auth_time has expired",
141 setup: func() {
142 req = fosite.NewAccessRequest(&DefaultSession{
143 Claims: &jwt.IDTokenClaims{
144 Subject: "peter",
145 AuthTime: time.Now().Add(-time.Hour).UTC(),
146 },
147 Headers: &jwt.Headers{},
148 })
149 req.Form.Set("max_age", "60")
150 },
151 expectErr: true,
152 },
153 {
154 description: "should fail because prompt=none was requested and auth_time indicates fresh login",
155 setup: func() {
156 req = fosite.NewAccessRequest(&DefaultSession{
157 Claims: &jwt.IDTokenClaims{
158 Subject: "peter",
159 AuthTime: time.Now().UTC(),
160 RequestedAt: time.Now().Add(-time.Minute),
161 },
162 Headers: &jwt.Headers{},
163 })
164 req.Form.Set("prompt", "none")
165 },
166 expectErr: true,
167 },
168 {
169 description: "should pass because prompt=none was requested and auth_time indicates fresh login but grant type is refresh_token",
170 setup: func() {
171 req = fosite.NewAccessRequest(&DefaultSession{
172 Claims: &jwt.IDTokenClaims{
173 Subject: "peter",
174 AuthTime: time.Now().UTC(),
175 RequestedAt: time.Now().Add(-time.Minute),
176 },
177 Headers: &jwt.Headers{},
178 })
179 req.Form.Set("prompt", "none")
180 req.Form.Set("grant_type", "refresh_token")
181 },
182 expectErr: false,
183 },
184 {
185 description: "should pass because prompt=none was requested and auth_time indicates old login",
186 setup: func() {
187 req = fosite.NewAccessRequest(&DefaultSession{
188 Claims: &jwt.IDTokenClaims{
189 Subject: "peter",
190 AuthTime: time.Now().Add(-time.Hour).UTC(),
191 RequestedAt: time.Now().Add(-time.Minute),
192 },
193 Headers: &jwt.Headers{},
194 })
195 req.Form.Set("prompt", "none")
196 },
197 expectErr: false,
198 },
199 {
200 description: "should pass because prompt=login was requested and auth_time indicates fresh login",
201 setup: func() {
202 req = fosite.NewAccessRequest(&DefaultSession{
203 Claims: &jwt.IDTokenClaims{
204 Subject: "peter",
205 AuthTime: time.Now().UTC(),
206 RequestedAt: time.Now().Add(-time.Minute),
207 },
208 Headers: &jwt.Headers{},
209 })
210 req.Form.Set("prompt", "login")
211 },
212 expectErr: false,
213 },
214 {
215 description: "should fail because prompt=login was requested and auth_time indicates old login",
216 setup: func() {
217 req = fosite.NewAccessRequest(&DefaultSession{
218 Claims: &jwt.IDTokenClaims{
219 Subject: "peter",
220 AuthTime: time.Now().Add(-time.Hour).UTC(),
221 RequestedAt: time.Now().Add(-time.Minute),
222 },
223 Headers: &jwt.Headers{},
224 })
225 req.Form.Set("prompt", "login")
226 },
227 expectErr: true,
228 },
229 {
230 description: "should pass because id_token_hint subject matches subject from claims",
231 setup: func() {
232 req = fosite.NewAccessRequest(&DefaultSession{
233 Claims: &jwt.IDTokenClaims{
234 Subject: "peter",
235 AuthTime: time.Now().Add(-time.Hour).UTC(),
236 RequestedAt: time.Now().Add(-time.Minute),
237 },
238 Headers: &jwt.Headers{},
239 })
240 token, _ := j.GenerateIDToken(context.TODO(), fosite.NewAccessRequest(&DefaultSession{
241 Claims: &jwt.IDTokenClaims{
242 Subject: "peter",
243 },
244 Headers: &jwt.Headers{},
245 }))
246 req.Form.Set("id_token_hint", token)
247 },
248 expectErr: false,
249 },
250 {
251 description: "should pass even though token is expired",
252 setup: func() {
253 req = fosite.NewAccessRequest(&DefaultSession{
254 Claims: &jwt.IDTokenClaims{
255 Subject: "peter",
256 AuthTime: time.Now().Add(-time.Hour).UTC(),
257 RequestedAt: time.Now().Add(-time.Minute),
258 },
259 Headers: &jwt.Headers{},
260 })
261 token, _ := j.GenerateIDToken(context.TODO(), fosite.NewAccessRequest(&DefaultSession{
262 Claims: &jwt.IDTokenClaims{
263 Subject: "peter",
264 ExpiresAt: time.Now().Add(-time.Hour).UTC(),
265 },
266 Headers: &jwt.Headers{},
267 }))
268 req.Form.Set("id_token_hint", token)
269 },
270 expectErr: false,
271 },
272 {
273 description: "should fail because id_token_hint subject does not match subject from claims",
274 setup: func() {
275 req = fosite.NewAccessRequest(&DefaultSession{
276 Claims: &jwt.IDTokenClaims{
277 Subject: "peter",
278 AuthTime: time.Now().Add(-time.Hour).UTC(),
279 RequestedAt: time.Now().Add(-time.Minute),
280 },
281 Headers: &jwt.Headers{},
282 })
283 token, _ := j.GenerateIDToken(context.TODO(), fosite.NewAccessRequest(&DefaultSession{
284 Claims: &jwt.IDTokenClaims{Subject: "alice"}, Headers: &jwt.Headers{},
285 }))
286 req.Form.Set("id_token_hint", token)
287 },
288 expectErr: true,
289 },
290 } {
291 t.Run(fmt.Sprintf("case=%d/description=%s", k, c.description), func(t *testing.T) {
292 c.setup()
293 token, err := j.GenerateIDToken(context.TODO(), req)
294 assert.Equal(t, c.expectErr, err != nil, "%d: %+v", k, err)
295 if !c.expectErr {
296 assert.NotEmpty(t, token)
297 }
298 })
299 }
300 }
301
View as plain text