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.oauth2.sdk.ciba; 019 020 021import com.nimbusds.common.contenttype.ContentType; 022import com.nimbusds.jose.JWSObject; 023import com.nimbusds.jwt.JWT; 024import com.nimbusds.jwt.JWTClaimsSet; 025import com.nimbusds.jwt.JWTParser; 026import com.nimbusds.jwt.SignedJWT; 027import com.nimbusds.langtag.LangTag; 028import com.nimbusds.langtag.LangTagException; 029import com.nimbusds.langtag.LangTagUtils; 030import com.nimbusds.oauth2.sdk.AbstractAuthenticatedRequest; 031import com.nimbusds.oauth2.sdk.ParseException; 032import com.nimbusds.oauth2.sdk.Scope; 033import com.nimbusds.oauth2.sdk.SerializeException; 034import com.nimbusds.oauth2.sdk.auth.ClientAuthentication; 035import com.nimbusds.oauth2.sdk.auth.Secret; 036import com.nimbusds.oauth2.sdk.http.HTTPRequest; 037import com.nimbusds.oauth2.sdk.id.Identifier; 038import com.nimbusds.oauth2.sdk.rar.AuthorizationDetail; 039import com.nimbusds.oauth2.sdk.token.BearerAccessToken; 040import com.nimbusds.oauth2.sdk.util.*; 041import com.nimbusds.openid.connect.sdk.OIDCClaimsRequest; 042import com.nimbusds.openid.connect.sdk.claims.ACR; 043import net.jcip.annotations.Immutable; 044 045import java.net.URI; 046import java.util.*; 047 048 049/** 050 * <p>CIBA request to an OpenID provider / OAuth 2.0 authorisation server 051 * backend authentication endpoint. Supports plan as well as signed (JWT) 052 * requests. 053 * 054 * <p>Example HTTP request: 055 * 056 * <pre> 057 * POST /bc-authorize HTTP/1.1 058 * Host: server.example.com 059 * Content-Type: application/x-www-form-urlencoded 060 * 061 * scope=openid%20email%20example-scope& 062 * client_notification_token=8d67dc78-7faa-4d41-aabd-67707b374255& 063 * binding_message=W4SCT& 064 * login_hint_token=eyJraWQiOiJsdGFjZXNidyIsImFsZyI6IkVTMjU2In0.eyJ 065 * zdWJfaWQiOnsic3ViamVjdF90eXBlIjoicGhvbmUiLCJwaG9uZSI6IisxMzMwMjg 066 * xODAwNCJ9fQ.Kk8jcUbHjJAQkRSHyDuFQr3NMEOSJEZc85VfER74tX6J9CuUllr8 067 * 9WKUHUR7MA0-mWlptMRRhdgW1ZDt7g1uwQ& 068 * client_assertion_type=urn%3Aietf%3Aparams%3Aoauth%3A 069 * client-assertion-type%3Ajwt-bearer& 070 * client_assertion=eyJraWQiOiJsdGFjZXNidyIsImFsZyI6IkVTMjU2In0.eyJ 071 * pc3MiOiJzNkJoZFJrcXQzIiwic3ViIjoiczZCaGRSa3F0MyIsImF1ZCI6Imh0dHB 072 * zOi8vc2VydmVyLmV4YW1wbGUuY29tIiwianRpIjoiYmRjLVhzX3NmLTNZTW80RlN 073 * 6SUoyUSIsImlhdCI6MTUzNzgxOTQ4NiwiZXhwIjoxNTM3ODE5Nzc3fQ.Ybr8mg_3 074 * E2OptOSsA8rnelYO_y1L-yFaF_j1iemM3ntB61_GN3APe5cl_-5a6cvGlP154XAK 075 * 7fL-GaZSdnd9kg 076 * </pre> 077 * 078 * <p>Related specifications: 079 * 080 * <ul> 081 * <li>OpenID Connect CIBA Flow - Core 1.0 082 * </ul> 083 */ 084@Immutable 085public class CIBARequest extends AbstractAuthenticatedRequest { 086 087 088 /** 089 * The maximum allowed length of a client notification token. 090 */ 091 public static final int CLIENT_NOTIFICATION_TOKEN_MAX_LENGTH = 1024; 092 093 094 /** 095 * The registered parameter names. 096 */ 097 private static final Set<String> REGISTERED_PARAMETER_NAMES; 098 099 static { 100 Set<String> p = new HashSet<>(); 101 102 // Plain 103 p.add("scope"); 104 p.add("client_notification_token"); 105 p.add("acr_values"); 106 p.add("login_hint_token"); 107 p.add("id_token_hint"); 108 p.add("login_hint"); 109 p.add("binding_message"); 110 p.add("user_code"); 111 p.add("requested_expiry"); 112 p.add("claims"); 113 p.add("claims_locales"); 114 p.add("purpose"); 115 p.add("authorization_details"); 116 p.add("resource"); 117 118 // Signed JWT 119 p.add("request"); 120 121 REGISTERED_PARAMETER_NAMES = Collections.unmodifiableSet(p); 122 } 123 124 125 /** 126 * The scope (required), must contain {@code openid}. 127 */ 128 private final Scope scope; 129 130 131 /** 132 * The client notification token, required for the CIBA ping and push 133 * token delivery modes. 134 */ 135 private final BearerAccessToken clientNotificationToken; 136 137 138 /** 139 * Requested Authentication Context Class Reference values (optional). 140 */ 141 private final List<ACR> acrValues; 142 143 144 /** 145 * A token containing information identifying the end-user for whom 146 * authentication is being requested (optional). 147 */ 148 private final String loginHintTokenString; 149 150 151 /** 152 * Previously issued ID token passed as a hint to identify the end-user 153 * for whom authentication is being requested (optional). 154 */ 155 private final JWT idTokenHint; 156 157 158 /** 159 * Login hint (email address, phone number, etc) about the end-user for 160 * whom authentication is being requested (optional). 161 */ 162 private final String loginHint; 163 164 165 /** 166 * Human-readable binding message for the display at the consumption 167 * and authentication devices (optional). 168 */ 169 private final String bindingMessage; 170 171 172 /** 173 * User secret code (password, PIN, etc.) to authorise the CIBA request 174 * with the authentication device (optional). 175 */ 176 private final Secret userCode; 177 178 179 /** 180 * Requested expiration for the {@code auth_req_id} (optional). 181 */ 182 private final Integer requestedExpiry; 183 184 185 /** 186 * Individual claims to be returned (optional). 187 */ 188 private final OIDCClaimsRequest claims; 189 190 191 /** 192 * The end-user's preferred languages and scripts for claims being 193 * returned (optional). 194 */ 195 private final List<LangTag> claimsLocales; 196 197 198 /** 199 * The transaction specific purpose, for use in OpenID Connect Identity 200 * Assurance (optional). 201 */ 202 private final String purpose; 203 204 205 /** 206 * The RAR details (optional). 207 */ 208 private final List<AuthorizationDetail> authorizationDetails; 209 210 211 /** 212 * The resource URI(s) (optional). 213 */ 214 private final List<URI> resources; 215 216 217 /** 218 * Custom parameters. 219 */ 220 private final Map<String,List<String>> customParams; 221 222 223 /** 224 * The JWT for a signed request. 225 */ 226 private final SignedJWT signedRequest; 227 228 229 /** 230 * Builder for constructing CIBA requests. 231 */ 232 public static class Builder { 233 234 235 /** 236 * The endpoint URI (optional). 237 */ 238 private URI endpoint; 239 240 241 /** 242 * The client authentication (required). 243 */ 244 private final ClientAuthentication clientAuth; 245 246 247 /** 248 * The scope (required). 249 */ 250 private final Scope scope; 251 252 253 /** 254 * The client notification type, required for the CIBA ping and 255 * push token delivery modes. 256 */ 257 private BearerAccessToken clientNotificationToken; 258 259 260 /** 261 * Requested Authentication Context Class Reference values 262 * (optional). 263 */ 264 private List<ACR> acrValues; 265 266 267 /** 268 * A token containing information identifying the end-user for 269 * whom authentication is being requested (optional). 270 */ 271 private String loginHintTokenString; 272 273 274 /** 275 * Previously issued ID token passed as a hint to identify the 276 * end-user for whom authentication is being requested 277 * (optional). 278 */ 279 private JWT idTokenHint; 280 281 282 /** 283 * Identity hint (email address, phone number, etc) about the 284 * end-user for whom authentication is being requested 285 * (optional). 286 */ 287 private String loginHint; 288 289 290 /** 291 * Human readable binding message for the display at the 292 * consumption and authentication devices (optional). 293 */ 294 private String bindingMessage; 295 296 297 /** 298 * User secret code (password, PIN, etc) to authorise the CIBA 299 * request with the authentication device (optional). 300 */ 301 private Secret userCode; 302 303 304 /** 305 * Requested expiration for the {@code auth_req_id} (optional). 306 */ 307 private Integer requestedExpiry; 308 309 310 /** 311 * Individual claims to be returned (optional). 312 */ 313 private OIDCClaimsRequest claims; 314 315 316 /** 317 * The end-user's preferred languages and scripts for claims 318 * being returned (optional). 319 */ 320 private List<LangTag> claimsLocales; 321 322 323 /** 324 * The transaction specific purpose (optional). 325 */ 326 private String purpose; 327 328 329 /** 330 * The RAR details (optional). 331 */ 332 private List<AuthorizationDetail> authorizationDetails; 333 334 335 /** 336 * The resource URI(s) (optional). 337 */ 338 private List<URI> resources; 339 340 341 /** 342 * Custom parameters. 343 */ 344 private Map<String,List<String>> customParams = new HashMap<>(); 345 346 347 /** 348 * The JWT for a signed request. 349 */ 350 private final SignedJWT signedRequest; 351 352 353 /** 354 * Creates a new CIBA request builder. 355 * 356 * @param clientAuth The client authentication. Must not be 357 * {@code null}. 358 * @param scope The requested scope, {@code null} if not 359 * specified. 360 */ 361 public Builder(final ClientAuthentication clientAuth, 362 final Scope scope) { 363 364 this.clientAuth = Objects.requireNonNull(clientAuth); 365 this.scope = scope; 366 signedRequest = null; 367 } 368 369 370 /** 371 * Creates a new CIBA signed request builder. 372 * 373 * @param clientAuth The client authentication. Must not be 374 * {@code null}. 375 * @param signedRequest The signed request JWT. Must not be 376 * {@code null}. 377 */ 378 public Builder(final ClientAuthentication clientAuth, 379 final SignedJWT signedRequest) { 380 381 this.clientAuth = Objects.requireNonNull(clientAuth); 382 this.signedRequest = Objects.requireNonNull(signedRequest); 383 scope = null; 384 } 385 386 387 /** 388 * Creates a new CIBA request builder from the specified 389 * request. 390 * 391 * @param request The CIBA request. Must not be {@code null}. 392 */ 393 public Builder(final CIBARequest request) { 394 395 endpoint = request.getEndpointURI(); 396 clientAuth = request.getClientAuthentication(); 397 scope = request.getScope(); 398 clientNotificationToken = request.getClientNotificationToken(); 399 acrValues = request.getACRValues(); 400 loginHintTokenString = request.getLoginHintTokenString(); 401 idTokenHint = request.getIDTokenHint(); 402 loginHint = request.getLoginHint(); 403 bindingMessage = request.getBindingMessage(); 404 userCode = request.getUserCode(); 405 requestedExpiry = request.getRequestedExpiry(); 406 claims = request.getOIDCClaims(); 407 claimsLocales = request.getClaimsLocales(); 408 purpose = request.getPurpose(); 409 authorizationDetails = request.getAuthorizationDetails(); 410 resources = request.getResources(); 411 customParams = request.getCustomParameters(); 412 signedRequest = request.getRequestJWT(); 413 } 414 415 416 /** 417 * Sets the client notification token, required for the CIBA 418 * ping and push token delivery modes. Corresponds to the 419 * {@code client_notification_token} parameter. 420 * 421 * @param token The client notification token, {@code null} if 422 * not specified. 423 * 424 * @return This builder. 425 */ 426 public Builder clientNotificationToken(final BearerAccessToken token) { 427 this.clientNotificationToken = token; 428 return this; 429 } 430 431 432 /** 433 * Sets the requested Authentication Context Class Reference 434 * values. Corresponds to the optional {@code acr_values} 435 * parameter. 436 * 437 * @param acrValues The requested ACR values, {@code null} if 438 * not specified. 439 * 440 * @return This builder. 441 */ 442 public Builder acrValues(final List<ACR> acrValues) { 443 this.acrValues = acrValues; 444 return this; 445 } 446 447 448 /** 449 * Sets the login hint token string, containing information 450 * identifying the end-user for whom authentication is being requested. 451 * Corresponds to the {@code login_hint_token} parameter. 452 * 453 * @param loginHintTokenString The login hint token string, 454 * {@code null} if not specified. 455 * 456 * @return This builder. 457 */ 458 public Builder loginHintTokenString(final String loginHintTokenString) { 459 this.loginHintTokenString = loginHintTokenString; 460 return this; 461 } 462 463 464 /** 465 * Sets the ID Token hint, passed as a hint to identify the 466 * end-user for whom authentication is being requested. 467 * Corresponds to the {@code id_token_hint} parameter. 468 * 469 * @param idTokenHint The ID Token hint, {@code null} if not 470 * specified. 471 * 472 * @return This builder. 473 */ 474 public Builder idTokenHint(final JWT idTokenHint) { 475 this.idTokenHint = idTokenHint; 476 return this; 477 } 478 479 480 /** 481 * Sets the login hint (email address, phone number, etc), 482 * about the end-user for whom authentication is being 483 * requested. Corresponds to the {@code login_hint} parameter. 484 * 485 * @param loginHint The login hint, {@code null} if not 486 * specified. 487 * 488 * @return This builder. 489 */ 490 public Builder loginHint(final String loginHint) { 491 this.loginHint = loginHint; 492 return this; 493 } 494 495 496 /** 497 * Sets the human readable binding message for the display at 498 * the consumption and authentication devices. Corresponds to 499 * the {@code binding_message} parameter. 500 * 501 * @param bindingMessage The binding message, {@code null} if 502 * not specified. 503 * 504 * @return This builder. 505 */ 506 public Builder bindingMessage(final String bindingMessage) { 507 this.bindingMessage = bindingMessage; 508 return this; 509 } 510 511 512 /** 513 * Gets the user secret code (password, PIN, etc) to authorise 514 * the CIBA request with the authentication device. Corresponds 515 * to the {@code user_code} parameter. 516 * 517 * @param userCode The user code, {@code null} if not 518 * specified. 519 * 520 * @return This builder. 521 */ 522 public Builder userCode(final Secret userCode) { 523 this.userCode = userCode; 524 return this; 525 } 526 527 528 /** 529 * Sets the requested expiration for the {@code auth_req_id}. 530 * Corresponds to the {@code requested_expiry} parameter. 531 * 532 * @param requestedExpiry The required expiry (as positive 533 * integer), {@code null} if not 534 * specified. 535 * 536 * @return This builder. 537 */ 538 public Builder requestedExpiry(final Integer requestedExpiry) { 539 this.requestedExpiry = requestedExpiry; 540 return this; 541 } 542 543 544 /** 545 * Sets the individual OpenID claims to be returned. 546 * Corresponds to the optional {@code claims} parameter. 547 * 548 * @param claims The individual OpenID claims to be returned, 549 * {@code null} if not specified. 550 * 551 * @return This builder. 552 */ 553 public Builder claims(final OIDCClaimsRequest claims) { 554 555 this.claims = claims; 556 return this; 557 } 558 559 560 /** 561 * Sets the end-user's preferred languages and scripts for the 562 * claims being returned, ordered by preference. Corresponds to 563 * the optional {@code claims_locales} parameter. 564 * 565 * @param claimsLocales The preferred claims locales, 566 * {@code null} if not specified. 567 * 568 * @return This builder. 569 */ 570 public Builder claimsLocales(final List<LangTag> claimsLocales) { 571 572 this.claimsLocales = claimsLocales; 573 return this; 574 } 575 576 577 /** 578 * Sets the transaction specific purpose. Corresponds to the 579 * optional {@code purpose} parameter. 580 * 581 * @param purpose The purpose, {@code null} if not specified. 582 * 583 * @return This builder. 584 */ 585 public Builder purpose(final String purpose) { 586 587 this.purpose = purpose; 588 return this; 589 } 590 591 592 /** 593 * Sets the Rich Authorisation Request (RAR) details. 594 * 595 * @param authorizationDetails The authorisation details, 596 * {@code null} if not specified. 597 * 598 * @return This builder. 599 */ 600 public Builder authorizationDetails(final List<AuthorizationDetail> authorizationDetails) { 601 this.authorizationDetails = authorizationDetails; 602 return this; 603 } 604 605 606 /** 607 * Sets the resource server URI. 608 * 609 * @param resource The resource URI, {@code null} if not 610 * specified. 611 * 612 * @return This builder. 613 */ 614 public Builder resource(final URI resource) { 615 if (resource != null) { 616 this.resources = Collections.singletonList(resource); 617 } else { 618 this.resources = null; 619 } 620 return this; 621 } 622 623 624 /** 625 * Sets the resource server URI(s). 626 * 627 * @param resources The resource URI(s), {@code null} if not 628 * specified. 629 * 630 * @return This builder. 631 */ 632 public Builder resources(final URI ... resources) { 633 if (resources != null) { 634 this.resources = Arrays.asList(resources); 635 } else { 636 this.resources = null; 637 } 638 return this; 639 } 640 641 642 /** 643 * Sets a custom parameter. 644 * 645 * @param name The parameter name. Must not be {@code null}. 646 * @param values The parameter values, {@code null} if not 647 * specified. 648 * 649 * @return This builder. 650 */ 651 public Builder customParameter(final String name, final String ... values) { 652 653 if (values == null || values.length == 0) { 654 customParams.remove(name); 655 } else { 656 customParams.put(name, Arrays.asList(values)); 657 } 658 659 return this; 660 } 661 662 663 /** 664 * Sets the URI of the CIBA endpoint. 665 * 666 * @param endpoint The URI of the CIBA endpoint. May be 667 * {@code null} if the {@link #toHTTPRequest()} 668 * method is not going to be used. 669 * 670 * @return This builder. 671 */ 672 public Builder endpointURI(final URI endpoint) { 673 674 this.endpoint = endpoint; 675 return this; 676 } 677 678 679 /** 680 * Builds a new CIBA request. 681 * 682 * @return The CIBA request. 683 */ 684 public CIBARequest build() { 685 686 try { 687 if (signedRequest != null) { 688 return new CIBARequest( 689 endpoint, 690 clientAuth, 691 signedRequest 692 ); 693 } 694 695 // Plain request 696 return new CIBARequest( 697 endpoint, 698 clientAuth, 699 scope, 700 clientNotificationToken, 701 acrValues, 702 loginHintTokenString, 703 idTokenHint, 704 loginHint, 705 bindingMessage, 706 userCode, 707 requestedExpiry, 708 claims, 709 claimsLocales, 710 purpose, 711 authorizationDetails, 712 resources, 713 customParams 714 ); 715 } catch (IllegalArgumentException e) { 716 throw new IllegalArgumentException(e.getMessage(), e); 717 } 718 } 719 } 720 721 722 /** 723 * Creates a new CIBA request. 724 * 725 * @param endpoint The URI of the CIBA endpoint. May be 726 * {@code null} if the 727 * {@link #toHTTPRequest()} method is 728 * not going to be used. 729 * @param clientAuth The client authentication. Must not 730 * be {@code null}. 731 * @param scope The requested scope. Must not be 732 * empty or {@code null}. 733 * @param clientNotificationToken The client notification token, 734 * {@code null} if not specified. 735 * @param acrValues The requested ACR values, 736 * {@code null} if not specified. 737 * @param loginHintTokenString The login hint token string, 738 * {@code null} if not specified. 739 * @param idTokenHint The ID Token hint, {@code null} if 740 * not specified. 741 * @param loginHint The login hint, {@code null} if not 742 * specified. 743 * @param bindingMessage The binding message, {@code null} if 744 * not specified. 745 * @param userCode The user code, {@code null} if not 746 * specified. 747 * @param requestedExpiry The required expiry (as positive 748 * integer), {@code null} if not 749 * specified. 750 * @param customParams Custom parameters, empty or 751 * {@code null} if not specified. 752 */ 753 @Deprecated 754 public CIBARequest(final URI endpoint, 755 final ClientAuthentication clientAuth, 756 final Scope scope, 757 final BearerAccessToken clientNotificationToken, 758 final List<ACR> acrValues, 759 final String loginHintTokenString, 760 final JWT idTokenHint, 761 final String loginHint, 762 final String bindingMessage, 763 final Secret userCode, 764 final Integer requestedExpiry, 765 final Map<String, List<String>> customParams) { 766 767 this(endpoint, clientAuth, 768 scope, clientNotificationToken, acrValues, 769 loginHintTokenString, idTokenHint, loginHint, 770 bindingMessage, userCode, requestedExpiry, 771 null, customParams); 772 } 773 774 775 /** 776 * Creates a new CIBA request. 777 * 778 * @param endpoint The URI of the CIBA endpoint. May be 779 * {@code null} if the 780 * {@link #toHTTPRequest()} method is 781 * not going to be used. 782 * @param clientAuth The client authentication. Must not 783 * be {@code null}. 784 * @param scope The requested scope. Must not be 785 * empty or {@code null}. 786 * @param clientNotificationToken The client notification token, 787 * {@code null} if not specified. 788 * @param acrValues The requested ACR values, 789 * {@code null} if not specified. 790 * @param loginHintTokenString The login hint token string, 791 * {@code null} if not specified. 792 * @param idTokenHint The ID Token hint, {@code null} if 793 * not specified. 794 * @param loginHint The login hint, {@code null} if not 795 * specified. 796 * @param bindingMessage The binding message, {@code null} if 797 * not specified. 798 * @param userCode The user code, {@code null} if not 799 * specified. 800 * @param requestedExpiry The required expiry (as positive 801 * integer), {@code null} if not 802 * specified. 803 * @param claims The individual claims to be returned, 804 * {@code null} if not specified. 805 * @param customParams Custom parameters, empty or 806 * {@code null} if not specified. 807 */ 808 @Deprecated 809 public CIBARequest(final URI endpoint, 810 final ClientAuthentication clientAuth, 811 final Scope scope, 812 final BearerAccessToken clientNotificationToken, 813 final List<ACR> acrValues, 814 final String loginHintTokenString, 815 final JWT idTokenHint, 816 final String loginHint, 817 final String bindingMessage, 818 final Secret userCode, 819 final Integer requestedExpiry, 820 final OIDCClaimsRequest claims, 821 final Map<String, List<String>> customParams) { 822 823 this(endpoint, clientAuth, 824 scope, clientNotificationToken, acrValues, 825 loginHintTokenString, idTokenHint, loginHint, 826 bindingMessage, userCode, requestedExpiry, 827 claims, null, null, 828 null, 829 customParams); 830 } 831 832 833 /** 834 * Creates a new CIBA request. 835 * 836 * @param endpoint The URI of the CIBA endpoint. May be 837 * {@code null} if the 838 * {@link #toHTTPRequest()} method is 839 * not going to be used. 840 * @param clientAuth The client authentication. Must not 841 * be {@code null}. 842 * @param scope The requested scope. Must not be 843 * empty or {@code null}. 844 * @param clientNotificationToken The client notification token, 845 * {@code null} if not specified. 846 * @param acrValues The requested ACR values, 847 * {@code null} if not specified. 848 * @param loginHintTokenString The login hint token string, 849 * {@code null} if not specified. 850 * @param idTokenHint The ID Token hint, {@code null} if 851 * not specified. 852 * @param loginHint The login hint, {@code null} if not 853 * specified. 854 * @param bindingMessage The binding message, {@code null} if 855 * not specified. 856 * @param userCode The user code, {@code null} if not 857 * specified. 858 * @param requestedExpiry The required expiry (as positive 859 * integer), {@code null} if not 860 * specified. 861 * @param claims The individual claims to be 862 * returned, {@code null} if not 863 * specified. 864 * @param claimsLocales The preferred languages and scripts 865 * for claims being returned, 866 * {@code null} if not specified. 867 * @param purpose The transaction specific purpose, 868 * {@code null} if not specified. 869 * @param resources The resource URI(s), {@code null} if 870 * not specified. 871 * @param customParams Custom parameters, empty or 872 * {@code null} if not specified. 873 */ 874 @Deprecated 875 public CIBARequest(final URI endpoint, 876 final ClientAuthentication clientAuth, 877 final Scope scope, 878 final BearerAccessToken clientNotificationToken, 879 final List<ACR> acrValues, 880 final String loginHintTokenString, 881 final JWT idTokenHint, 882 final String loginHint, 883 final String bindingMessage, 884 final Secret userCode, 885 final Integer requestedExpiry, 886 final OIDCClaimsRequest claims, 887 final List<LangTag> claimsLocales, 888 final String purpose, 889 final List<URI> resources, 890 final Map<String, List<String>> customParams) { 891 892 this(endpoint, clientAuth, 893 scope, clientNotificationToken, acrValues, 894 loginHintTokenString, idTokenHint, loginHint, 895 bindingMessage, userCode, requestedExpiry, 896 claims, claimsLocales, purpose, null ,resources, 897 customParams); 898 } 899 900 901 /** 902 * Creates a new CIBA request. 903 * 904 * @param endpoint The URI of the CIBA endpoint. May be 905 * {@code null} if the 906 * {@link #toHTTPRequest()} method is 907 * not going to be used. 908 * @param clientAuth The client authentication. Must not 909 * be {@code null}. 910 * @param scope The requested scope. Must not be 911 * empty or {@code null}. 912 * @param clientNotificationToken The client notification token, 913 * {@code null} if not specified. 914 * @param acrValues The requested ACR values, 915 * {@code null} if not specified. 916 * @param loginHintTokenString The login hint token string, 917 * {@code null} if not specified. 918 * @param idTokenHint The ID Token hint, {@code null} if 919 * not specified. 920 * @param loginHint The login hint, {@code null} if not 921 * specified. 922 * @param bindingMessage The binding message, {@code null} if 923 * not specified. 924 * @param userCode The user code, {@code null} if not 925 * specified. 926 * @param requestedExpiry The required expiry (as positive 927 * integer), {@code null} if not 928 * specified. 929 * @param claims The individual claims to be 930 * returned, {@code null} if not 931 * specified. 932 * @param claimsLocales The preferred languages and scripts 933 * for claims being returned, 934 * {@code null} if not specified. 935 * @param purpose The transaction specific purpose, 936 * {@code null} if not specified. 937 * @param authorizationDetails The Rich Authorisation Request (RAR) 938 * details, {@code null} if not 939 * specified. 940 * @param resources The resource URI(s), {@code null} if 941 * not specified. 942 * @param customParams Custom parameters, empty or 943 * {@code null} if not specified. 944 */ 945 public CIBARequest(final URI endpoint, 946 final ClientAuthentication clientAuth, 947 final Scope scope, 948 final BearerAccessToken clientNotificationToken, 949 final List<ACR> acrValues, 950 final String loginHintTokenString, 951 final JWT idTokenHint, 952 final String loginHint, 953 final String bindingMessage, 954 final Secret userCode, 955 final Integer requestedExpiry, 956 final OIDCClaimsRequest claims, 957 final List<LangTag> claimsLocales, 958 final String purpose, 959 final List<AuthorizationDetail> authorizationDetails, 960 final List<URI> resources, 961 final Map<String, List<String>> customParams) { 962 963 super(endpoint, clientAuth); 964 965 this.scope = scope; 966 967 if (clientNotificationToken != null && clientNotificationToken.getValue().length() > CLIENT_NOTIFICATION_TOKEN_MAX_LENGTH) { 968 throw new IllegalArgumentException("The client notification token must not exceed " + CLIENT_NOTIFICATION_TOKEN_MAX_LENGTH + " chars"); 969 } 970 this.clientNotificationToken = clientNotificationToken; 971 972 this.acrValues = acrValues; 973 974 // https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0-03.html#rfc.section.7.1 975 // As in the CIBA flow the OP does not have an interaction with 976 // the end-user through the consumption device, it is REQUIRED 977 // that the Client provides one (and only one) of the hints 978 // specified above in the authentication request, that is 979 // "login_hint_token", "id_token_hint" or "login_hint". 980 int numHints = 0; 981 982 if (loginHintTokenString != null) numHints++; 983 this.loginHintTokenString = loginHintTokenString; 984 985 if (idTokenHint != null) numHints++; 986 this.idTokenHint = idTokenHint; 987 988 if (loginHint != null) numHints++; 989 this.loginHint = loginHint; 990 991 if (numHints != 1) { 992 throw new IllegalArgumentException("One user identity hist must be provided (login_hint_token, id_token_hint or login_hint)"); 993 } 994 995 this.bindingMessage = bindingMessage; 996 997 this.userCode = userCode; 998 999 if (requestedExpiry != null && requestedExpiry < 1) { 1000 throw new IllegalArgumentException("The requested expiry must be a positive integer"); 1001 } 1002 this.requestedExpiry = requestedExpiry; 1003 1004 this.claims = claims; 1005 1006 if (claimsLocales != null) { 1007 this.claimsLocales = Collections.unmodifiableList(claimsLocales); 1008 } else { 1009 this.claimsLocales = null; 1010 } 1011 1012 this.purpose = purpose; 1013 1014 this.authorizationDetails = authorizationDetails; 1015 1016 this.resources = ResourceUtils.ensureLegalResourceURIs(resources); 1017 1018 this.customParams = customParams != null ? customParams : Collections.<String, List<String>>emptyMap(); 1019 1020 signedRequest = null; 1021 } 1022 1023 1024 /** 1025 * Creates a new CIBA signed request. 1026 * 1027 * @param endpoint The URI of the CIBA endpoint. May be 1028 * {@code null} if the {@link #toHTTPRequest()} 1029 * method is not going to be used. 1030 * @param clientAuth The client authentication. Must not be 1031 * {@code null}. 1032 * @param signedRequest The signed request JWT. Must not be 1033 * {@code null}. 1034 */ 1035 public CIBARequest(final URI endpoint, 1036 final ClientAuthentication clientAuth, 1037 final SignedJWT signedRequest) { 1038 1039 super(endpoint, clientAuth); 1040 1041 if (JWSObject.State.UNSIGNED.equals(signedRequest.getState())) { 1042 throw new IllegalArgumentException("The request JWT must be in a signed state"); 1043 } 1044 this.signedRequest = signedRequest; 1045 1046 scope = null; 1047 clientNotificationToken = null; 1048 acrValues = null; 1049 loginHintTokenString = null; 1050 idTokenHint = null; 1051 loginHint = null; 1052 bindingMessage = null; 1053 userCode = null; 1054 requestedExpiry = null; 1055 claims = null; 1056 claimsLocales = null; 1057 authorizationDetails = null; 1058 purpose = null; 1059 resources = null; 1060 customParams = Collections.emptyMap(); 1061 } 1062 1063 1064 /** 1065 * Returns the registered (standard) CIBA request parameter names. 1066 * 1067 * @return The registered CIBA request parameter names, as an 1068 * unmodifiable set. 1069 */ 1070 public static Set<String> getRegisteredParameterNames() { 1071 1072 return REGISTERED_PARAMETER_NAMES; 1073 } 1074 1075 1076 /** 1077 * Returns the scope. Corresponds to the optional {@code scope} 1078 * parameter. 1079 * 1080 * @return The scope, {@code null} if not specified. 1081 */ 1082 public Scope getScope() { 1083 1084 return scope; 1085 } 1086 1087 1088 /** 1089 * Returns the client notification token, required for the CIBA ping 1090 * and push token delivery modes. Corresponds to the 1091 * {@code client_notification_token} parameter. 1092 * 1093 * @return The client notification token, {@code null} if not 1094 * specified. 1095 */ 1096 public BearerAccessToken getClientNotificationToken() { 1097 1098 return clientNotificationToken; 1099 } 1100 1101 1102 /** 1103 * Returns the requested Authentication Context Class Reference values. 1104 * Corresponds to the optional {@code acr_values} parameter. 1105 * 1106 * @return The requested ACR values, {@code null} if not specified. 1107 */ 1108 public List<ACR> getACRValues() { 1109 1110 return acrValues; 1111 } 1112 1113 1114 /** 1115 * Returns the hint type. 1116 * 1117 * @return The hint type. 1118 */ 1119 public CIBAHintType getHintType() { 1120 1121 if (getLoginHintTokenString() != null) { 1122 return CIBAHintType.LOGIN_HINT_TOKEN; 1123 } else if (getIDTokenHint() != null) { 1124 return CIBAHintType.ID_TOKEN_HINT; 1125 } else { 1126 return CIBAHintType.LOGIN_HINT; 1127 } 1128 } 1129 1130 1131 /** 1132 * Returns the login hint token string, containing information 1133 * identifying the end-user for whom authentication is being requested. 1134 * Corresponds to the {@code login_hint_token} parameter. 1135 * 1136 * @return The login hint token string, {@code null} if not 1137 * specified. 1138 */ 1139 public String getLoginHintTokenString() { 1140 1141 return loginHintTokenString; 1142 } 1143 1144 1145 /** 1146 * Returns the ID Token hint, passed as a hint to identify the end-user 1147 * for whom authentication is being requested. Corresponds to the 1148 * {@code id_token_hint} parameter. 1149 * 1150 * @return The ID Token hint, {@code null} if not specified. 1151 */ 1152 public JWT getIDTokenHint() { 1153 1154 return idTokenHint; 1155 } 1156 1157 1158 /** 1159 * Returns the login hint (email address, phone number, etc), about the 1160 * end-user for whom authentication is being requested. Corresponds to 1161 * the {@code login_hint} parameter. 1162 * 1163 * @return The login hint, {@code null} if not specified. 1164 */ 1165 public String getLoginHint() { 1166 1167 return loginHint; 1168 } 1169 1170 1171 /** 1172 * Returns the human-readable binding message for the display at the 1173 * consumption and authentication devices. Corresponds to the 1174 * {@code binding_message} parameter. 1175 * 1176 * @return The binding message, {@code null} if not specified. 1177 */ 1178 public String getBindingMessage() { 1179 1180 return bindingMessage; 1181 } 1182 1183 1184 /** 1185 * Returns the user secret code (password, PIN, etc) to authorise the 1186 * CIBA request with the authentication device. Corresponds to the 1187 * {@code user_code} parameter. 1188 * 1189 * @return The user code, {@code null} if not specified. 1190 */ 1191 public Secret getUserCode() { 1192 1193 return userCode; 1194 } 1195 1196 1197 /** 1198 * Returns the requested expiration for the {@code auth_req_id}. 1199 * Corresponds to the {@code requested_expiry} parameter. 1200 * 1201 * @return The required expiry (as positive integer), {@code null} if 1202 * not specified. 1203 */ 1204 public Integer getRequestedExpiry() { 1205 1206 return requestedExpiry; 1207 } 1208 1209 1210 /** 1211 * Returns the individual claims to be returned. Corresponds to the 1212 * optional {@code claims} parameter. 1213 * 1214 * @return The individual claims to be returned, {@code null} if not 1215 * specified. 1216 */ 1217 public OIDCClaimsRequest getOIDCClaims() { 1218 1219 return claims; 1220 } 1221 1222 1223 /** 1224 * Returns the end-user's preferred languages and scripts for the 1225 * claims being returned, ordered by preference. Corresponds to the 1226 * optional {@code claims_locales} parameter. 1227 * 1228 * @return The preferred claims locales, {@code null} if not specified. 1229 */ 1230 public List<LangTag> getClaimsLocales() { 1231 1232 return claimsLocales; 1233 } 1234 1235 1236 /** 1237 * Returns the transaction specific purpose. Corresponds to the 1238 * optional {@code purpose} parameter. 1239 * 1240 * @return The purpose, {@code null} if not specified. 1241 */ 1242 public String getPurpose() { 1243 1244 return purpose; 1245 } 1246 1247 1248 /** 1249 * Returns the Rich Authorisation Request (RAR) details. 1250 * 1251 * @return The authorisation details, {@code null} if not specified. 1252 */ 1253 public List<AuthorizationDetail> getAuthorizationDetails() { 1254 1255 return authorizationDetails; 1256 } 1257 1258 1259 /** 1260 * Returns the resource server URI. 1261 * 1262 * @return The resource URI(s), {@code null} if not specified. 1263 */ 1264 public List<URI> getResources() { 1265 1266 return resources; 1267 } 1268 1269 1270 /** 1271 * Returns the additional custom parameters. 1272 * 1273 * @return The additional custom parameters as an unmodifiable map, 1274 * empty map if none. 1275 */ 1276 public Map<String, List<String>> getCustomParameters() { 1277 1278 return customParams; 1279 } 1280 1281 1282 /** 1283 * Returns the specified custom parameter. 1284 * 1285 * @param name The parameter name. Must not be {@code null}. 1286 * 1287 * @return The parameter value(s), {@code null} if not specified. 1288 */ 1289 public List<String> getCustomParameter(final String name) { 1290 1291 return customParams.get(name); 1292 } 1293 1294 1295 /** 1296 * Returns {@code true} if this request is signed. 1297 * 1298 * @return {@code true} for a signed request, {@code false} for a plain 1299 * request. 1300 */ 1301 public boolean isSigned() { 1302 1303 return signedRequest != null; 1304 } 1305 1306 1307 /** 1308 * Returns the JWT for a signed request. 1309 * 1310 * @return The request JWT. 1311 */ 1312 public SignedJWT getRequestJWT() { 1313 1314 return signedRequest; 1315 } 1316 1317 1318 /** 1319 * Returns the for parameters for this CIBA request. Parameters which 1320 * are part of the client authentication are not included. 1321 * 1322 * @return The parameters. 1323 */ 1324 public Map<String, List<String>> toParameters() { 1325 1326 // Put custom params first, so they may be overwritten by std params 1327 Map<String, List<String>> params = new LinkedHashMap<>(getCustomParameters()); 1328 1329 if (isSigned()) { 1330 params.put("request", Collections.singletonList(signedRequest.serialize())); 1331 return params; 1332 } 1333 1334 if (CollectionUtils.isNotEmpty(getScope())) { 1335 params.put("scope", Collections.singletonList(getScope().toString())); 1336 } 1337 1338 if (getClientNotificationToken() != null) { 1339 params.put("client_notification_token", Collections.singletonList(getClientNotificationToken().getValue())); 1340 } 1341 if (getACRValues() != null) { 1342 params.put("acr_values", Identifier.toStringList(getACRValues())); 1343 } 1344 if (getLoginHintTokenString() != null) { 1345 params.put("login_hint_token", Collections.singletonList(getLoginHintTokenString())); 1346 } 1347 if (getIDTokenHint() != null) { 1348 params.put("id_token_hint", Collections.singletonList(getIDTokenHint().serialize())); 1349 } 1350 if (getLoginHint() != null) { 1351 params.put("login_hint", Collections.singletonList(getLoginHint())); 1352 } 1353 if (getBindingMessage() != null) { 1354 params.put("binding_message", Collections.singletonList(getBindingMessage())); 1355 } 1356 if (getUserCode() != null) { 1357 params.put("user_code", Collections.singletonList(getUserCode().getValue())); 1358 } 1359 if (getRequestedExpiry() != null) { 1360 params.put("requested_expiry", Collections.singletonList(getRequestedExpiry().toString())); 1361 } 1362 if (getOIDCClaims() != null) { 1363 params.put("claims", Collections.singletonList(getOIDCClaims().toJSONString())); 1364 } 1365 if (CollectionUtils.isNotEmpty(getClaimsLocales())) { 1366 params.put("claims_locales", Collections.singletonList(LangTagUtils.concat(getClaimsLocales()))); 1367 } 1368 if (getPurpose() != null) { 1369 params.put("purpose", Collections.singletonList(purpose)); 1370 } 1371 if (getAuthorizationDetails() != null) { 1372 params.put("authorization_details", Collections.singletonList(AuthorizationDetail.toJSONString(getAuthorizationDetails()))); 1373 } 1374 if (CollectionUtils.isNotEmpty(getResources())) { 1375 params.put("resource", URIUtils.toStringList(getResources(), true)); 1376 } 1377 1378 return params; 1379 } 1380 1381 1382 /** 1383 * Returns the parameters for this CIBA request as a JSON Web Token 1384 * (JWT) claims set. Intended for creating a signed CIBA request. 1385 * 1386 * @return The parameters as JWT claim set. 1387 */ 1388 public JWTClaimsSet toJWTClaimsSet() { 1389 1390 if (isSigned()) { 1391 throw new IllegalStateException(); 1392 } 1393 1394 return JWTClaimsSetUtils.toJWTClaimsSet(toParameters()); 1395 } 1396 1397 1398 /** 1399 * Returns the matching HTTP request. 1400 * 1401 * @return The HTTP request. 1402 */ 1403 @Override 1404 public HTTPRequest toHTTPRequest() { 1405 1406 if (getEndpointURI() == null) 1407 throw new SerializeException("The endpoint URI is not specified"); 1408 1409 HTTPRequest httpRequest = new HTTPRequest(HTTPRequest.Method.POST, getEndpointURI()); 1410 httpRequest.setEntityContentType(ContentType.APPLICATION_URLENCODED); 1411 1412 getClientAuthentication().applyTo(httpRequest); 1413 1414 Map<String, List<String>> params; 1415 try { 1416 params = new LinkedHashMap<>(httpRequest.getBodyAsFormParameters()); 1417 } catch (ParseException e) { 1418 throw new SerializeException(e.getMessage(), e); 1419 } 1420 params.putAll(toParameters()); 1421 httpRequest.setBody(URLUtils.serializeParameters(params)); 1422 1423 return httpRequest; 1424 } 1425 1426 1427 /** 1428 * Parses a CIBA request from the specified HTTP request. 1429 * 1430 * @param httpRequest The HTTP request. Must not be {@code null}. 1431 * 1432 * @return The CIBA request. 1433 * 1434 * @throws ParseException If parsing failed. 1435 */ 1436 public static CIBARequest parse(final HTTPRequest httpRequest) throws ParseException { 1437 1438 // Only HTTP POST accepted 1439 URI uri = httpRequest.getURI(); 1440 httpRequest.ensureMethod(HTTPRequest.Method.POST); 1441 httpRequest.ensureEntityContentType(ContentType.APPLICATION_URLENCODED); 1442 1443 ClientAuthentication clientAuth = ClientAuthentication.parse(httpRequest); 1444 1445 if (clientAuth == null) { 1446 throw new ParseException("Missing required client authentication"); 1447 } 1448 1449 Map<String, List<String>> params = httpRequest.getQueryParameters(); 1450 1451 String v; 1452 1453 if (params.containsKey("request")) { 1454 // Signed request 1455 v = MultivaluedMapUtils.getFirstValue(params, "request"); 1456 1457 if (StringUtils.isBlank(v)) { 1458 throw new ParseException("Empty request parameter"); 1459 } 1460 1461 SignedJWT signedRequest; 1462 try { 1463 signedRequest = SignedJWT.parse(v); 1464 } catch (java.text.ParseException e) { 1465 throw new ParseException("Invalid request JWT: " + e.getMessage(), e); 1466 } 1467 1468 try { 1469 return new CIBARequest(uri, clientAuth, signedRequest); 1470 } catch (IllegalArgumentException e) { 1471 throw new ParseException(e.getMessage(), e); 1472 } 1473 } 1474 1475 1476 // Plain request 1477 1478 // Parse required scope 1479 v = MultivaluedMapUtils.getFirstValue(params, "scope"); 1480 Scope scope = Scope.parse(v); 1481 1482 v = MultivaluedMapUtils.getFirstValue(params, "client_notification_token"); 1483 BearerAccessToken clientNotificationToken = null; 1484 if (StringUtils.isNotBlank(v)) { 1485 clientNotificationToken = new BearerAccessToken(v); 1486 } 1487 1488 v = MultivaluedMapUtils.getFirstValue(params, "acr_values"); 1489 List<ACR> acrValues = null; 1490 if (StringUtils.isNotBlank(v)) { 1491 acrValues = new LinkedList<>(); 1492 StringTokenizer st = new StringTokenizer(v, " "); 1493 while (st.hasMoreTokens()) { 1494 acrValues.add(new ACR(st.nextToken())); 1495 } 1496 } 1497 1498 String loginHintTokenString = MultivaluedMapUtils.getFirstValue(params, "login_hint_token"); 1499 1500 v = MultivaluedMapUtils.getFirstValue(params, "id_token_hint"); 1501 JWT idTokenHint = null; 1502 if (StringUtils.isNotBlank(v)) { 1503 try { 1504 idTokenHint = JWTParser.parse(v); 1505 } catch (java.text.ParseException e) { 1506 throw new ParseException("Invalid id_token_hint parameter: " + e.getMessage()); 1507 } 1508 } 1509 1510 String loginHint = MultivaluedMapUtils.getFirstValue(params, "login_hint"); 1511 1512 v = MultivaluedMapUtils.getFirstValue(params, "user_code"); 1513 1514 Secret userCode = null; 1515 if (StringUtils.isNotBlank(v)) { 1516 userCode = new Secret(v); 1517 } 1518 1519 String bindingMessage = MultivaluedMapUtils.getFirstValue(params, "binding_message"); 1520 1521 v = MultivaluedMapUtils.getFirstValue(params, "requested_expiry"); 1522 1523 Integer requestedExpiry = null; 1524 if (StringUtils.isNotBlank(v)) { 1525 try { 1526 requestedExpiry = Integer.valueOf(v); 1527 } catch (NumberFormatException e) { 1528 throw new ParseException("The requested_expiry parameter must be an integer"); 1529 } 1530 } 1531 1532 v = MultivaluedMapUtils.getFirstValue(params, "claims"); 1533 OIDCClaimsRequest claims = null; 1534 if (StringUtils.isNotBlank(v)) { 1535 try { 1536 claims = OIDCClaimsRequest.parse(v); 1537 } catch (ParseException e) { 1538 throw new ParseException("Invalid claims parameter: " + e.getMessage(), e); 1539 } 1540 } 1541 1542 1543 List<LangTag> claimsLocales; 1544 try { 1545 claimsLocales = LangTagUtils.parseLangTagList(MultivaluedMapUtils.getFirstValue(params, "claims_locales")); 1546 } catch (LangTagException e) { 1547 throw new ParseException("Invalid claims_locales parameter: " + e.getMessage(), e); 1548 } 1549 1550 String purpose = MultivaluedMapUtils.getFirstValue(params, "purpose"); 1551 1552 List<AuthorizationDetail> authorizationDetails = null; 1553 v = MultivaluedMapUtils.getFirstValue(params, "authorization_details"); 1554 if (StringUtils.isNotBlank(v)) { 1555 authorizationDetails = AuthorizationDetail.parseList(v); 1556 } 1557 1558 List<URI> resources = ResourceUtils.parseResourceURIs(params.get("resource")); 1559 1560 // Parse additional custom parameters 1561 Map<String,List<String>> customParams = null; 1562 1563 for (Map.Entry<String,List<String>> p: params.entrySet()) { 1564 1565 if (! REGISTERED_PARAMETER_NAMES.contains(p.getKey()) && ! clientAuth.getFormParameterNames().contains(p.getKey())) { 1566 // We have a custom parameter 1567 if (customParams == null) { 1568 customParams = new HashMap<>(); 1569 } 1570 customParams.put(p.getKey(), p.getValue()); 1571 } 1572 } 1573 1574 try { 1575 return new CIBARequest( 1576 uri, clientAuth, 1577 scope, clientNotificationToken, acrValues, 1578 loginHintTokenString, idTokenHint, loginHint, 1579 bindingMessage, userCode, requestedExpiry, 1580 claims, claimsLocales, purpose, authorizationDetails, 1581 resources, 1582 customParams); 1583 } catch (IllegalArgumentException e) { 1584 throw new ParseException(e.getMessage()); 1585 } 1586 } 1587}