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.util.CollectionUtils;
026import net.jcip.annotations.Immutable;
027
028import com.nimbusds.oauth2.sdk.AuthorizationRequest;
029import com.nimbusds.oauth2.sdk.GeneralException;
030import com.nimbusds.oauth2.sdk.OAuth2Error;
031import com.nimbusds.oauth2.sdk.ciba.CIBARequest;
032import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
033import com.nimbusds.openid.connect.sdk.OIDCClaimsRequest;
034import com.nimbusds.openid.connect.sdk.OIDCScopeValue;
035import com.nimbusds.openid.connect.sdk.claims.ACR;
036import com.nimbusds.openid.connect.sdk.claims.ClaimRequirement;
037import com.nimbusds.openid.connect.sdk.claims.ClaimsSetRequest;
038import com.nimbusds.openid.connect.sdk.rp.OIDCClientInformation;
039
040
041/**
042 * Resolved authentication Context Class Reference (ACR) request.
043 */
044@Immutable 
045public final class ACRRequest {
046
047
048        /**
049         * The essential ACR values.
050         */
051        private final List<ACR> essentialACRs;
052
053
054        /**
055         * The voluntary ACR values.
056         */
057        private final List<ACR> voluntaryACRs;
058
059
060        /**
061         * Creates a new Authentication Context Class Reference (ACR) request.
062         *
063         * @param essentialACRs The requested essential ACR values, by order of
064         *                      preference, {@code null} if not specified.
065         * @param voluntaryACRs The requested voluntary ACR values, by order of
066         *                      preference, {@code null} if not specified.
067         */
068        public ACRRequest(final List<ACR> essentialACRs, final List<ACR> voluntaryACRs) {
069
070                this.essentialACRs = essentialACRs;
071                this.voluntaryACRs = voluntaryACRs;
072        }
073        
074
075        /**
076         * Gets the requested essential ACR values.
077         * 
078         * @return The essential ACR values, by order of preference, 
079         *         {@code null} if not specified.
080         */
081        public List<ACR> getEssentialACRs() {
082                
083                return essentialACRs;
084        }
085        
086        
087        /**
088         * Gets the requested voluntary ACR values.
089         * 
090         * @return The voluntary ACR values, by order of preference, 
091         *         {@code null} if not specified.
092         */
093        public List<ACR> getVoluntaryACRs() {
094                
095                return voluntaryACRs;
096        }
097        
098        
099        /**
100         * Returns {@code true} if no essential and voluntary ACR values are
101         * requested.
102         * 
103         * @return {@code true} if no essential and voluntary ACR values are
104         *         requested, else {@code false}.
105         */
106        public boolean isEmpty() {
107
108                return !(essentialACRs != null && !essentialACRs.isEmpty()) &&
109                       !(voluntaryACRs != null && !voluntaryACRs.isEmpty());
110        }
111        
112        
113        /**
114         * Applies any default requested ACR values (as voluntary values) from
115         * the registered client information.
116         *
117         * @param clientInfo The registered client information. Must not be
118         *                   {@code null}.
119         *
120         * @return The ACR request, updated if default ACR values are applied.
121         */
122        public ACRRequest applyDefaultACRs(final OIDCClientInformation clientInfo) {
123                
124                return applyDefaultACRs(clientInfo, null);
125        }
126
127
128        /**
129         * Applies any default requested ACR values (as voluntary values) from
130         * the registered client information and a global OpenID provider
131         * configuration.
132         *
133         * @param clientInfo  The registered client information. Must not be
134         *                    {@code null}.
135         * @param defaultACRs Fallback default ACR values in case there are
136         *                    no registered for the client, {@code null} if
137         *                    none.
138         *
139         * @return The ACR request, updated if default ACR values are applied.
140         */
141        public ACRRequest applyDefaultACRs(final OIDCClientInformation clientInfo,
142                                           final List<ACR> defaultACRs) {
143
144                // Apply default ACR from client reg store as voluntary?
145                if (isEmpty()) {
146                        if (clientInfo.getOIDCMetadata().getDefaultACRs() != null) {
147                                List<ACR> voluntaryACRs = new LinkedList<>(clientInfo.getOIDCMetadata().getDefaultACRs());
148                                return new ACRRequest(null, voluntaryACRs);
149                        }
150                        if (CollectionUtils.isNotEmpty(defaultACRs)) {
151                                List<ACR> voluntaryACRs = new LinkedList<>(defaultACRs);
152                                return new ACRRequest(null, voluntaryACRs);
153                        }
154                }
155
156                return this;
157        }
158        
159        
160        /**
161         * Ensures all requested essential ACR values are supported by the
162         * OpenID provider.
163         *
164         * @param authzRequest  The OAuth 2.0 authorisation request / OpenID
165         *                      authentication request. Must not be
166         *                      {@code null}.
167         * @param supportedACRs The ACR values supported by the OpenID
168         *                      provider, {@code null} if not specified.
169         *
170         * @throws GeneralException If a requested essential ACR value is not
171         *                          supported by the OpenID provider.
172         */
173        public void ensureACRSupport(final AuthorizationRequest authzRequest, final List<ACR> supportedACRs)
174                throws GeneralException {
175                
176                // Ensure any requested essential ACR is supported
177                if (getEssentialACRs() != null) {
178                        
179                        boolean foundSupportedEssentialACR = false;
180                        
181                        for (ACR acr: getEssentialACRs()) {
182                                
183                                if (supportedACRs != null && supportedACRs.contains(acr)) {
184                                        foundSupportedEssentialACR = true;
185                                        break;
186                                }
187                        }
188                        
189                        if (! foundSupportedEssentialACR) {
190                                String msg = "Requested essential ACR(s) not supported";
191                                throw new GeneralException(msg,
192                                        OAuth2Error.ACCESS_DENIED.appendDescription(": " + msg),
193                                        authzRequest.getClientID(),
194                                        authzRequest.getRedirectionURI(),
195                                        authzRequest.impliedResponseMode(),
196                                        authzRequest.getState());
197                        }
198                }
199        }
200        
201        
202        /**
203         * Ensures all requested essential ACR values are supported by the
204         * OpenID provider.
205         *
206         * @param authRequest The OpenID authentication request. Must not be
207         *                    {@code null}.
208         * @param opMetadata  The OpenID provider metadata. Must not be
209         *                    {@code null}.
210         *
211         * @throws GeneralException If a requested essential ACR value is not
212         *                          supported by the OpenID provider.
213         */
214        @Deprecated
215        public void ensureACRSupport(final AuthenticationRequest authRequest, final OIDCProviderMetadata opMetadata)
216                throws GeneralException {
217                
218                ensureACRSupport(authRequest, opMetadata.getACRs());
219        }
220        
221        
222        /**
223         * Resolves the requested essential and voluntary ACR values from the
224         * specified OAuth 2.0 authorisation request / OpenID authentication
225         * request.
226         * 
227         * @param authzRequest The OAuth 2.0 authorisation request / OpenID
228         *                     authentication request. Should be resolved. Must
229         *                     not be {@code null}.
230         * 
231         * @return The resolved ACR request.
232         */
233        public static ACRRequest resolve(final AuthorizationRequest authzRequest) {
234                
235                if (! (authzRequest instanceof AuthenticationRequest)) {
236                        // Plain OAuth 2.0
237                        return new ACRRequest(null, null);
238                }
239                
240                // OpenID
241                AuthenticationRequest authRequest = (AuthenticationRequest) authzRequest;
242                
243                // OpenID
244                return resolve(authRequest.getACRValues(), authRequest.getOIDCClaims());
245        }
246        
247        
248        /**
249         * Resolves the requested essential and voluntary ACR values from the
250         * specified CIBA request.
251         *
252         * @param cibaRequest The CIBA request. Must be resolved and not
253         *                    {@code null}.
254         *
255         * @return The resolved ACR request.
256         */
257        public static ACRRequest resolve(final CIBARequest cibaRequest) {
258                
259                if (cibaRequest.isSigned()) {
260                        throw new IllegalArgumentException("The CIBA request must be resolved (not signed)");
261                }
262                
263                if (cibaRequest.getScope() != null && ! cibaRequest.getScope().contains(OIDCScopeValue.OPENID)) {
264                        // Plain OAuth 2.0
265                        return new ACRRequest(null, null);
266                }
267                
268                // OpenID
269                return resolve(cibaRequest.getACRValues(), cibaRequest.getOIDCClaims());
270        }
271        
272        
273        
274        private static ClaimsSetRequest.Entry getACRClaimRequest(final OIDCClaimsRequest claimsRequest) {
275                
276                if (claimsRequest == null) {
277                        return null;
278                }
279                
280                ClaimsSetRequest idTokenClaimsRequest = claimsRequest.getIDTokenClaimsRequest();
281                
282                if (idTokenClaimsRequest == null) {
283                        return null;
284                }
285                
286                for (ClaimsSetRequest.Entry en: idTokenClaimsRequest.getEntries()) {
287                        if ("acr".equals(en.getClaimName())) {
288                                return en;
289                        }
290                }
291                return null;
292        }
293        
294        
295        /**
296         * Resolves the requested essential and voluntary ACR values from the
297         * specified top-level {@code acr_values} request parameter and
298         * {@code claims} request parameter.
299         *
300         * @param acrValues     The top-level {@code acr_values} request
301         *                      parameter, {@code null} if not specified.
302         * @param claimsRequest The OpenID {@code claims} request parameter,
303         *                      {@code null} if not specified.
304         *
305         * @return The resolved ACR request.
306         */
307        public static ACRRequest resolve(final List<ACR> acrValues, final OIDCClaimsRequest claimsRequest) {
308                
309                List<ACR> essentialACRs = null;
310                List<ACR> voluntaryACRs = null;
311                
312                ClaimsSetRequest.Entry en = getACRClaimRequest(claimsRequest);
313                
314                if (en != null) {
315                        if (en.getClaimRequirement().equals(ClaimRequirement.ESSENTIAL)) {
316                                
317                                essentialACRs = new ArrayList<>();
318                                
319                                if (en.getValueAsString() != null)
320                                        essentialACRs.add(new ACR(en.getValueAsString()));
321                                
322                                if (en.getValuesAsListOfStrings() != null) {
323                                        for (String v: en.getValuesAsListOfStrings())
324                                                essentialACRs.add(new ACR(v));
325                                }
326                        } else {
327                                voluntaryACRs = new ArrayList<>();
328                                
329                                if (en.getValueAsString() != null)
330                                        voluntaryACRs.add(new ACR(en.getValueAsString()));
331                                
332                                if (en.getValuesAsListOfStrings() != null) {
333                                        
334                                        for (String v: en.getValuesAsListOfStrings())
335                                                voluntaryACRs.add(new ACR(v));
336                                }
337                        }
338                }
339                
340                if (acrValues != null) {
341                        
342                        if (voluntaryACRs == null)
343                                voluntaryACRs = new ArrayList<>();
344                        
345                        voluntaryACRs.addAll(acrValues);
346                }
347                
348                return new ACRRequest(essentialACRs, voluntaryACRs);
349        }
350}