1
1
/*
2
- * Copyright 2002-2018 the original author or authors.
2
+ * Copyright 2002-2025 the original author or authors.
3
3
*
4
4
* Licensed under the Apache License, Version 2.0 (the "License");
5
5
* you may not use this file except in compliance with the License.
29
29
import org .springframework .security .oauth2 .core .OAuth2TokenValidator ;
30
30
import org .springframework .security .oauth2 .core .OAuth2TokenValidatorResult ;
31
31
import org .springframework .util .Assert ;
32
+ import org .springframework .util .ObjectUtils ;
32
33
33
34
/**
34
35
* An implementation of {@link OAuth2TokenValidator} for verifying claims in a Jwt-based
@@ -54,6 +55,10 @@ public final class JwtTimestampValidator implements OAuth2TokenValidator<Jwt> {
54
55
55
56
private final Duration clockSkew ;
56
57
58
+ private boolean allowEmptyExpiryClaim = true ;
59
+
60
+ private boolean allowEmptyNotBeforeClaim = true ;
61
+
57
62
private Clock clock = Clock .systemUTC ();
58
63
59
64
/**
@@ -68,30 +73,54 @@ public JwtTimestampValidator(Duration clockSkew) {
68
73
this .clockSkew = clockSkew ;
69
74
}
70
75
76
+ /**
77
+ * Whether to allow the {@code exp} header to be empty. The default value is
78
+ * {@code true}
79
+ *
80
+ * @since 7.0
81
+ */
82
+ public void setAllowEmptyExpiryClaim (boolean allowEmptyExpiryClaim ) {
83
+ this .allowEmptyExpiryClaim = allowEmptyExpiryClaim ;
84
+ }
85
+
86
+ /**
87
+ * Whether to allow the {@code nbf} header to be empty. The default value is
88
+ * {@code true}
89
+ *
90
+ * @since 7.0
91
+ */
92
+ public void setAllowEmptyNotBeforeClaim (boolean allowEmptyNotBeforeClaim ) {
93
+ this .allowEmptyNotBeforeClaim = allowEmptyNotBeforeClaim ;
94
+ }
95
+
71
96
@ Override
72
97
public OAuth2TokenValidatorResult validate (Jwt jwt ) {
73
98
Assert .notNull (jwt , "jwt cannot be null" );
74
99
Instant expiry = jwt .getExpiresAt ();
100
+ if (!this .allowEmptyExpiryClaim && ObjectUtils .isEmpty (expiry )) {
101
+ return createOAuth2Error ("exp is required" );
102
+ }
75
103
if (expiry != null ) {
76
104
if (Instant .now (this .clock ).minus (this .clockSkew ).isAfter (expiry )) {
77
- OAuth2Error oAuth2Error = createOAuth2Error (String .format ("Jwt expired at %s" , jwt .getExpiresAt ()));
78
- return OAuth2TokenValidatorResult .failure (oAuth2Error );
105
+ return createOAuth2Error (String .format ("Jwt expired at %s" , jwt .getExpiresAt ()));
79
106
}
80
107
}
81
108
Instant notBefore = jwt .getNotBefore ();
109
+ if (!this .allowEmptyNotBeforeClaim && ObjectUtils .isEmpty (notBefore )) {
110
+ return createOAuth2Error ("nbf is required" );
111
+ }
82
112
if (notBefore != null ) {
83
113
if (Instant .now (this .clock ).plus (this .clockSkew ).isBefore (notBefore )) {
84
- OAuth2Error oAuth2Error = createOAuth2Error (String .format ("Jwt used before %s" , jwt .getNotBefore ()));
85
- return OAuth2TokenValidatorResult .failure (oAuth2Error );
114
+ return createOAuth2Error (String .format ("Jwt used before %s" , jwt .getNotBefore ()));
86
115
}
87
116
}
88
117
return OAuth2TokenValidatorResult .success ();
89
118
}
90
119
91
- private OAuth2Error createOAuth2Error (String reason ) {
120
+ private OAuth2TokenValidatorResult createOAuth2Error (String reason ) {
92
121
this .logger .debug (reason );
93
- return new OAuth2Error (OAuth2ErrorCodes .INVALID_TOKEN , reason ,
94
- "https://tools.ietf.org/html/rfc6750#section-3.1" );
122
+ return OAuth2TokenValidatorResult . failure ( new OAuth2Error (OAuth2ErrorCodes .INVALID_TOKEN , reason ,
123
+ "https://tools.ietf.org/html/rfc6750#section-3.1" )) ;
95
124
}
96
125
97
126
/**
0 commit comments