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}