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}