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.op;
019
020
021import java.util.ArrayList;
022import java.util.LinkedList;
023import java.util.List;
024
025import com.nimbusds.oauth2.sdk.GeneralException;
026import com.nimbusds.oauth2.sdk.OAuth2Error;
027import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
028import com.nimbusds.openid.connect.sdk.ClaimsRequest;
029import com.nimbusds.openid.connect.sdk.claims.ACR;
030import com.nimbusds.openid.connect.sdk.claims.ClaimRequirement;
031import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
032import net.jcip.annotations.Immutable;
033
034
035/**
036 * Resolved authentication Context Class Reference (ACR) request.
037 */
038@Immutable 
039public final class ACRRequest {
040
041
042        /**
043         * The essential ACR values.
044         */
045        private final List<ACR> essentialACRs;
046
047
048        /**
049         * The voluntary ACR values.
050         */
051        private final List<ACR> voluntaryACRs;
052
053
054        /**
055         * Creates a new Authentication Context Class Reference (ACR) request.
056         *
057         * @param essentialACRs The requested essential ACR values, by order of
058         *                      preference, {@code null} if not specified.
059         * @param voluntaryACRs The requested voluntary ACR values, by order of
060         *                      preference, {@code null} if not specified.
061         */
062        public ACRRequest(final List<ACR> essentialACRs, final List<ACR> voluntaryACRs) {
063
064                this.essentialACRs = essentialACRs;
065                this.voluntaryACRs = voluntaryACRs;
066        }
067        
068
069        /**
070         * Gets the requested essential ACR values.
071         * 
072         * @return The essential ACR values, by order of preference, 
073         *         {@code null} if not specified.
074         */
075        public List<ACR> getEssentialACRs() {
076                
077                return essentialACRs;
078        }
079        
080        
081        /**
082         * Gets the requested voluntary ACR values.
083         * 
084         * @return The voluntary ACR values, by order of preference, 
085         *         {@code null} if not specified.
086         */
087        public List<ACR> getVoluntaryACRs() {
088                
089                return voluntaryACRs;
090        }
091        
092        
093        /**
094         * Checks if this ACR request has no essential or voluntary values
095         * specified.
096         * 
097         * @return {@code true} if this ACR request doesn't specify any 
098         *         essential or voluntary values, else {@code false}.
099         */
100        public boolean isEmpty() {
101
102                return !(essentialACRs != null && !essentialACRs.isEmpty()) &&
103                       !(voluntaryACRs != null && !voluntaryACRs.isEmpty());
104        }
105        
106        
107        /**
108         * Applies the registered default ACR values for the requesting client
109         * (as a voluntary ACR value, provided no ACR values were explicitly
110         * requested).
111         *
112         * @param clientInfo The registered client information. Must not be
113         *                   {@code null}.
114         *
115         * @return The ACR request, updated if registered default ACR values
116         *         were applied.
117         */
118        public ACRRequest applyDefaultACRs(final OIDCClientInformation clientInfo) {
119                
120                // Apply default ACR from client reg store as voluntary?
121                if (isEmpty()) {
122                        if (clientInfo.getOIDCMetadata().getDefaultACRs() != null) {
123                                List<ACR> voluntaryACRs = new LinkedList<>(clientInfo.getOIDCMetadata().getDefaultACRs());
124                                return new ACRRequest(null, voluntaryACRs);
125                        }
126                }
127                
128                return this;
129        }
130        
131        
132        /**
133         * Ensures all requested essential ACR values are supported by those
134         * supported by the OpenID provider.
135         *
136         * @param authRequest   The OpenID authentication request. Must not be
137         *                      {@code null}.
138         * @param supportedACRs The ACR values supported by the OpenID
139         *                      provider, {@code null} if not specified.
140         *
141         * @throws GeneralException If a requested essential ACR value is not
142         *                          supported by the OpenID provider.
143         */
144        public void ensureACRSupport(final AuthenticationRequest authRequest, final List<ACR> supportedACRs)
145                throws GeneralException {
146                
147                // Ensure any requested essential ACR is supported
148                if (getEssentialACRs() != null) {
149                        
150                        boolean foundSupportedEssentialACR = false;
151                        
152                        for (ACR acr: getEssentialACRs()) {
153                                
154                                if (supportedACRs != null && supportedACRs.contains(acr)) {
155                                        foundSupportedEssentialACR = true;
156                                        break;
157                                }
158                        }
159                        
160                        if (! foundSupportedEssentialACR) {
161                                String msg = "Requested essential ACR(s) not supported";
162                                throw new GeneralException(msg,
163                                        OAuth2Error.ACCESS_DENIED.appendDescription(": " + msg),
164                                        authRequest.getClientID(),
165                                        authRequest.getRedirectionURI(),
166                                        authRequest.impliedResponseMode(),
167                                        authRequest.getState());
168                        }
169                }
170        }
171        
172        
173        /**
174         * Ensures all requested essential ACR values are supported by the
175         * OpenID provider.
176         *
177         * @param authRequest The OpenID authentication request. Must not be
178         *                    {@code null}.
179         * @param opMetadata  The OpenID provider metadata. Must not be
180         *                    {@code null}.
181         *
182         * @throws GeneralException If a requested essential ACR value is not
183         *                          supported by the OpenID provider.
184         */
185        @Deprecated
186        public void ensureACRSupport(final AuthenticationRequest authRequest, final OIDCProviderMetadata opMetadata)
187                throws GeneralException {
188                
189                ensureACRSupport(authRequest, opMetadata.getACRs());
190        }
191        
192        
193        /**
194         * Resolves the requested essential and voluntary ACR values from the
195         * specified OpenID authentication request.
196         * 
197         * @param authRequest The OpenID authentication request. Should be
198         *                    resolved. Must not be {@code null}.
199         * 
200         * @return The resolved ACR request.
201         */
202        public static ACRRequest resolve(final AuthenticationRequest authRequest) {
203                
204                List<ACR> essentialACRs = null;
205                List<ACR> voluntaryACRs = null;
206                
207                ClaimsRequest claimsRequest = authRequest.getClaims();
208                
209                if (claimsRequest != null) {
210                        
211                        for (ClaimsRequest.Entry claimEntry: claimsRequest.getIDTokenClaims()) {
212                                
213                                if (! claimEntry.getClaimName().equals("acr"))
214                                        continue;
215                                
216                                if (claimEntry.getClaimRequirement().equals(ClaimRequirement.ESSENTIAL)) {
217                                        
218                                        essentialACRs = new ArrayList<>();
219                                        
220                                        if (claimEntry.getValue() != null)
221                                                essentialACRs.add(new ACR(claimEntry.getValue()));
222                                        
223                                        if (claimEntry.getValues() != null) {
224                                                
225                                                for (String v: claimEntry.getValues())
226                                                        essentialACRs.add(new ACR(v));
227                                        }
228                                        
229                                } else {
230                                        voluntaryACRs = new ArrayList<>();
231                                        
232                                        if (claimEntry.getValue() != null)
233                                                voluntaryACRs.add(new ACR(claimEntry.getValue()));
234                                        
235                                        if (claimEntry.getValues() != null) {
236                                                
237                                                for (String v: claimEntry.getValues())
238                                                        voluntaryACRs.add(new ACR(v));
239                                        }
240                                }
241                        }
242                }
243                
244                
245                List<ACR> topLevelACRs = authRequest.getACRValues();
246                
247                if (topLevelACRs != null) {
248                        
249                        if (voluntaryACRs == null)
250                                voluntaryACRs = new ArrayList<>();
251                        
252                        voluntaryACRs.addAll(topLevelACRs);
253                }
254                
255                return new ACRRequest(essentialACRs, voluntaryACRs);
256        }
257}