001/*
002 * nimbus-jose-jwt
003 *
004 * Copyright 2012-2016, Connect2id Ltd.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.jwt.proc;
019
020
021import java.util.Date;
022import java.util.List;
023
024import net.jcip.annotations.ThreadSafe;
025
026import com.nimbusds.jose.proc.SecurityContext;
027import com.nimbusds.jwt.JWTClaimsSet;
028import com.nimbusds.jwt.util.DateUtils;
029
030
031/**
032 * Default JWT claims verifier. This class is thread-safe.
033 *
034 * <p>Performs the following checks:
035 *
036 * <ol>
037 *     <li>If an expiration time (exp) claim is present, makes sure it is
038 *         ahead of the current time, else the JWT claims set is rejected.
039 *     <li>If a not-before-time (nbf) claim is present, makes sure it is
040 *         before the current time, else the JWT claims set is rejected.
041 * </ol>
042 *
043 * <p>This class may be extended to perform additional checks.
044 *
045 * @author Vladimir Dzhuvinov
046 * @version 2019-10-15
047 */
048@ThreadSafe
049public class DefaultJWTClaimsVerifier <C extends SecurityContext> implements JWTClaimsSetVerifier<C>, JWTClaimsVerifier, ClockSkewAware {
050
051
052        /**
053         * The default maximum acceptable clock skew, in seconds (60).
054         */
055        public static final int DEFAULT_MAX_CLOCK_SKEW_SECONDS = 60;
056
057
058        /**
059         * The maximum acceptable clock skew, in seconds.
060         */
061        private int maxClockSkew = DEFAULT_MAX_CLOCK_SKEW_SECONDS;
062        
063        
064        /**
065         * The issued-at time claim requirement.
066         */
067        private boolean iatRequired = false;
068        
069        
070        /**
071         * The expiration time claim requirement.
072         */
073        private boolean expRequired = false;
074        
075        
076        /**
077         * The not-before time claim requirement.
078         */
079        private boolean nbfRequired = false;
080        
081        
082        /**
083         * The accepted issuer, {@code null} if not specified.
084         */
085        private String acceptedIssuer;
086        
087        
088        /**
089         * The accepted audience, {@code null} if not specified.
090         */
091        private String acceptedAudience;
092
093
094        @Override
095        public int getMaxClockSkew() {
096                return maxClockSkew;
097        }
098
099
100        @Override
101        public void setMaxClockSkew(int maxClockSkewSeconds) {
102                maxClockSkew = maxClockSkewSeconds;
103        }
104        
105        
106        /**
107         * Gets the issued-at time ("iat") requirement.
108         *
109         * @return {@code true} if the issued-at time claim is required,
110         *         {@code false} if not.
111         *
112         * @since 8.1
113         */
114        public boolean requiresIssuedAtTime() {
115                return iatRequired;
116        }
117        
118        
119        /**
120         * Sets the issued-at time ("iat") requirement.
121         *
122         * @param iatRequired {@code true} if the issued-at time claim is
123         *                    required, {@code false} if not.
124         *
125         * @since 8.1
126         */
127        public void requiresIssuedAtTime(final boolean iatRequired) {
128                this.iatRequired = iatRequired;
129        }
130        
131        
132        /**
133         * Gets the expiration time ("exp") requirement.
134         *
135         * @return {@code true} if the expiration time claim is required,
136         *         {@code false} if not.
137         *
138         * @since 8.1
139         */
140        public boolean requiresExpirationTime() {
141                return expRequired;
142        }
143        
144        
145        /**
146         * Sets the expiration time ("exp") requirement.
147         *
148         * @param expRequired {@code true} if the expiration time claim is
149         *                    required, {@code false} if not.
150         *
151         * @since 8.1
152         */
153        public void requiresExpirationTime(final boolean expRequired) {
154                this.expRequired = expRequired;
155        }
156        
157        
158        /**
159         * Gets the not-before time ("nbf") requirement.
160         *
161         * @return {@code true} if the not-before time claim is required,
162         *         {@code false} if not.
163         *
164         * @since 8.1
165         */
166        public boolean requiresNotBeforeTime() {
167                return nbfRequired;
168        }
169        
170        
171        /**
172         * Sets the not-before time ("nbf") requirement.
173         *
174         * @param nbfRequired {@code true} if the not-before time claim is
175         *                    required, {@code false} if not.
176         *
177         * @since 8.1
178         */
179        public void requiresNotBeforeTime(final boolean nbfRequired) {
180                this.nbfRequired = nbfRequired;
181        }
182        
183        
184        /**
185         * Gets the accepted issuer ("iss").
186         *
187         * @return The accepted issuer, {@code null} if not specified.
188         *
189         * @since 8.1
190         */
191        public String getAcceptedIssuer() {
192                return acceptedIssuer;
193        }
194        
195        
196        /**
197         * Sets the accepted issuer ("iss").
198         *
199         * @param iss The accepted issuer, {@code null} if not specified.
200         *
201         * @since 8.1
202         */
203        public void setAcceptedIssuer(final String iss) {
204                acceptedIssuer = iss;
205        }
206        
207        
208        /**
209         * Gets the accepted audience ("aud").
210         *
211         * @return The accepted audience, {@code null} if not specified.
212         *
213         * @since 8.1
214         */
215        public String getAcceptedAudience() {
216                return acceptedAudience;
217        }
218        
219        
220        /**
221         * Sets the accepted audience ("aud").
222         *
223         * @param aud The accepted audience, {@code null} if not specified.
224         *
225         * @since 8.1
226         */
227        public void setAcceptedAudience(final String aud) {
228                acceptedAudience = aud;
229        }
230        
231        
232        @Override
233        public void verify(final JWTClaimsSet claimsSet)
234                throws BadJWTException {
235
236                verify(claimsSet, null);
237        }
238        
239        
240        @Override
241        public void verify(final JWTClaimsSet claimsSet, final C context)
242                throws BadJWTException {
243                
244                if (iatRequired && claimsSet.getIssueTime() == null) {
245                        throw new BadJWTException("JWT issued-at time missing");
246                }
247                
248                final Date now = new Date();
249                
250                final Date exp = claimsSet.getExpirationTime();
251                
252                if (expRequired && exp == null) {
253                        throw new BadJWTException("JWT expiration time missing");
254                }
255                
256                if (exp != null) {
257                        
258                        if (! DateUtils.isAfter(exp, now, maxClockSkew)) {
259                                throw new BadJWTException("Expired JWT");
260                        }
261                }
262                
263                final Date nbf = claimsSet.getNotBeforeTime();
264                
265                if (nbfRequired && nbf == null) {
266                        throw new BadJWTException("JWT not-before time missing");
267                }
268                
269                if (nbf != null) {
270                        
271                        if (! DateUtils.isBefore(nbf, now, maxClockSkew)) {
272                                throw new BadJWTException("JWT before use time");
273                        }
274                }
275                
276                if (acceptedIssuer != null) {
277                        String iss = claimsSet.getIssuer();
278                        if (iss == null) {
279                                throw new BadJWTException("JWT issuer missing");
280                        }
281                        if (! acceptedIssuer.equals(iss)) {
282                                throw new BadJWTException("JWT issuer not accepted: " + iss);
283                        }
284                }
285                
286                if (acceptedAudience != null) {
287                        List<String> audList = claimsSet.getAudience();
288                        if (audList == null || audList.isEmpty()) {
289                                throw new BadJWTException("JWT audience missing");
290                        }
291                        if (! audList.contains(acceptedAudience)) {
292                                throw new BadJWTException("JWT audience not accepted: " + audList);
293                        }
294                }
295        }
296}