001/*
002 * oauth2-oidc-sdk
003 *
004 * Copyright 2012-2021, 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.dpop.verifiers;
019
020
021import java.util.Date;
022import java.util.Map;
023import java.util.Timer;
024import java.util.TimerTask;
025import java.util.concurrent.ConcurrentHashMap;
026
027import net.jcip.annotations.ThreadSafe;
028
029import com.nimbusds.oauth2.sdk.id.JWTID;
030import com.nimbusds.oauth2.sdk.util.singleuse.AlreadyUsedException;
031import com.nimbusds.oauth2.sdk.util.singleuse.SingleUseChecker;
032
033
034/**
035 * DPoP proof JWT single use checker. Caches a hash of the checked DPoP JWT
036 * "jti" (JWT ID) claims for a given DPoP issuer. The checker should be
037 * {@link #shutdown() shut down} when no longer in use.
038 */
039@ThreadSafe
040@Deprecated
041public class DefaultDPoPSingleUseChecker implements SingleUseChecker<Map.Entry<DPoPIssuer, JWTID>> {
042        
043        private final Timer timer;
044        
045        private final ConcurrentHashMap<String,Long> cachedJTIs = new ConcurrentHashMap<>();
046        
047        
048        /**
049         * Creates a new DPoP proof JWT single use checker.
050         *
051         * @param lifetimeSeconds      The lifetime of cached DPoP proof "jti"
052         *                             (JWT ID) claims, in seconds.
053         * @param purgeIntervalSeconds The interval in seconds for purging the
054         *                             cached "jti" (JWT ID) claims of checked
055         *                             DPoP proofs.
056         */
057        public DefaultDPoPSingleUseChecker(final long lifetimeSeconds,
058                                           final long purgeIntervalSeconds) {
059                
060                timer = new Timer("dpop-single-use-jti-cache-purge-task", true);
061                
062                timer.schedule(
063                        new TimerTask() {
064                                @Override
065                                public void run() {
066                                        final long nowMS = new Date().getTime();
067                                        final long expHorizon = nowMS - lifetimeSeconds * 1000;
068                                        for (Map.Entry<String, Long> en: cachedJTIs.entrySet()) {
069                                                if (en.getValue() < expHorizon) {
070                                                        cachedJTIs.remove(en.getKey());
071                                                }
072                                        }
073                                }
074                        },
075                        purgeIntervalSeconds * 1000,
076                        purgeIntervalSeconds * 1000);
077        }
078        
079        
080        @Override
081        public void markAsUsed(final Map.Entry<DPoPIssuer, JWTID> object)
082                throws AlreadyUsedException {
083                
084                String key = object.getKey().getValue() + ":" + InMemoryDPoPSingleUseChecker.computeSHA256(object.getValue());
085                
086                long nowMS = new Date().getTime();
087                
088                if (cachedJTIs.putIfAbsent(key, nowMS) != null) {
089                        throw new AlreadyUsedException("Detected jti replay");
090                }
091        }
092        
093        
094        /**
095         * Returns the number of cached items.
096         *
097         * @return The cached items, zero if none.
098         */
099        public int getCacheSize() {
100                
101                return cachedJTIs.size();
102        }
103        
104        
105        /**
106         * Shuts down this checker and frees any associated resources.
107         */
108        public void shutdown() {
109                
110                timer.cancel();
111        }
112}