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}