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.openid.connect.sdk.validators;
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.oauth2.sdk.id.ClientID;
026import com.nimbusds.oauth2.sdk.id.Issuer;
027import com.nimbusds.openid.connect.sdk.claims.LogoutTokenClaimsSet;
028import net.jcip.annotations.ThreadSafe;
029
030import java.util.Arrays;
031import java.util.Collections;
032import java.util.HashSet;
033import java.util.Map;
034
035
036/**
037 * Logout token claims verifier.
038 *
039 * <p>Related specifications:
040 *
041 * <ul>
042 *     <li>OpenID Connect Back-Channel Logout 1.0
043 * </ul>
044 */
045@ThreadSafe
046public class LogoutTokenClaimsVerifier extends DefaultJWTClaimsVerifier {
047        
048        
049        /**
050         * The expected logout token issuer.
051         */
052        private final Issuer expectedIssuer;
053
054
055        /**
056         * The requesting client.
057         */
058        private final ClientID expectedClientID;
059        
060        
061        /**
062         * Creates a new logout token claims verifier.
063         *
064         * @param issuer   The expected ID token issuer. Must not be
065         *                 {@code null}.
066         * @param clientID The client ID. Must not be {@code null}.
067         */
068        public LogoutTokenClaimsVerifier(final Issuer issuer,
069                                         final ClientID clientID) {
070
071                super(
072                        Collections.singleton(clientID.getValue()),
073                        new JWTClaimsSet.Builder()
074                                .issuer(issuer.getValue())
075                                .build(),
076                        new HashSet<>(Arrays.asList("iat", "exp", "jti", "events")),
077                        Collections.singleton("nonce")
078                );
079                this.expectedIssuer = issuer;
080                this.expectedClientID = clientID;
081        }
082        
083        
084        /**
085         * Returns the expected ID token issuer.
086         *
087         * @return The ID token issuer.
088         */
089        public Issuer getExpectedIssuer() {
090                
091                return expectedIssuer;
092        }
093        
094        
095        /**
096         * Returns the client ID for verifying the ID token audience.
097         *
098         * @return The client ID.
099         */
100        public ClientID getClientID() {
101                
102                return expectedClientID;
103        }
104        
105        
106        @Override
107        public void verify(final JWTClaimsSet claimsSet, final SecurityContext ctx)
108                throws BadJWTException {
109
110                super.verify(claimsSet, ctx);
111
112                // See http://openid.net/specs/openid-connect-backchannel-1_0-ID1.html#Validation
113                
114                // Check event type
115                try {
116                        Map<String, Object> events = claimsSet.getJSONObjectClaim("events");
117                        
118                        if (events == null) {
119                                throw new BadJWTException("Missing / invalid JWT events (events) claim");
120                        }
121                        
122                        if (com.nimbusds.jose.util.JSONObjectUtils.getJSONObject(events, LogoutTokenClaimsSet.EVENT_TYPE) == null) {
123                                throw new BadJWTException("Missing event type, required " + LogoutTokenClaimsSet.EVENT_TYPE);
124                        }
125                        
126                } catch (java.text.ParseException e) {
127                        throw new BadJWTException("Invalid JWT events (events) claim", e);
128                }
129                
130                // Either sub or sid must be present
131                try {
132                        if (claimsSet.getSubject() == null && claimsSet.getStringClaim("sid") == null) {
133                                throw new BadJWTException("Missing subject (sub) and / or session ID (sid) claim(s)");
134                        }
135                        
136                } catch (java.text.ParseException e) {
137                        throw new BadJWTException("Invalid session ID (sid) claim");
138                }
139        }
140}