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