1
21
22 package rfc7523
23
24 import (
25 "context"
26 "time"
27
28 "github.com/ory/fosite/handler/oauth2"
29
30 "gopkg.in/square/go-jose.v2"
31 "gopkg.in/square/go-jose.v2/jwt"
32
33 "github.com/ory/fosite"
34 "github.com/ory/x/errorsx"
35 )
36
37 const grantTypeJWTBearer = "urn:ietf:params:oauth:grant-type:jwt-bearer"
38
39 type Handler struct {
40 Storage RFC7523KeyStorage
41 ScopeStrategy fosite.ScopeStrategy
42 AudienceMatchingStrategy fosite.AudienceMatchingStrategy
43
44
45 TokenURL string
46
47 SkipClientAuth bool
48
49 JWTIDOptional bool
50
51 JWTIssuedDateOptional bool
52
53
54 JWTMaxDuration time.Duration
55
56 *oauth2.HandleHelper
57 }
58
59
60
61 func (c *Handler) HandleTokenEndpointRequest(ctx context.Context, request fosite.AccessRequester) error {
62 if err := c.CheckRequest(request); err != nil {
63 return err
64 }
65
66 assertion := request.GetRequestForm().Get("assertion")
67 if assertion == "" {
68 return errorsx.WithStack(fosite.ErrInvalidRequest.WithHintf("The assertion request parameter must be set when using grant_type of '%s'.", grantTypeJWTBearer))
69 }
70
71 token, err := jwt.ParseSigned(assertion)
72 if err != nil {
73 return errorsx.WithStack(fosite.ErrInvalidGrant.
74 WithHint("Unable to parse JSON Web Token passed in \"assertion\" request parameter.").
75 WithWrap(err).WithDebug(err.Error()),
76 )
77 }
78
79
80 if err := c.validateTokenPreRequisites(token); err != nil {
81 return err
82 }
83
84 key, err := c.findPublicKeyForToken(ctx, token)
85 if err != nil {
86 return err
87 }
88
89 claims := jwt.Claims{}
90 if err := token.Claims(key, &claims); err != nil {
91 return errorsx.WithStack(fosite.ErrInvalidGrant.
92 WithHint("Unable to verify the integrity of the 'assertion' value.").
93 WithWrap(err).WithDebug(err.Error()),
94 )
95 }
96
97 if err := c.validateTokenClaims(ctx, claims, key); err != nil {
98 return err
99 }
100
101 scopes, err := c.Storage.GetPublicKeyScopes(ctx, claims.Issuer, claims.Subject, key.KeyID)
102 if err != nil {
103 return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()))
104 }
105
106 for _, scope := range request.GetRequestedScopes() {
107 if !c.ScopeStrategy(scopes, scope) {
108 return errorsx.WithStack(fosite.ErrInvalidScope.WithHintf("The public key registered for issuer \"%s\" and subject \"%s\" is not allowed to request scope \"%s\".", claims.Issuer, claims.Subject, scope))
109 }
110 }
111
112 if claims.ID != "" {
113 if err := c.Storage.MarkJWTUsedForTime(ctx, claims.ID, claims.Expiry.Time()); err != nil {
114 return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()))
115 }
116 }
117
118 for _, scope := range request.GetRequestedScopes() {
119 request.GrantScope(scope)
120 }
121
122 for _, audience := range claims.Audience {
123 request.GrantAudience(audience)
124 }
125
126 session, err := c.getSessionFromRequest(request)
127 if err != nil {
128 return err
129 }
130 session.SetExpiresAt(fosite.AccessToken, time.Now().UTC().Add(c.HandleHelper.AccessTokenLifespan).Round(time.Second))
131 session.SetSubject(claims.Subject)
132
133 return nil
134 }
135
136 func (c *Handler) PopulateTokenEndpointResponse(ctx context.Context, request fosite.AccessRequester, response fosite.AccessResponder) error {
137 if err := c.CheckRequest(request); err != nil {
138 return err
139 }
140
141 return c.IssueAccessToken(ctx, request, response)
142 }
143
144 func (c *Handler) CanSkipClientAuth(requester fosite.AccessRequester) bool {
145 return c.SkipClientAuth
146 }
147
148 func (c *Handler) CanHandleTokenEndpointRequest(requester fosite.AccessRequester) bool {
149
150
151 return requester.GetGrantTypes().ExactOne(grantTypeJWTBearer)
152 }
153
154 func (c *Handler) CheckRequest(request fosite.AccessRequester) error {
155 if !c.CanHandleTokenEndpointRequest(request) {
156 return errorsx.WithStack(fosite.ErrUnknownRequest)
157 }
158
159
160
161
162
163
164
165
166
167 if !c.CanSkipClientAuth(request) && !request.GetClient().GetGrantTypes().Has(grantTypeJWTBearer) {
168 return errorsx.WithStack(fosite.ErrUnauthorizedClient.WithHintf("The OAuth 2.0 Client is not allowed to use authorization grant \"%s\".", grantTypeJWTBearer))
169 }
170
171 return nil
172 }
173
174 func (c *Handler) validateTokenPreRequisites(token *jwt.JSONWebToken) error {
175 unverifiedClaims := jwt.Claims{}
176 if err := token.UnsafeClaimsWithoutVerification(&unverifiedClaims); err != nil {
177 return errorsx.WithStack(fosite.ErrInvalidGrant.
178 WithHint("Looks like there are no claims in JWT in \"assertion\" request parameter.").
179 WithWrap(err).WithDebug(err.Error()),
180 )
181 }
182 if unverifiedClaims.Issuer == "" {
183 return errorsx.WithStack(fosite.ErrInvalidGrant.
184 WithHint("The JWT in \"assertion\" request parameter MUST contain an \"iss\" (issuer) claim."),
185 )
186 }
187 if unverifiedClaims.Subject == "" {
188 return errorsx.WithStack(fosite.ErrInvalidGrant.
189 WithHint("The JWT in \"assertion\" request parameter MUST contain a \"sub\" (subject) claim."),
190 )
191 }
192
193 return nil
194 }
195
196 func (c *Handler) findPublicKeyForToken(ctx context.Context, token *jwt.JSONWebToken) (*jose.JSONWebKey, error) {
197 unverifiedClaims := jwt.Claims{}
198 if err := token.UnsafeClaimsWithoutVerification(&unverifiedClaims); err != nil {
199 return nil, errorsx.WithStack(fosite.ErrInvalidRequest.WithWrap(err).WithDebug(err.Error()))
200 }
201
202 var keyID string
203 for _, header := range token.Headers {
204 if header.KeyID != "" {
205 keyID = header.KeyID
206 break
207 }
208 }
209
210 keyNotFoundErr := fosite.ErrInvalidGrant.WithHintf(
211 "No public JWK was registered for issuer \"%s\" and subject \"%s\", and public key is required to check signature of JWT in \"assertion\" request parameter.",
212 unverifiedClaims.Issuer,
213 unverifiedClaims.Subject,
214 )
215 if keyID != "" {
216 key, err := c.Storage.GetPublicKey(ctx, unverifiedClaims.Issuer, unverifiedClaims.Subject, keyID)
217 if err != nil {
218 return nil, errorsx.WithStack(keyNotFoundErr.WithWrap(err).WithDebug(err.Error()))
219 }
220 return key, nil
221 }
222
223 keys, err := c.Storage.GetPublicKeys(ctx, unverifiedClaims.Issuer, unverifiedClaims.Subject)
224 if err != nil {
225 return nil, errorsx.WithStack(keyNotFoundErr.WithWrap(err).WithDebug(err.Error()))
226 }
227
228 claims := jwt.Claims{}
229 for _, key := range keys.Keys {
230 err := token.Claims(key, &claims)
231 if err == nil {
232 return &key, nil
233 }
234 }
235
236 return nil, errorsx.WithStack(keyNotFoundErr)
237 }
238
239 func (c *Handler) validateTokenClaims(ctx context.Context, claims jwt.Claims, key *jose.JSONWebKey) error {
240 if len(claims.Audience) == 0 {
241 return errorsx.WithStack(fosite.ErrInvalidGrant.
242 WithHint("The JWT in \"assertion\" request parameter MUST contain an \"aud\" (audience) claim."),
243 )
244 }
245
246 if !claims.Audience.Contains(c.TokenURL) {
247 return errorsx.WithStack(fosite.ErrInvalidGrant.
248 WithHintf(
249 "The JWT in \"assertion\" request parameter MUST contain an \"aud\" (audience) claim containing a value \"%s\" that identifies the authorization server as an intended audience.",
250 c.TokenURL,
251 ),
252 )
253 }
254
255 if claims.Expiry == nil {
256 return errorsx.WithStack(fosite.ErrInvalidGrant.
257 WithHint("The JWT in \"assertion\" request parameter MUST contain an \"exp\" (expiration time) claim."),
258 )
259 }
260
261 if claims.Expiry.Time().Before(time.Now()) {
262 return errorsx.WithStack(fosite.ErrInvalidGrant.
263 WithHint("The JWT in \"assertion\" request parameter expired."),
264 )
265 }
266
267 if claims.NotBefore != nil && !claims.NotBefore.Time().Before(time.Now()) {
268 return errorsx.WithStack(fosite.ErrInvalidGrant.
269 WithHintf(
270 "The JWT in \"assertion\" request parameter contains an \"nbf\" (not before) claim, that identifies the time '%s' before which the token MUST NOT be accepted.",
271 claims.NotBefore.Time().Format(time.RFC3339),
272 ),
273 )
274 }
275
276 if !c.JWTIssuedDateOptional && claims.IssuedAt == nil {
277 return errorsx.WithStack(fosite.ErrInvalidGrant.
278 WithHint("The JWT in \"assertion\" request parameter MUST contain an \"iat\" (issued at) claim."),
279 )
280 }
281
282 var issuedDate time.Time
283 if claims.IssuedAt != nil {
284 issuedDate = claims.IssuedAt.Time()
285 } else {
286 issuedDate = time.Now()
287 }
288 if claims.Expiry.Time().Sub(issuedDate) > c.JWTMaxDuration {
289 return errorsx.WithStack(fosite.ErrInvalidGrant.
290 WithHintf(
291 "The JWT in \"assertion\" request parameter contains an \"exp\" (expiration time) claim with value \"%s\" that is unreasonably far in the future, considering token issued at \"%s\".",
292 claims.Expiry.Time().Format(time.RFC3339),
293 issuedDate.Format(time.RFC3339),
294 ),
295 )
296 }
297
298 if !c.JWTIDOptional && claims.ID == "" {
299 return errorsx.WithStack(fosite.ErrInvalidGrant.
300 WithHint("The JWT in \"assertion\" request parameter MUST contain an \"jti\" (JWT ID) claim."),
301 )
302 }
303
304 if claims.ID != "" {
305 used, err := c.Storage.IsJWTUsed(ctx, claims.ID)
306 if err != nil {
307 return errorsx.WithStack(fosite.ErrServerError.WithWrap(err).WithDebug(err.Error()))
308 }
309 if used {
310 return errorsx.WithStack(fosite.ErrJTIKnown)
311 }
312 }
313
314 return nil
315 }
316
317 type extendedSession interface {
318 Session
319 fosite.Session
320 }
321
322 func (c *Handler) getSessionFromRequest(requester fosite.AccessRequester) (extendedSession, error) {
323 session := requester.GetSession()
324 if jwtSession, ok := session.(extendedSession); !ok {
325 return nil, errorsx.WithStack(
326 fosite.ErrServerError.WithHintf("Session must be of type *rfc7523.Session but got type: %T", session),
327 )
328 } else {
329 return jwtSession, nil
330 }
331 }
332
View as plain text