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, section 7.1. 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 uri; 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 if (clientAuth == null) { 365 throw new IllegalArgumentException("The client authentication must not be null"); 366 } 367 this.clientAuth = clientAuth; 368 369 this.scope = scope; 370 371 signedRequest = null; 372 } 373 374 375 /** 376 * Creates a new CIBA signed request builder. 377 * 378 * @param clientAuth The client authentication. Must not be 379 * {@code null}. 380 * @param signedRequest The signed request JWT. Must not be 381 * {@code null}. 382 */ 383 public Builder(final ClientAuthentication clientAuth, 384 final SignedJWT signedRequest) { 385 386 if (clientAuth == null) { 387 throw new IllegalArgumentException("The client authentication must not be null"); 388 } 389 this.clientAuth = clientAuth; 390 391 if (signedRequest == null) { 392 throw new IllegalArgumentException("The signed request JWT must not be null"); 393 } 394 this.signedRequest = signedRequest; 395 396 scope = null; 397 } 398 399 400 /** 401 * Creates a new CIBA request builder from the specified 402 * request. 403 * 404 * @param request The CIBA request. Must not be {@code null}. 405 */ 406 public Builder(final CIBARequest request) { 407 408 uri = request.getEndpointURI(); 409 clientAuth = request.getClientAuthentication(); 410 scope = request.getScope(); 411 clientNotificationToken = request.getClientNotificationToken(); 412 acrValues = request.getACRValues(); 413 loginHintTokenString = request.getLoginHintTokenString(); 414 idTokenHint = request.getIDTokenHint(); 415 loginHint = request.getLoginHint(); 416 bindingMessage = request.getBindingMessage(); 417 userCode = request.getUserCode(); 418 requestedExpiry = request.getRequestedExpiry(); 419 claims = request.getOIDCClaims(); 420 claimsLocales = request.getClaimsLocales(); 421 purpose = request.getPurpose(); 422 authorizationDetails = request.getAuthorizationDetails(); 423 resources = request.getResources(); 424 customParams = request.getCustomParameters(); 425 signedRequest = request.getRequestJWT(); 426 } 427 428 429 /** 430 * Sets the client notification token, required for the CIBA 431 * ping and push token delivery modes. Corresponds to the 432 * {@code client_notification_token} parameter. 433 * 434 * @param token The client notification token, {@code null} if 435 * not specified. 436 * 437 * @return This builder. 438 */ 439 public Builder clientNotificationToken(final BearerAccessToken token) { 440 this.clientNotificationToken = token; 441 return this; 442 } 443 444 445 /** 446 * Sets the requested Authentication Context Class Reference 447 * values. Corresponds to the optional {@code acr_values} 448 * parameter. 449 * 450 * @param acrValues The requested ACR values, {@code null} if 451 * not specified. 452 * 453 * @return This builder. 454 */ 455 public Builder acrValues(final List<ACR> acrValues) { 456 this.acrValues = acrValues; 457 return this; 458 } 459 460 461 /** 462 * Sets the login hint token string, containing information 463 * identifying the end-user for whom authentication is being requested. 464 * Corresponds to the {@code login_hint_token} parameter. 465 * 466 * @param loginHintTokenString The login hint token string, 467 * {@code null} if not specified. 468 * 469 * @return This builder. 470 */ 471 public Builder loginHintTokenString(final String loginHintTokenString) { 472 this.loginHintTokenString = loginHintTokenString; 473 return this; 474 } 475 476 477 /** 478 * Sets the ID Token hint, passed as a hint to identify the 479 * end-user for whom authentication is being requested. 480 * Corresponds to the {@code id_token_hint} parameter. 481 * 482 * @param idTokenHint The ID Token hint, {@code null} if not 483 * specified. 484 * 485 * @return This builder. 486 */ 487 public Builder idTokenHint(final JWT idTokenHint) { 488 this.idTokenHint = idTokenHint; 489 return this; 490 } 491 492 493 /** 494 * Sets the login hint (email address, phone number, etc), 495 * about the end-user for whom authentication is being 496 * requested. Corresponds to the {@code login_hint} parameter. 497 * 498 * @param loginHint The login hint, {@code null} if not 499 * specified. 500 * 501 * @return This builder. 502 */ 503 public Builder loginHint(final String loginHint) { 504 this.loginHint = loginHint; 505 return this; 506 } 507 508 509 /** 510 * Sets the human readable binding message for the display at 511 * the consumption and authentication devices. Corresponds to 512 * the {@code binding_message} parameter. 513 * 514 * @param bindingMessage The binding message, {@code null} if 515 * not specified. 516 * 517 * @return This builder. 518 */ 519 public Builder bindingMessage(final String bindingMessage) { 520 this.bindingMessage = bindingMessage; 521 return this; 522 } 523 524 525 /** 526 * Gets the user secret code (password, PIN, etc) to authorise 527 * the CIBA request with the authentication device. Corresponds 528 * to the {@code user_code} parameter. 529 * 530 * @param userCode The user code, {@code null} if not 531 * specified. 532 * 533 * @return This builder. 534 */ 535 public Builder userCode(final Secret userCode) { 536 this.userCode = userCode; 537 return this; 538 } 539 540 541 /** 542 * Sets the requested expiration for the {@code auth_req_id}. 543 * Corresponds to the {@code requested_expiry} parameter. 544 * 545 * @param requestedExpiry The required expiry (as positive 546 * integer), {@code null} if not 547 * specified. 548 * 549 * @return This builder. 550 */ 551 public Builder requestedExpiry(final Integer requestedExpiry) { 552 this.requestedExpiry = requestedExpiry; 553 return this; 554 } 555 556 557 /** 558 * Sets the individual OpenID claims to be returned. 559 * Corresponds to the optional {@code claims} parameter. 560 * 561 * @param claims The individual OpenID claims to be returned, 562 * {@code null} if not specified. 563 * 564 * @return This builder. 565 */ 566 public Builder claims(final OIDCClaimsRequest claims) { 567 568 this.claims = claims; 569 return this; 570 } 571 572 573 /** 574 * Sets the end-user's preferred languages and scripts for the 575 * claims being returned, ordered by preference. Corresponds to 576 * the optional {@code claims_locales} parameter. 577 * 578 * @param claimsLocales The preferred claims locales, 579 * {@code null} if not specified. 580 * 581 * @return This builder. 582 */ 583 public Builder claimsLocales(final List<LangTag> claimsLocales) { 584 585 this.claimsLocales = claimsLocales; 586 return this; 587 } 588 589 590 /** 591 * Sets the transaction specific purpose. Corresponds to the 592 * optional {@code purpose} parameter. 593 * 594 * @param purpose The purpose, {@code null} if not specified. 595 * 596 * @return This builder. 597 */ 598 public Builder purpose(final String purpose) { 599 600 this.purpose = purpose; 601 return this; 602 } 603 604 605 /** 606 * Sets the Rich Authorisation Request (RAR) details. 607 * 608 * @param authorizationDetails The authorisation details, 609 * {@code null} if not specified. 610 * 611 * @return This builder. 612 */ 613 public Builder authorizationDetails(final List<AuthorizationDetail> authorizationDetails) { 614 this.authorizationDetails = authorizationDetails; 615 return this; 616 } 617 618 619 /** 620 * Sets the resource server URI. 621 * 622 * @param resource The resource URI, {@code null} if not 623 * specified. 624 * 625 * @return This builder. 626 */ 627 public Builder resource(final URI resource) { 628 if (resource != null) { 629 this.resources = Collections.singletonList(resource); 630 } else { 631 this.resources = null; 632 } 633 return this; 634 } 635 636 637 /** 638 * Sets the resource server URI(s). 639 * 640 * @param resources The resource URI(s), {@code null} if not 641 * specified. 642 * 643 * @return This builder. 644 */ 645 public Builder resources(final URI ... resources) { 646 if (resources != null) { 647 this.resources = Arrays.asList(resources); 648 } else { 649 this.resources = null; 650 } 651 return this; 652 } 653 654 655 /** 656 * Sets a custom parameter. 657 * 658 * @param name The parameter name. Must not be {@code null}. 659 * @param values The parameter values, {@code null} if not 660 * specified. 661 * 662 * @return This builder. 663 */ 664 public Builder customParameter(final String name, final String ... values) { 665 666 if (values == null || values.length == 0) { 667 customParams.remove(name); 668 } else { 669 customParams.put(name, Arrays.asList(values)); 670 } 671 672 return this; 673 } 674 675 676 /** 677 * Sets the URI of the endpoint (HTTP or HTTPS) for which the 678 * request is intended. 679 * 680 * @param uri The endpoint URI, {@code null} if not specified. 681 * 682 * @return This builder. 683 */ 684 public Builder endpointURI(final URI uri) { 685 686 this.uri = uri; 687 return this; 688 } 689 690 691 /** 692 * Builds a new CIBA request. 693 * 694 * @return The CIBA request. 695 */ 696 public CIBARequest build() { 697 698 try { 699 if (signedRequest != null) { 700 return new CIBARequest( 701 uri, 702 clientAuth, 703 signedRequest 704 ); 705 } 706 707 // Plain request 708 return new CIBARequest( 709 uri, 710 clientAuth, 711 scope, 712 clientNotificationToken, 713 acrValues, 714 loginHintTokenString, 715 idTokenHint, 716 loginHint, 717 bindingMessage, 718 userCode, 719 requestedExpiry, 720 claims, 721 claimsLocales, 722 purpose, 723 authorizationDetails, 724 resources, 725 customParams 726 ); 727 } catch (IllegalArgumentException e) { 728 throw new IllegalArgumentException(e.getMessage(), e); 729 } 730 } 731 } 732 733 734 /** 735 * Creates a new CIBA request. 736 * 737 * @param uri The endpoint URI, {@code null} if not 738 * specified. 739 * @param clientAuth The client authentication. Must not 740 * be {@code null}. 741 * @param scope The requested scope. Must not be 742 * empty or {@code null}. 743 * @param clientNotificationToken The client notification token, 744 * {@code null} if not specified. 745 * @param acrValues The requested ACR values, 746 * {@code null} if not specified. 747 * @param loginHintTokenString The login hint token string, 748 * {@code null} if not specified. 749 * @param idTokenHint The ID Token hint, {@code null} if 750 * not specified. 751 * @param loginHint The login hint, {@code null} if not 752 * specified. 753 * @param bindingMessage The binding message, {@code null} if 754 * not specified. 755 * @param userCode The user code, {@code null} if not 756 * specified. 757 * @param requestedExpiry The required expiry (as positive 758 * integer), {@code null} if not 759 * specified. 760 * @param customParams Custom parameters, empty or 761 * {@code null} if not specified. 762 */ 763 @Deprecated 764 public CIBARequest(final URI uri, 765 final ClientAuthentication clientAuth, 766 final Scope scope, 767 final BearerAccessToken clientNotificationToken, 768 final List<ACR> acrValues, 769 final String loginHintTokenString, 770 final JWT idTokenHint, 771 final String loginHint, 772 final String bindingMessage, 773 final Secret userCode, 774 final Integer requestedExpiry, 775 final Map<String, List<String>> customParams) { 776 777 this(uri, clientAuth, 778 scope, clientNotificationToken, acrValues, 779 loginHintTokenString, idTokenHint, loginHint, 780 bindingMessage, userCode, requestedExpiry, 781 null, customParams); 782 } 783 784 785 /** 786 * Creates a new CIBA request. 787 * 788 * @param uri The endpoint URI, {@code null} if not 789 * specified. 790 * @param clientAuth The client authentication. Must not 791 * be {@code null}. 792 * @param scope The requested scope. Must not be 793 * empty or {@code null}. 794 * @param clientNotificationToken The client notification token, 795 * {@code null} if not specified. 796 * @param acrValues The requested ACR values, 797 * {@code null} if not specified. 798 * @param loginHintTokenString The login hint token string, 799 * {@code null} if not specified. 800 * @param idTokenHint The ID Token hint, {@code null} if 801 * not specified. 802 * @param loginHint The login hint, {@code null} if not 803 * specified. 804 * @param bindingMessage The binding message, {@code null} if 805 * not specified. 806 * @param userCode The user code, {@code null} if not 807 * specified. 808 * @param requestedExpiry The required expiry (as positive 809 * integer), {@code null} if not 810 * specified. 811 * @param claims The individual claims to be returned, 812 * {@code null} if not specified. 813 * @param customParams Custom parameters, empty or 814 * {@code null} if not specified. 815 */ 816 @Deprecated 817 public CIBARequest(final URI uri, 818 final ClientAuthentication clientAuth, 819 final Scope scope, 820 final BearerAccessToken clientNotificationToken, 821 final List<ACR> acrValues, 822 final String loginHintTokenString, 823 final JWT idTokenHint, 824 final String loginHint, 825 final String bindingMessage, 826 final Secret userCode, 827 final Integer requestedExpiry, 828 final OIDCClaimsRequest claims, 829 final Map<String, List<String>> customParams) { 830 831 this(uri, clientAuth, 832 scope, clientNotificationToken, acrValues, 833 loginHintTokenString, idTokenHint, loginHint, 834 bindingMessage, userCode, requestedExpiry, 835 claims, null, null, 836 null, 837 customParams); 838 } 839 840 841 /** 842 * Creates a new CIBA request. 843 * 844 * @param uri The endpoint URI, {@code null} if not 845 * specified. 846 * @param clientAuth The client authentication. Must not 847 * be {@code null}. 848 * @param scope The requested scope. Must not be 849 * empty or {@code null}. 850 * @param clientNotificationToken The client notification token, 851 * {@code null} if not specified. 852 * @param acrValues The requested ACR values, 853 * {@code null} if not specified. 854 * @param loginHintTokenString The login hint token string, 855 * {@code null} if not specified. 856 * @param idTokenHint The ID Token hint, {@code null} if 857 * not specified. 858 * @param loginHint The login hint, {@code null} if not 859 * specified. 860 * @param bindingMessage The binding message, {@code null} if 861 * not specified. 862 * @param userCode The user code, {@code null} if not 863 * specified. 864 * @param requestedExpiry The required expiry (as positive 865 * integer), {@code null} if not 866 * specified. 867 * @param claims The individual claims to be 868 * returned, {@code null} if not 869 * specified. 870 * @param claimsLocales The preferred languages and scripts 871 * for claims being returned, 872 * {@code null} if not specified. 873 * @param purpose The transaction specific purpose, 874 * {@code null} if not specified. 875 * @param resources The resource URI(s), {@code null} if 876 * not specified. 877 * @param customParams Custom parameters, empty or 878 * {@code null} if not specified. 879 */ 880 @Deprecated 881 public CIBARequest(final URI uri, 882 final ClientAuthentication clientAuth, 883 final Scope scope, 884 final BearerAccessToken clientNotificationToken, 885 final List<ACR> acrValues, 886 final String loginHintTokenString, 887 final JWT idTokenHint, 888 final String loginHint, 889 final String bindingMessage, 890 final Secret userCode, 891 final Integer requestedExpiry, 892 final OIDCClaimsRequest claims, 893 final List<LangTag> claimsLocales, 894 final String purpose, 895 final List<URI> resources, 896 final Map<String, List<String>> customParams) { 897 898 this(uri, clientAuth, 899 scope, clientNotificationToken, acrValues, 900 loginHintTokenString, idTokenHint, loginHint, 901 bindingMessage, userCode, requestedExpiry, 902 claims, claimsLocales, purpose, null ,resources, 903 customParams); 904 } 905 906 907 /** 908 * Creates a new CIBA request. 909 * 910 * @param uri The endpoint URI, {@code null} if not 911 * specified. 912 * @param clientAuth The client authentication. Must not 913 * be {@code null}. 914 * @param scope The requested scope. Must not be 915 * empty or {@code null}. 916 * @param clientNotificationToken The client notification token, 917 * {@code null} if not specified. 918 * @param acrValues The requested ACR values, 919 * {@code null} if not specified. 920 * @param loginHintTokenString The login hint token string, 921 * {@code null} if not specified. 922 * @param idTokenHint The ID Token hint, {@code null} if 923 * not specified. 924 * @param loginHint The login hint, {@code null} if not 925 * specified. 926 * @param bindingMessage The binding message, {@code null} if 927 * not specified. 928 * @param userCode The user code, {@code null} if not 929 * specified. 930 * @param requestedExpiry The required expiry (as positive 931 * integer), {@code null} if not 932 * specified. 933 * @param claims The individual claims to be 934 * returned, {@code null} if not 935 * specified. 936 * @param claimsLocales The preferred languages and scripts 937 * for claims being returned, 938 * {@code null} if not specified. 939 * @param purpose The transaction specific purpose, 940 * {@code null} if not specified. 941 * @param authorizationDetails The Rich Authorisation Request (RAR) 942 * details, {@code null} if not 943 * specified. 944 * @param resources The resource URI(s), {@code null} if 945 * not specified. 946 * @param customParams Custom parameters, empty or 947 * {@code null} if not specified. 948 */ 949 public CIBARequest(final URI uri, 950 final ClientAuthentication clientAuth, 951 final Scope scope, 952 final BearerAccessToken clientNotificationToken, 953 final List<ACR> acrValues, 954 final String loginHintTokenString, 955 final JWT idTokenHint, 956 final String loginHint, 957 final String bindingMessage, 958 final Secret userCode, 959 final Integer requestedExpiry, 960 final OIDCClaimsRequest claims, 961 final List<LangTag> claimsLocales, 962 final String purpose, 963 final List<AuthorizationDetail> authorizationDetails, 964 final List<URI> resources, 965 final Map<String, List<String>> customParams) { 966 967 super(uri, clientAuth); 968 969 this.scope = scope; 970 971 if (clientNotificationToken != null && clientNotificationToken.getValue().length() > CLIENT_NOTIFICATION_TOKEN_MAX_LENGTH) { 972 throw new IllegalArgumentException("The client notification token must not exceed " + CLIENT_NOTIFICATION_TOKEN_MAX_LENGTH + " chars"); 973 } 974 this.clientNotificationToken = clientNotificationToken; 975 976 this.acrValues = acrValues; 977 978 // https://openid.net/specs/openid-client-initiated-backchannel-authentication-core-1_0-03.html#rfc.section.7.1 979 // As in the CIBA flow the OP does not have an interaction with 980 // the end-user through the consumption device, it is REQUIRED 981 // that the Client provides one (and only one) of the hints 982 // specified above in the authentication request, that is 983 // "login_hint_token", "id_token_hint" or "login_hint". 984 int numHints = 0; 985 986 if (loginHintTokenString != null) numHints++; 987 this.loginHintTokenString = loginHintTokenString; 988 989 if (idTokenHint != null) numHints++; 990 this.idTokenHint = idTokenHint; 991 992 if (loginHint != null) numHints++; 993 this.loginHint = loginHint; 994 995 if (numHints != 1) { 996 throw new IllegalArgumentException("One user identity hist must be provided (login_hint_token, id_token_hint or login_hint)"); 997 } 998 999 this.bindingMessage = bindingMessage; 1000 1001 this.userCode = userCode; 1002 1003 if (requestedExpiry != null && requestedExpiry < 1) { 1004 throw new IllegalArgumentException("The requested expiry must be a positive integer"); 1005 } 1006 this.requestedExpiry = requestedExpiry; 1007 1008 this.claims = claims; 1009 1010 if (claimsLocales != null) { 1011 this.claimsLocales = Collections.unmodifiableList(claimsLocales); 1012 } else { 1013 this.claimsLocales = null; 1014 } 1015 1016 this.purpose = purpose; 1017 1018 this.authorizationDetails = authorizationDetails; 1019 1020 this.resources = ResourceUtils.ensureLegalResourceURIs(resources); 1021 1022 this.customParams = customParams != null ? customParams : Collections.<String, List<String>>emptyMap(); 1023 1024 signedRequest = null; 1025 } 1026 1027 1028 /** 1029 * Creates a new CIBA signed request. 1030 * 1031 * @param uri The endpoint URI, {@code null} if not 1032 * specified. 1033 * @param clientAuth The client authentication. Must not be 1034 * {@code null}. 1035 * @param signedRequest The signed request JWT. Must not be 1036 * {@code null}. 1037 */ 1038 public CIBARequest(final URI uri, 1039 final ClientAuthentication clientAuth, 1040 final SignedJWT signedRequest) { 1041 1042 super(uri, clientAuth); 1043 1044 if (signedRequest == null) { 1045 throw new IllegalArgumentException("The signed request JWT must not be null"); 1046 } 1047 if (JWSObject.State.UNSIGNED.equals(signedRequest.getState())) { 1048 throw new IllegalArgumentException("The request JWT must be in a signed state"); 1049 } 1050 this.signedRequest = signedRequest; 1051 1052 scope = null; 1053 clientNotificationToken = null; 1054 acrValues = null; 1055 loginHintTokenString = null; 1056 idTokenHint = null; 1057 loginHint = null; 1058 bindingMessage = null; 1059 userCode = null; 1060 requestedExpiry = null; 1061 claims = null; 1062 claimsLocales = null; 1063 authorizationDetails = null; 1064 purpose = null; 1065 resources = null; 1066 customParams = Collections.emptyMap(); 1067 } 1068 1069 1070 /** 1071 * Returns the registered (standard) CIBA request parameter names. 1072 * 1073 * @return The registered CIBA request parameter names, as an 1074 * unmodifiable set. 1075 */ 1076 public static Set<String> getRegisteredParameterNames() { 1077 1078 return REGISTERED_PARAMETER_NAMES; 1079 } 1080 1081 1082 /** 1083 * Returns the scope. Corresponds to the optional {@code scope} 1084 * parameter. 1085 * 1086 * @return The scope, {@code null} if not specified. 1087 */ 1088 public Scope getScope() { 1089 1090 return scope; 1091 } 1092 1093 1094 /** 1095 * Returns the client notification token, required for the CIBA ping 1096 * and push token delivery modes. Corresponds to the 1097 * {@code client_notification_token} parameter. 1098 * 1099 * @return The client notification token, {@code null} if not 1100 * specified. 1101 */ 1102 public BearerAccessToken getClientNotificationToken() { 1103 1104 return clientNotificationToken; 1105 } 1106 1107 1108 /** 1109 * Returns the requested Authentication Context Class Reference values. 1110 * Corresponds to the optional {@code acr_values} parameter. 1111 * 1112 * @return The requested ACR values, {@code null} if not specified. 1113 */ 1114 public List<ACR> getACRValues() { 1115 1116 return acrValues; 1117 } 1118 1119 1120 /** 1121 * Returns the hint type. 1122 * 1123 * @return The hint type. 1124 */ 1125 public CIBAHintType getHintType() { 1126 1127 if (getLoginHintTokenString() != null) { 1128 return CIBAHintType.LOGIN_HINT_TOKEN; 1129 } else if (getIDTokenHint() != null) { 1130 return CIBAHintType.ID_TOKEN_HINT; 1131 } else { 1132 return CIBAHintType.LOGIN_HINT; 1133 } 1134 } 1135 1136 1137 /** 1138 * Returns the login hint token string, containing information 1139 * identifying the end-user for whom authentication is being requested. 1140 * Corresponds to the {@code login_hint_token} parameter. 1141 * 1142 * @return The login hint token string, {@code null} if not 1143 * specified. 1144 */ 1145 public String getLoginHintTokenString() { 1146 1147 return loginHintTokenString; 1148 } 1149 1150 1151 /** 1152 * Returns the ID Token hint, passed as a hint to identify the end-user 1153 * for whom authentication is being requested. Corresponds to the 1154 * {@code id_token_hint} parameter. 1155 * 1156 * @return The ID Token hint, {@code null} if not specified. 1157 */ 1158 public JWT getIDTokenHint() { 1159 1160 return idTokenHint; 1161 } 1162 1163 1164 /** 1165 * Returns the login hint (email address, phone number, etc), about the 1166 * end-user for whom authentication is being requested. Corresponds to 1167 * the {@code login_hint} parameter. 1168 * 1169 * @return The login hint, {@code null} if not specified. 1170 */ 1171 public String getLoginHint() { 1172 1173 return loginHint; 1174 } 1175 1176 1177 /** 1178 * Returns the human-readable binding message for the display at the 1179 * consumption and authentication devices. Corresponds to the 1180 * {@code binding_message} parameter. 1181 * 1182 * @return The binding message, {@code null} if not specified. 1183 */ 1184 public String getBindingMessage() { 1185 1186 return bindingMessage; 1187 } 1188 1189 1190 /** 1191 * Returns the user secret code (password, PIN, etc) to authorise the 1192 * CIBA request with the authentication device. Corresponds to the 1193 * {@code user_code} parameter. 1194 * 1195 * @return The user code, {@code null} if not specified. 1196 */ 1197 public Secret getUserCode() { 1198 1199 return userCode; 1200 } 1201 1202 1203 /** 1204 * Returns the requested expiration for the {@code auth_req_id}. 1205 * Corresponds to the {@code requested_expiry} parameter. 1206 * 1207 * @return The required expiry (as positive integer), {@code null} if 1208 * not specified. 1209 */ 1210 public Integer getRequestedExpiry() { 1211 1212 return requestedExpiry; 1213 } 1214 1215 1216 /** 1217 * Returns the individual claims to be returned. Corresponds to the 1218 * optional {@code claims} parameter. 1219 * 1220 * @return The individual claims to be returned, {@code null} if not 1221 * specified. 1222 */ 1223 public OIDCClaimsRequest getOIDCClaims() { 1224 1225 return claims; 1226 } 1227 1228 1229 /** 1230 * Returns the end-user's preferred languages and scripts for the 1231 * claims being returned, ordered by preference. Corresponds to the 1232 * optional {@code claims_locales} parameter. 1233 * 1234 * @return The preferred claims locales, {@code null} if not specified. 1235 */ 1236 public List<LangTag> getClaimsLocales() { 1237 1238 return claimsLocales; 1239 } 1240 1241 1242 /** 1243 * Returns the transaction specific purpose. Corresponds to the 1244 * optional {@code purpose} parameter. 1245 * 1246 * @return The purpose, {@code null} if not specified. 1247 */ 1248 public String getPurpose() { 1249 1250 return purpose; 1251 } 1252 1253 1254 /** 1255 * Returns the Rich Authorisation Request (RAR) details. 1256 * 1257 * @return The authorisation details, {@code null} if not specified. 1258 */ 1259 public List<AuthorizationDetail> getAuthorizationDetails() { 1260 1261 return authorizationDetails; 1262 } 1263 1264 1265 /** 1266 * Returns the resource server URI. 1267 * 1268 * @return The resource URI(s), {@code null} if not specified. 1269 */ 1270 public List<URI> getResources() { 1271 1272 return resources; 1273 } 1274 1275 1276 /** 1277 * Returns the additional custom parameters. 1278 * 1279 * @return The additional custom parameters as an unmodifiable map, 1280 * empty map if none. 1281 */ 1282 public Map<String, List<String>> getCustomParameters() { 1283 1284 return customParams; 1285 } 1286 1287 1288 /** 1289 * Returns the specified custom parameter. 1290 * 1291 * @param name The parameter name. Must not be {@code null}. 1292 * 1293 * @return The parameter value(s), {@code null} if not specified. 1294 */ 1295 public List<String> getCustomParameter(final String name) { 1296 1297 return customParams.get(name); 1298 } 1299 1300 1301 /** 1302 * Returns {@code true} if this request is signed. 1303 * 1304 * @return {@code true} for a signed request, {@code false} for a plain 1305 * request. 1306 */ 1307 public boolean isSigned() { 1308 1309 return signedRequest != null; 1310 } 1311 1312 1313 /** 1314 * Returns the JWT for a signed request. 1315 * 1316 * @return The request JWT. 1317 */ 1318 public SignedJWT getRequestJWT() { 1319 1320 return signedRequest; 1321 } 1322 1323 1324 /** 1325 * Returns the for parameters for this CIBA request. Parameters which 1326 * are part of the client authentication are not included. 1327 * 1328 * @return The parameters. 1329 */ 1330 public Map<String, List<String>> toParameters() { 1331 1332 // Put custom params first, so they may be overwritten by std params 1333 Map<String, List<String>> params = new LinkedHashMap<>(getCustomParameters()); 1334 1335 if (isSigned()) { 1336 params.put("request", Collections.singletonList(signedRequest.serialize())); 1337 return params; 1338 } 1339 1340 if (CollectionUtils.isNotEmpty(getScope())) { 1341 params.put("scope", Collections.singletonList(getScope().toString())); 1342 } 1343 1344 if (getClientNotificationToken() != null) { 1345 params.put("client_notification_token", Collections.singletonList(getClientNotificationToken().getValue())); 1346 } 1347 if (getACRValues() != null) { 1348 params.put("acr_values", Identifier.toStringList(getACRValues())); 1349 } 1350 if (getLoginHintTokenString() != null) { 1351 params.put("login_hint_token", Collections.singletonList(getLoginHintTokenString())); 1352 } 1353 if (getIDTokenHint() != null) { 1354 params.put("id_token_hint", Collections.singletonList(getIDTokenHint().serialize())); 1355 } 1356 if (getLoginHint() != null) { 1357 params.put("login_hint", Collections.singletonList(getLoginHint())); 1358 } 1359 if (getBindingMessage() != null) { 1360 params.put("binding_message", Collections.singletonList(getBindingMessage())); 1361 } 1362 if (getUserCode() != null) { 1363 params.put("user_code", Collections.singletonList(getUserCode().getValue())); 1364 } 1365 if (getRequestedExpiry() != null) { 1366 params.put("requested_expiry", Collections.singletonList(getRequestedExpiry().toString())); 1367 } 1368 if (getOIDCClaims() != null) { 1369 params.put("claims", Collections.singletonList(getOIDCClaims().toJSONString())); 1370 } 1371 if (CollectionUtils.isNotEmpty(getClaimsLocales())) { 1372 params.put("claims_locales", Collections.singletonList(LangTagUtils.concat(getClaimsLocales()))); 1373 } 1374 if (getPurpose() != null) { 1375 params.put("purpose", Collections.singletonList(purpose)); 1376 } 1377 if (getAuthorizationDetails() != null) { 1378 params.put("authorization_details", Collections.singletonList(AuthorizationDetail.toJSONString(getAuthorizationDetails()))); 1379 } 1380 if (CollectionUtils.isNotEmpty(getResources())) { 1381 params.put("resource", URIUtils.toStringList(getResources(), true)); 1382 } 1383 1384 return params; 1385 } 1386 1387 1388 /** 1389 * Returns the parameters for this CIBA request as a JSON Web Token 1390 * (JWT) claims set. Intended for creating a signed CIBA request. 1391 * 1392 * @return The parameters as JWT claim set. 1393 */ 1394 public JWTClaimsSet toJWTClaimsSet() { 1395 1396 if (isSigned()) { 1397 throw new IllegalStateException(); 1398 } 1399 1400 return JWTClaimsSetUtils.toJWTClaimsSet(toParameters()); 1401 } 1402 1403 1404 /** 1405 * Returns the matching HTTP request. 1406 * 1407 * @return The HTTP request. 1408 */ 1409 @Override 1410 public HTTPRequest toHTTPRequest() { 1411 1412 if (getEndpointURI() == null) 1413 throw new SerializeException("The endpoint URI is not specified"); 1414 1415 HTTPRequest httpRequest = new HTTPRequest(HTTPRequest.Method.POST, getEndpointURI()); 1416 httpRequest.setEntityContentType(ContentType.APPLICATION_URLENCODED); 1417 1418 getClientAuthentication().applyTo(httpRequest); 1419 1420 Map<String, List<String>> params; 1421 try { 1422 params = new LinkedHashMap<>(httpRequest.getBodyAsFormParameters()); 1423 } catch (ParseException e) { 1424 throw new SerializeException(e.getMessage(), e); 1425 } 1426 params.putAll(toParameters()); 1427 httpRequest.setBody(URLUtils.serializeParameters(params)); 1428 1429 return httpRequest; 1430 } 1431 1432 1433 /** 1434 * Parses a CIBA request from the specified HTTP request. 1435 * 1436 * @param httpRequest The HTTP request. Must not be {@code null}. 1437 * 1438 * @return The CIBA request. 1439 * 1440 * @throws ParseException If parsing failed. 1441 */ 1442 public static CIBARequest parse(final HTTPRequest httpRequest) throws ParseException { 1443 1444 // Only HTTP POST accepted 1445 URI uri = httpRequest.getURI(); 1446 httpRequest.ensureMethod(HTTPRequest.Method.POST); 1447 httpRequest.ensureEntityContentType(ContentType.APPLICATION_URLENCODED); 1448 1449 ClientAuthentication clientAuth = ClientAuthentication.parse(httpRequest); 1450 1451 if (clientAuth == null) { 1452 throw new ParseException("Missing required client authentication"); 1453 } 1454 1455 Map<String, List<String>> params = httpRequest.getQueryParameters(); 1456 1457 String v; 1458 1459 if (params.containsKey("request")) { 1460 // Signed request 1461 v = MultivaluedMapUtils.getFirstValue(params, "request"); 1462 1463 if (StringUtils.isBlank(v)) { 1464 throw new ParseException("Empty request parameter"); 1465 } 1466 1467 SignedJWT signedRequest; 1468 try { 1469 signedRequest = SignedJWT.parse(v); 1470 } catch (java.text.ParseException e) { 1471 throw new ParseException("Invalid request JWT: " + e.getMessage(), e); 1472 } 1473 1474 try { 1475 return new CIBARequest(uri, clientAuth, signedRequest); 1476 } catch (IllegalArgumentException e) { 1477 throw new ParseException(e.getMessage(), e); 1478 } 1479 } 1480 1481 1482 // Plain request 1483 1484 // Parse required scope 1485 v = MultivaluedMapUtils.getFirstValue(params, "scope"); 1486 Scope scope = Scope.parse(v); 1487 1488 v = MultivaluedMapUtils.getFirstValue(params, "client_notification_token"); 1489 BearerAccessToken clientNotificationToken = null; 1490 if (StringUtils.isNotBlank(v)) { 1491 clientNotificationToken = new BearerAccessToken(v); 1492 } 1493 1494 v = MultivaluedMapUtils.getFirstValue(params, "acr_values"); 1495 List<ACR> acrValues = null; 1496 if (StringUtils.isNotBlank(v)) { 1497 acrValues = new LinkedList<>(); 1498 StringTokenizer st = new StringTokenizer(v, " "); 1499 while (st.hasMoreTokens()) { 1500 acrValues.add(new ACR(st.nextToken())); 1501 } 1502 } 1503 1504 String loginHintTokenString = MultivaluedMapUtils.getFirstValue(params, "login_hint_token"); 1505 1506 v = MultivaluedMapUtils.getFirstValue(params, "id_token_hint"); 1507 JWT idTokenHint = null; 1508 if (StringUtils.isNotBlank(v)) { 1509 try { 1510 idTokenHint = JWTParser.parse(v); 1511 } catch (java.text.ParseException e) { 1512 throw new ParseException("Invalid id_token_hint parameter: " + e.getMessage()); 1513 } 1514 } 1515 1516 String loginHint = MultivaluedMapUtils.getFirstValue(params, "login_hint"); 1517 1518 v = MultivaluedMapUtils.getFirstValue(params, "user_code"); 1519 1520 Secret userCode = null; 1521 if (StringUtils.isNotBlank(v)) { 1522 userCode = new Secret(v); 1523 } 1524 1525 String bindingMessage = MultivaluedMapUtils.getFirstValue(params, "binding_message"); 1526 1527 v = MultivaluedMapUtils.getFirstValue(params, "requested_expiry"); 1528 1529 Integer requestedExpiry = null; 1530 if (StringUtils.isNotBlank(v)) { 1531 try { 1532 requestedExpiry = Integer.valueOf(v); 1533 } catch (NumberFormatException e) { 1534 throw new ParseException("The requested_expiry parameter must be an integer"); 1535 } 1536 } 1537 1538 v = MultivaluedMapUtils.getFirstValue(params, "claims"); 1539 OIDCClaimsRequest claims = null; 1540 if (StringUtils.isNotBlank(v)) { 1541 try { 1542 claims = OIDCClaimsRequest.parse(v); 1543 } catch (ParseException e) { 1544 throw new ParseException("Invalid claims parameter: " + e.getMessage(), e); 1545 } 1546 } 1547 1548 1549 List<LangTag> claimsLocales; 1550 try { 1551 claimsLocales = LangTagUtils.parseLangTagList(MultivaluedMapUtils.getFirstValue(params, "claims_locales")); 1552 } catch (LangTagException e) { 1553 throw new ParseException("Invalid claims_locales parameter: " + e.getMessage(), e); 1554 } 1555 1556 String purpose = MultivaluedMapUtils.getFirstValue(params, "purpose"); 1557 1558 List<AuthorizationDetail> authorizationDetails = null; 1559 v = MultivaluedMapUtils.getFirstValue(params, "authorization_details"); 1560 if (StringUtils.isNotBlank(v)) { 1561 authorizationDetails = AuthorizationDetail.parseList(v); 1562 } 1563 1564 List<URI> resources = ResourceUtils.parseResourceURIs(params.get("resource")); 1565 1566 // Parse additional custom parameters 1567 Map<String,List<String>> customParams = null; 1568 1569 for (Map.Entry<String,List<String>> p: params.entrySet()) { 1570 1571 if (! REGISTERED_PARAMETER_NAMES.contains(p.getKey()) && ! clientAuth.getFormParameterNames().contains(p.getKey())) { 1572 // We have a custom parameter 1573 if (customParams == null) { 1574 customParams = new HashMap<>(); 1575 } 1576 customParams.put(p.getKey(), p.getValue()); 1577 } 1578 } 1579 1580 try { 1581 return new CIBARequest( 1582 uri, clientAuth, 1583 scope, clientNotificationToken, acrValues, 1584 loginHintTokenString, idTokenHint, loginHint, 1585 bindingMessage, userCode, requestedExpiry, 1586 claims, claimsLocales, purpose, authorizationDetails, 1587 resources, 1588 customParams); 1589 } catch (IllegalArgumentException e) { 1590 throw new ParseException(e.getMessage()); 1591 } 1592 } 1593}