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 aud The permitted audience (aud) claim. Must not be empty or 078 * {@code null}. Should be the identity of the recipient, 079 * such as the issuer URI for an OpenID provider. 080 */ 081 public JWTAssertionDetailsVerifier(final Set<Audience> aud) { 082 083 this(aud, -1L); 084 } 085 086 087 /** 088 * Creates a new JWT bearer assertion details (claims set) verifier. 089 * 090 * @param aud The permitted audience (aud) claim. Must not be 091 * empty or {@code null}. Should be the identity of 092 * the recipient, such as the issuer URI for an 093 * OpenID provider. 094 * @param expMaxAhead The maximum number of seconds the expiration time 095 * (exp) claim can be ahead of the current time, if 096 * zero or negative this check is disabled. 097 */ 098 public JWTAssertionDetailsVerifier(final Set<Audience> aud, 099 final long expMaxAhead) { 100 101 super( 102 new HashSet<>(Identifier.toStringList(aud)), 103 null, 104 new HashSet<>(Arrays.asList("aud", "exp", "sub", "iss")), 105 null); 106 107 if (CollectionUtils.isEmpty(aud)) { 108 throw new IllegalArgumentException("The expected audience set must not be null or empty"); 109 } 110 111 this.expectedAudience = aud; 112 113 this.expMaxAhead = expMaxAhead; 114 } 115 116 117 /** 118 * Returns the expected audience values. 119 * 120 * @return The expected audience (aud) claim values. 121 */ 122 @Deprecated 123 public Set<Audience> getExpectedAudience() { 124 125 return expectedAudience; 126 } 127 128 129 /** 130 * Returns the maximum number of seconds the expiration time (exp) 131 * claim can be ahead of the current time. 132 * 133 * @return The maximum number of seconds, if zero or negative this 134 * check is disabled. 135 */ 136 public long getExpirationTimeMaxAhead() { 137 138 return expMaxAhead; 139 } 140 141 142 @Override 143 public void verify(final JWTClaimsSet claimsSet, final SecurityContext context) 144 throws BadJWTException { 145 146 super.verify(claimsSet, context); 147 148 if (expMaxAhead > 0) { 149 long now = DateUtils.toSecondsSinceEpoch(new Date()); 150 long exp = DateUtils.toSecondsSinceEpoch(claimsSet.getExpirationTime()); 151 if (now + expMaxAhead < exp) { 152 throw new BadJWTException("JWT expiration too far ahead"); 153 } 154 } 155 } 156}