001/* 002 * oauth2-oidc-sdk 003 * 004 * Copyright 2012-2016, Connect2id Ltd and contributors. 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.oauth2.sdk.assertions.jwt; 019 020 021import com.nimbusds.jose.proc.SecurityContext; 022import com.nimbusds.jwt.JWTClaimsSet; 023import com.nimbusds.jwt.proc.BadJWTException; 024import com.nimbusds.jwt.proc.DefaultJWTClaimsVerifier; 025import com.nimbusds.jwt.util.DateUtils; 026import com.nimbusds.oauth2.sdk.id.Audience; 027import com.nimbusds.oauth2.sdk.id.Identifier; 028import com.nimbusds.oauth2.sdk.util.CollectionUtils; 029import net.jcip.annotations.Immutable; 030 031import java.util.Arrays; 032import java.util.Date; 033import java.util.HashSet; 034import java.util.Set; 035 036 037/** 038 * JSON Web Token (JWT) bearer assertion details (claims set) verifier for 039 * OAuth 2.0 client authentication and authorisation grants. Intended for 040 * initial validation of JWT assertions: 041 * 042 * <ul> 043 * <li>Audience check 044 * <li>Expiration time check 045 * <li>Expiration time too far ahead check (optional) 046 * <li>Not-before time check (if set) 047 * <li>Subject and issuer presence check 048 * </ul> 049 * 050 * <p>Related specifications: 051 * 052 * <ul> 053 * <li>JSON Web Token (JWT) Profile for OAuth 2.0 Client Authentication and 054 * Authorization Grants (RFC 7523). 055 * </ul> 056 */ 057@Immutable 058public class JWTAssertionDetailsVerifier extends DefaultJWTClaimsVerifier { 059 060 061 /** 062 * The expected audience. 063 */ 064 private final Set<Audience> expectedAudience; 065 066 067 /** 068 * The maximum number of seconds the expiration time can be ahead of 069 * the current time. 070 */ 071 private final long expMaxAhead; 072 073 074 /** 075 * Creates a new JWT bearer assertion details (claims set) verifier. 076 * 077 * @param expectedAudience The expected audience (aud) claim values. 078 * Must not be empty or {@code null}. Should 079 * typically contain the token endpoint URI and 080 * for OpenID provider it may also include the 081 * issuer URI. 082 */ 083 public JWTAssertionDetailsVerifier(final Set<Audience> expectedAudience) { 084 085 this(expectedAudience, -1L); 086 } 087 088 089 /** 090 * Creates a new JWT bearer assertion details (claims set) verifier. 091 * 092 * @param expectedAudience The expected audience (aud) claim values. 093 * Must not be empty or {@code null}. Should 094 * typically contain the token endpoint URI and 095 * for OpenID provider it may also include the 096 * issuer URI. 097 * @param expMaxAhead The maximum number of seconds the expiration 098 * time (exp) claim can be ahead of the current 099 * time, if zero or negative this check is 100 * disabled. 101 */ 102 public JWTAssertionDetailsVerifier(final Set<Audience> expectedAudience, 103 final long expMaxAhead) { 104 105 super( 106 new HashSet<>(Identifier.toStringList(expectedAudience)), 107 null, 108 new HashSet<>(Arrays.asList("aud", "exp", "sub", "iss")), 109 null); 110 111 if (CollectionUtils.isEmpty(expectedAudience)) { 112 throw new IllegalArgumentException("The expected audience set must not be null or empty"); 113 } 114 115 this.expectedAudience = expectedAudience; 116 117 this.expMaxAhead = expMaxAhead; 118 } 119 120 121 /** 122 * Returns the expected audience values. 123 * 124 * @return The expected audience (aud) claim values. 125 */ 126 @Deprecated 127 public Set<Audience> getExpectedAudience() { 128 129 return expectedAudience; 130 } 131 132 133 /** 134 * Returns the maximum number of seconds the expiration time (exp) 135 * claim can be ahead of the current time. 136 * 137 * @return The maximum number of seconds, if zero or negative this 138 * check is disabled. 139 */ 140 public long getExpirationTimeMaxAhead() { 141 142 return expMaxAhead; 143 } 144 145 146 @Override 147 public void verify(JWTClaimsSet claimsSet, SecurityContext context) 148 throws BadJWTException { 149 150 super.verify(claimsSet, context); 151 152 if (expMaxAhead > 0) { 153 long now = DateUtils.toSecondsSinceEpoch(new Date()); 154 long exp = DateUtils.toSecondsSinceEpoch(claimsSet.getExpirationTime()); 155 if (now + expMaxAhead < exp) { 156 throw new BadJWTException("JWT expiration too far ahead"); 157 } 158 } 159 } 160}