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}