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