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.rp.OIDCClientInformation;
028import net.jcip.annotations.Immutable;
029
030import com.nimbusds.openid.connect.sdk.AuthenticationRequest;
031import com.nimbusds.openid.connect.sdk.ClaimsRequest;
032import com.nimbusds.openid.connect.sdk.claims.ACR;
033import com.nimbusds.openid.connect.sdk.claims.ClaimRequirement;
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 the
135         * OpenID provider.
136         *
137         * @param authRequest The OpenID authentication request. Must not be
138         *                    {@code null}.
139         * @param opMetadata  The OpenID provider metadata. Must not be
140         *                    {@code null}.
141         *
142         * @throws GeneralException If a requested essential ACR value is not
143         *                          supported by the OpenID provider.
144         */
145        public void ensureACRSupport(final AuthenticationRequest authRequest, final OIDCProviderMetadata opMetadata)
146                throws GeneralException {
147                
148                // Ensure any requested essential ACR is supported
149                if (getEssentialACRs() != null) {
150                        
151                        boolean foundSupportedEssentialACR = false;
152                        
153                        for (ACR acr: getEssentialACRs()) {
154                                
155                                if (opMetadata.getACRs() != null && opMetadata.getACRs().contains(acr)) {
156                                        foundSupportedEssentialACR = true;
157                                        break;
158                                }
159                        }
160                        
161                        if (! foundSupportedEssentialACR) {
162                                String msg = "Requested essential ACR(s) not supported";
163                                throw new GeneralException(msg,
164                                        OAuth2Error.ACCESS_DENIED.appendDescription(": " + msg),
165                                        authRequest.getClientID(),
166                                        authRequest.getRedirectionURI(),
167                                        authRequest.impliedResponseMode(),
168                                        authRequest.getState());
169                        }
170                }
171        }
172        
173        
174        /**
175         * Resolves the requested essential and voluntary ACR values from the
176         * specified OpenID authentication request.
177         * 
178         * @param authRequest The OpenID authentication request. Should be
179         *                    resolved. Must not be {@code null}.
180         * 
181         * @return The resolved ACR request.
182         */
183        public static ACRRequest resolve(final AuthenticationRequest authRequest) {
184                
185                List<ACR> essentialACRs = null;
186                List<ACR> voluntaryACRs = null;
187                
188                ClaimsRequest claimsRequest = authRequest.getClaims();
189                
190                if (claimsRequest != null) {
191                        
192                        for (ClaimsRequest.Entry claimEntry: claimsRequest.getIDTokenClaims()) {
193                                
194                                if (! claimEntry.getClaimName().equals("acr"))
195                                        continue;
196                                
197                                if (claimEntry.getClaimRequirement().equals(ClaimRequirement.ESSENTIAL)) {
198                                        
199                                        essentialACRs = new ArrayList<>();
200                                        
201                                        if (claimEntry.getValue() != null)
202                                                essentialACRs.add(new ACR(claimEntry.getValue()));
203                                        
204                                        if (claimEntry.getValues() != null) {
205                                                
206                                                for (String v: claimEntry.getValues())
207                                                        essentialACRs.add(new ACR(v));
208                                        }
209                                        
210                                } else {
211                                        voluntaryACRs = new ArrayList<>();
212                                        
213                                        if (claimEntry.getValue() != null)
214                                                voluntaryACRs.add(new ACR(claimEntry.getValue()));
215                                        
216                                        if (claimEntry.getValues() != null) {
217                                                
218                                                for (String v: claimEntry.getValues())
219                                                        voluntaryACRs.add(new ACR(v));
220                                        }
221                                }
222                        }
223                }
224                
225                
226                List<ACR> topLevelACRs = authRequest.getACRValues();
227                
228                if (topLevelACRs != null) {
229                        
230                        if (voluntaryACRs == null)
231                                voluntaryACRs = new ArrayList<>();
232                        
233                        voluntaryACRs.addAll(topLevelACRs);
234                }
235                
236                return new ACRRequest(essentialACRs, voluntaryACRs);
237        }
238}