001/* 002 * oauth2-oidc-sdk 003 * 004 * Copyright 2012-2016, Connect2id Ltd and contributors. 005 * 006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use 007 * this file except in compliance with the License. You may obtain a copy of the 008 * License at 009 * 010 * http://www.apache.org/licenses/LICENSE-2.0 011 * 012 * Unless required by applicable law or agreed to in writing, software distributed 013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR 014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the 015 * specific language governing permissions and limitations under the License. 016 */ 017 018package com.nimbusds.openid.connect.sdk; 019 020 021import java.net.URI; 022import java.net.URISyntaxException; 023import java.util.*; 024 025import com.nimbusds.jwt.JWT; 026import com.nimbusds.jwt.JWTParser; 027import com.nimbusds.langtag.LangTag; 028import com.nimbusds.langtag.LangTagException; 029import com.nimbusds.oauth2.sdk.*; 030import com.nimbusds.oauth2.sdk.http.HTTPRequest; 031import com.nimbusds.oauth2.sdk.id.ClientID; 032import com.nimbusds.oauth2.sdk.id.State; 033import com.nimbusds.oauth2.sdk.pkce.CodeChallenge; 034import com.nimbusds.oauth2.sdk.pkce.CodeChallengeMethod; 035import com.nimbusds.oauth2.sdk.pkce.CodeVerifier; 036import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 037import com.nimbusds.oauth2.sdk.util.StringUtils; 038import com.nimbusds.oauth2.sdk.util.URIUtils; 039import com.nimbusds.oauth2.sdk.util.URLUtils; 040import com.nimbusds.openid.connect.sdk.claims.ACR; 041import net.jcip.annotations.Immutable; 042import net.minidev.json.JSONObject; 043 044 045/** 046 * OpenID Connect authentication request. Intended to authenticate an end-user 047 * and request the end-user's authorisation to release information to the 048 * client. Supports custom request parameters. 049 * 050 * <p>Example HTTP request (code flow): 051 * 052 * <pre> 053 * https://server.example.com/op/authorize? 054 * response_type=code%20id_token 055 * &client_id=s6BhdRkqt3 056 * &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb 057 * &scope=openid 058 * &nonce=n-0S6_WzA2Mj 059 * &state=af0ifjsldkj 060 * </pre> 061 * 062 * <p>Related specifications: 063 * 064 * <ul> 065 * <li>OpenID Connect Core 1.0, section 3.1.2.1. 066 * <li>Proof Key for Code Exchange by OAuth Public Clients (RFC 7636). 067 * </ul> 068 */ 069@Immutable 070public class AuthenticationRequest extends AuthorizationRequest { 071 072 073 /** 074 * The registered parameter names. 075 */ 076 private static final Set<String> REGISTERED_PARAMETER_NAMES; 077 078 079 /** 080 * Initialises the registered parameter name set. 081 */ 082 static { 083 Set<String> p = new HashSet<>(); 084 085 p.addAll(AuthorizationRequest.getRegisteredParameterNames()); 086 087 p.add("nonce"); 088 p.add("display"); 089 p.add("prompt"); 090 p.add("max_age"); 091 p.add("ui_locales"); 092 p.add("claims_locales"); 093 p.add("id_token_hint"); 094 p.add("login_hint"); 095 p.add("acr_values"); 096 p.add("claims"); 097 p.add("request_uri"); 098 p.add("request"); 099 100 REGISTERED_PARAMETER_NAMES = Collections.unmodifiableSet(p); 101 } 102 103 104 /** 105 * The nonce (required for implicit flow, optional for code flow). 106 */ 107 private final Nonce nonce; 108 109 110 /** 111 * The requested display type (optional). 112 */ 113 private final Display display; 114 115 116 /** 117 * The requested prompt (optional). 118 */ 119 private final Prompt prompt; 120 121 122 /** 123 * The required maximum authentication age, in seconds, -1 if not 124 * specified, zero implies prompt=login (optional). 125 */ 126 private final int maxAge; 127 128 129 /** 130 * The end-user's preferred languages and scripts for the user 131 * interface (optional). 132 */ 133 private final List<LangTag> uiLocales; 134 135 136 /** 137 * The end-user's preferred languages and scripts for claims being 138 * returned (optional). 139 */ 140 private final List<LangTag> claimsLocales; 141 142 143 /** 144 * Previously issued ID Token passed to the authorisation server as a 145 * hint about the end-user's current or past authenticated session with 146 * the client (optional). Should be present when {@code prompt=none} is 147 * used. 148 */ 149 private final JWT idTokenHint; 150 151 152 /** 153 * Hint to the authorisation server about the login identifier the 154 * end-user may use to log in (optional). 155 */ 156 private final String loginHint; 157 158 159 /** 160 * Requested Authentication Context Class Reference values (optional). 161 */ 162 private final List<ACR> acrValues; 163 164 165 /** 166 * Individual claims to be returned (optional). 167 */ 168 private final ClaimsRequest claims; 169 170 171 /** 172 * Request object (optional). 173 */ 174 private final JWT requestObject; 175 176 177 /** 178 * Request object URI (optional). 179 */ 180 private final URI requestURI; 181 182 183 /** 184 * Builder for constructing OpenID Connect authentication requests. 185 */ 186 public static class Builder { 187 188 189 /** 190 * The endpoint URI (optional). 191 */ 192 private URI uri; 193 194 195 /** 196 * The response type (required). 197 */ 198 private final ResponseType rt; 199 200 201 /** 202 * The client identifier (required). 203 */ 204 private final ClientID clientID; 205 206 207 /** 208 * The redirection URI where the response will be sent 209 * (required). 210 */ 211 private final URI redirectURI; 212 213 214 /** 215 * The scope (required). 216 */ 217 private final Scope scope; 218 219 220 /** 221 * The opaque value to maintain state between the request and 222 * the callback (recommended). 223 */ 224 private State state; 225 226 227 /** 228 * The nonce (required for implicit flow, optional for code 229 * flow). 230 */ 231 private Nonce nonce; 232 233 234 /** 235 * The requested display type (optional). 236 */ 237 private Display display; 238 239 240 /** 241 * The requested prompt (optional). 242 */ 243 private Prompt prompt; 244 245 246 /** 247 * The required maximum authentication age, in seconds, -1 if 248 * not specified, zero implies prompt=login (optional). 249 */ 250 private int maxAge = -1; 251 252 253 /** 254 * The end-user's preferred languages and scripts for the user 255 * interface (optional). 256 */ 257 private List<LangTag> uiLocales; 258 259 260 /** 261 * The end-user's preferred languages and scripts for claims 262 * being returned (optional). 263 */ 264 private List<LangTag> claimsLocales; 265 266 267 /** 268 * Previously issued ID Token passed to the authorisation 269 * server as a hint about the end-user's current or past 270 * authenticated session with the client (optional). Should be 271 * present when {@code prompt=none} is used. 272 */ 273 private JWT idTokenHint; 274 275 276 /** 277 * Hint to the authorisation server about the login identifier 278 * the end-user may use to log in (optional). 279 */ 280 private String loginHint; 281 282 283 /** 284 * Requested Authentication Context Class Reference values 285 * (optional). 286 */ 287 private List<ACR> acrValues; 288 289 290 /** 291 * Individual claims to be returned (optional). 292 */ 293 private ClaimsRequest claims; 294 295 296 /** 297 * Request object (optional). 298 */ 299 private JWT requestObject; 300 301 302 /** 303 * Request object URI (optional). 304 */ 305 private URI requestURI; 306 307 308 /** 309 * The response mode (optional). 310 */ 311 private ResponseMode rm; 312 313 314 /** 315 * The authorisation code challenge for PKCE (optional). 316 */ 317 private CodeChallenge codeChallenge; 318 319 320 /** 321 * The authorisation code challenge method for PKCE (optional). 322 */ 323 private CodeChallengeMethod codeChallengeMethod; 324 325 326 /** 327 * The additional custom parameters. 328 */ 329 private Map<String,String> customParams = new HashMap<>(); 330 331 332 /** 333 * Creates a new OpenID Connect authentication request builder. 334 * 335 * @param rt The response type. Corresponds to the 336 * {@code response_type} parameter. Must 337 * specify a valid OpenID Connect response 338 * type. Must not be {@code null}. 339 * @param scope The request scope. Corresponds to the 340 * {@code scope} parameter. Must contain an 341 * {@link OIDCScopeValue#OPENID openid 342 * value}. Must not be {@code null}. 343 * @param clientID The client identifier. Corresponds to the 344 * {@code client_id} parameter. Must not be 345 * {@code null}. 346 * @param redirectURI The redirection URI. Corresponds to the 347 * {@code redirect_uri} parameter. Must not 348 * be {@code null} unless set by means of 349 * the optional {@code request_object} / 350 * {@code request_uri} parameter. 351 */ 352 public Builder(final ResponseType rt, 353 final Scope scope, 354 final ClientID clientID, 355 final URI redirectURI) { 356 357 if (rt == null) 358 throw new IllegalArgumentException("The response type must not be null"); 359 360 OIDCResponseTypeValidator.validate(rt); 361 362 this.rt = rt; 363 364 if (scope == null) 365 throw new IllegalArgumentException("The scope must not be null"); 366 367 if (! scope.contains(OIDCScopeValue.OPENID)) 368 throw new IllegalArgumentException("The scope must include an \"openid\" value"); 369 370 this.scope = scope; 371 372 if (clientID == null) 373 throw new IllegalArgumentException("The client ID must not be null"); 374 375 this.clientID = clientID; 376 377 // Check presence at build time 378 this.redirectURI = redirectURI; 379 } 380 381 382 /** 383 * Creates a new OpenID Connect authentication request builder 384 * from the specified request. 385 * 386 * @param request The OpenID Connect authentication request. 387 * Must not be {@code null}. 388 */ 389 public Builder(final AuthenticationRequest request) { 390 391 uri = request.getEndpointURI(); 392 rt = request.getResponseType(); 393 clientID = request.getClientID(); 394 redirectURI = request.getRedirectionURI(); 395 scope = request.getScope(); 396 state = request.getState(); 397 nonce = request.getNonce(); 398 display = request.getDisplay(); 399 prompt = request.getPrompt(); 400 maxAge = request.getMaxAge(); 401 uiLocales = request.getUILocales(); 402 claimsLocales = request.getClaimsLocales(); 403 idTokenHint = request.getIDTokenHint(); 404 loginHint = request.getLoginHint(); 405 acrValues = request.getACRValues(); 406 claims = request.getClaims(); 407 requestObject = request.getRequestObject(); 408 requestURI = request.getRequestURI(); 409 rm = request.getResponseMode(); 410 codeChallenge = request.getCodeChallenge(); 411 codeChallengeMethod = request.getCodeChallengeMethod(); 412 customParams.putAll(request.getCustomParameters()); 413 } 414 415 416 /** 417 * Sets the state. Corresponds to the recommended {@code state} 418 * parameter. 419 * 420 * @param state The state, {@code null} if not specified. 421 * 422 * @return This builder. 423 */ 424 public Builder state(final State state) { 425 426 this.state = state; 427 return this; 428 } 429 430 431 /** 432 * Sets the URI of the endpoint (HTTP or HTTPS) for which the 433 * request is intended. 434 * 435 * @param uri The endpoint URI, {@code null} if not specified. 436 * 437 * @return This builder. 438 */ 439 public Builder endpointURI(final URI uri) { 440 441 this.uri = uri; 442 return this; 443 } 444 445 446 /** 447 * Sets the nonce. Corresponds to the conditionally optional 448 * {@code nonce} parameter. 449 * 450 * @param nonce The nonce, {@code null} if not specified. 451 * 452 * @return This builder. 453 */ 454 public Builder nonce(final Nonce nonce) { 455 456 this.nonce = nonce; 457 return this; 458 } 459 460 461 /** 462 * Sets the requested display type. Corresponds to the optional 463 * {@code display} parameter. 464 * 465 * @param display The requested display type, {@code null} if 466 * not specified. 467 * 468 * @return This builder. 469 */ 470 public Builder display(final Display display) { 471 472 this.display = display; 473 return this; 474 } 475 476 477 /** 478 * Sets the requested prompt. Corresponds to the optional 479 * {@code prompt} parameter. 480 * 481 * @param prompt The requested prompt, {@code null} if not 482 * specified. 483 * 484 * @return This builder. 485 */ 486 public Builder prompt(final Prompt prompt) { 487 488 this.prompt = prompt; 489 return this; 490 } 491 492 493 /** 494 * Sets the required maximum authentication age. Corresponds to 495 * the optional {@code max_age} parameter. 496 * 497 * @param maxAge The maximum authentication age, in seconds; 0 498 * if not specified. 499 * 500 * @return This builder. 501 */ 502 public Builder maxAge(final int maxAge) { 503 504 this.maxAge = maxAge; 505 return this; 506 } 507 508 509 /** 510 * Sets the end-user's preferred languages and scripts for the 511 * user interface, ordered by preference. Corresponds to the 512 * optional {@code ui_locales} parameter. 513 * 514 * @param uiLocales The preferred UI locales, {@code null} if 515 * not specified. 516 * 517 * @return This builder. 518 */ 519 public Builder uiLocales(final List<LangTag> uiLocales) { 520 521 this.uiLocales = uiLocales; 522 return this; 523 } 524 525 526 /** 527 * Sets the end-user's preferred languages and scripts for the 528 * claims being returned, ordered by preference. Corresponds to 529 * the optional {@code claims_locales} parameter. 530 * 531 * @param claimsLocales The preferred claims locales, 532 * {@code null} if not specified. 533 * 534 * @return This builder. 535 */ 536 public Builder claimsLocales(final List<LangTag> claimsLocales) { 537 538 this.claimsLocales = claimsLocales; 539 return this; 540 } 541 542 543 /** 544 * Sets the ID Token hint. Corresponds to the conditionally 545 * optional {@code id_token_hint} parameter. 546 * 547 * @param idTokenHint The ID Token hint, {@code null} if not 548 * specified. 549 * 550 * @return This builder. 551 */ 552 public Builder idTokenHint(final JWT idTokenHint) { 553 554 this.idTokenHint = idTokenHint; 555 return this; 556 } 557 558 559 /** 560 * Sets the login hint. Corresponds to the optional 561 * {@code login_hint} parameter. 562 * 563 * @param loginHint The login hint, {@code null} if not 564 * specified. 565 * 566 * @return This builder. 567 */ 568 public Builder loginHint(final String loginHint) { 569 570 this.loginHint = loginHint; 571 return this; 572 } 573 574 575 /** 576 * Sets the requested Authentication Context Class Reference 577 * values. Corresponds to the optional {@code acr_values} 578 * parameter. 579 * 580 * @param acrValues The requested ACR values, {@code null} if 581 * not specified. 582 * 583 * @return This builder. 584 */ 585 public Builder acrValues(final List<ACR> acrValues) { 586 587 this.acrValues = acrValues; 588 return this; 589 } 590 591 592 /** 593 * Sets the individual claims to be returned. Corresponds to 594 * the optional {@code claims} parameter. 595 * 596 * @param claims The individual claims to be returned, 597 * {@code null} if not specified. 598 * 599 * @return This builder. 600 */ 601 public Builder claims(final ClaimsRequest claims) { 602 603 this.claims = claims; 604 return this; 605 } 606 607 608 /** 609 * Sets the request object. Corresponds to the optional 610 * {@code request} parameter. Must not be specified together 611 * with a request object URI. 612 * 613 * @param requestObject The request object, {@code null} if not 614 * specified. 615 * 616 * @return This builder. 617 */ 618 public Builder requestObject(final JWT requestObject) { 619 620 this.requestObject = requestObject; 621 return this; 622 } 623 624 625 /** 626 * Sets the request object URI. Corresponds to the optional 627 * {@code request_uri} parameter. Must not be specified 628 * together with a request object. 629 * 630 * @param requestURI The request object URI, {@code null} if 631 * not specified. 632 * 633 * @return This builder. 634 */ 635 public Builder requestURI(final URI requestURI) { 636 637 this.requestURI = requestURI; 638 return this; 639 } 640 641 642 /** 643 * Sets the response mode. Corresponds to the optional 644 * {@code response_mode} parameter. Use of this parameter is 645 * not recommended unless a non-default response mode is 646 * requested (e.g. form_post). 647 * 648 * @param rm The response mode, {@code null} if not specified. 649 * 650 * @return This builder. 651 */ 652 public Builder responseMode(final ResponseMode rm) { 653 654 this.rm = rm; 655 return this; 656 } 657 658 659 /** 660 * Sets the code challenge for Proof Key for Code Exchange 661 * (PKCE) by public OAuth clients. 662 * 663 * @param codeChallenge The code challenge, {@code null} 664 * if not specified. 665 * @param codeChallengeMethod The code challenge method, 666 * {@code null} if not specified. 667 * 668 * @return This builder. 669 */ 670 @Deprecated 671 public Builder codeChallenge(final CodeChallenge codeChallenge, final CodeChallengeMethod codeChallengeMethod) { 672 673 this.codeChallenge = codeChallenge; 674 this.codeChallengeMethod = codeChallengeMethod; 675 return this; 676 } 677 678 679 /** 680 * Sets the code challenge for Proof Key for Code Exchange 681 * (PKCE) by public OAuth clients. 682 * 683 * @param codeVerifier The code verifier to use to 684 * compute the code challenge, 685 * {@code null} if PKCE is not 686 * specified. 687 * @param codeChallengeMethod The code challenge method, 688 * {@code null} if not specified. 689 * Defaults to 690 * {@link CodeChallengeMethod#PLAIN} 691 * if a code verifier is specified. 692 * 693 * @return This builder. 694 */ 695 public Builder codeChallenge(final CodeVerifier codeVerifier, final CodeChallengeMethod codeChallengeMethod) { 696 697 if (codeVerifier != null) { 698 CodeChallengeMethod method = codeChallengeMethod != null ? codeChallengeMethod : CodeChallengeMethod.getDefault(); 699 this.codeChallenge = CodeChallenge.compute(method, codeVerifier); 700 this.codeChallengeMethod = method; 701 } else { 702 this.codeChallenge = null; 703 this.codeChallengeMethod = null; 704 } 705 return this; 706 } 707 708 709 /** 710 * Sets the specified additional custom parameter. 711 * 712 * @param name The parameter name. Must not be {@code null}. 713 * @param value The parameter value, {@code null} if not 714 * specified. 715 * 716 * @return This builder. 717 */ 718 public Builder customParameter(final String name, final String value) { 719 720 customParams.put(name, value); 721 return this; 722 } 723 724 725 /** 726 * Builds a new authentication request. 727 * 728 * @return The authentication request. 729 */ 730 public AuthenticationRequest build() { 731 732 try { 733 return new AuthenticationRequest( 734 uri, rt, rm, scope, clientID, redirectURI, state, nonce, 735 display, prompt, maxAge, uiLocales, claimsLocales, 736 idTokenHint, loginHint, acrValues, claims, 737 requestObject, requestURI, 738 codeChallenge, codeChallengeMethod, 739 customParams); 740 741 } catch (IllegalArgumentException e) { 742 743 throw new IllegalStateException(e.getMessage(), e); 744 } 745 } 746 } 747 748 749 /** 750 * Creates a new minimal OpenID Connect authentication request. 751 * 752 * @param uri The URI of the OAuth 2.0 authorisation endpoint. 753 * May be {@code null} if the {@link #toHTTPRequest} 754 * method will not be used. 755 * @param rt The response type. Corresponds to the 756 * {@code response_type} parameter. Must specify a 757 * valid OpenID Connect response type. Must not be 758 * {@code null}. 759 * @param scope The request scope. Corresponds to the 760 * {@code scope} parameter. Must contain an 761 * {@link OIDCScopeValue#OPENID openid value}. Must 762 * not be {@code null}. 763 * @param clientID The client identifier. Corresponds to the 764 * {@code client_id} parameter. Must not be 765 * {@code null}. 766 * @param redirectURI The redirection URI. Corresponds to the 767 * {@code redirect_uri} parameter. Must not be 768 * {@code null}. 769 * @param state The state. Corresponds to the {@code state} 770 * parameter. May be {@code null}. 771 * @param nonce The nonce. Corresponds to the {@code nonce} 772 * parameter. May be {@code null} for code flow. 773 */ 774 public AuthenticationRequest(final URI uri, 775 final ResponseType rt, 776 final Scope scope, 777 final ClientID clientID, 778 final URI redirectURI, 779 final State state, 780 final Nonce nonce) { 781 782 // Not specified: display, prompt, maxAge, uiLocales, claimsLocales, 783 // idTokenHint, loginHint, acrValues, claims 784 // codeChallenge, codeChallengeMethod 785 this(uri, rt, null, scope, clientID, redirectURI, state, nonce, 786 null, null, -1, null, null, 787 null, null, null, null, null, null, 788 null, null); 789 } 790 791 792 /** 793 * Creates a new OpenID Connect authentication request. 794 * 795 * @param uri The URI of the OAuth 2.0 authorisation 796 * endpoint. May be {@code null} if the 797 * {@link #toHTTPRequest} method will not be 798 * used. 799 * @param rt The response type set. Corresponds to the 800 * {@code response_type} parameter. Must 801 * specify a valid OpenID Connect response 802 * type. Must not be {@code null}. 803 * @param rm The response mode. Corresponds to the 804 * optional {@code response_mode} parameter. 805 * Use of this parameter is not recommended 806 * unless a non-default response mode is 807 * requested (e.g. form_post). 808 * @param scope The request scope. Corresponds to the 809 * {@code scope} parameter. Must contain an 810 * {@link OIDCScopeValue#OPENID openid value}. 811 * Must not be {@code null}. 812 * @param clientID The client identifier. Corresponds to the 813 * {@code client_id} parameter. Must not be 814 * {@code null}. 815 * @param redirectURI The redirection URI. Corresponds to the 816 * {@code redirect_uri} parameter. Must not 817 * be {@code null} unless set by means of 818 * the optional {@code request_object} / 819 * {@code request_uri} parameter. 820 * @param state The state. Corresponds to the recommended 821 * {@code state} parameter. {@code null} if 822 * not specified. 823 * @param nonce The nonce. Corresponds to the 824 * {@code nonce} parameter. May be 825 * {@code null} for code flow. 826 * @param display The requested display type. Corresponds 827 * to the optional {@code display} 828 * parameter. 829 * {@code null} if not specified. 830 * @param prompt The requested prompt. Corresponds to the 831 * optional {@code prompt} parameter. 832 * {@code null} if not specified. 833 * @param maxAge The required maximum authentication age, 834 * in seconds. Corresponds to the optional 835 * {@code max_age} parameter. Zero if not 836 * specified. 837 * @param uiLocales The preferred languages and scripts for 838 * the user interface. Corresponds to the 839 * optional {@code ui_locales} parameter. 840 * {@code null} if not specified. 841 * @param claimsLocales The preferred languages and scripts for 842 * claims being returned. Corresponds to the 843 * optional {@code claims_locales} 844 * parameter. {@code null} if not specified. 845 * @param idTokenHint The ID Token hint. Corresponds to the 846 * optional {@code id_token_hint} parameter. 847 * {@code null} if not specified. 848 * @param loginHint The login hint. Corresponds to the 849 * optional {@code login_hint} parameter. 850 * {@code null} if not specified. 851 * @param acrValues The requested Authentication Context 852 * Class Reference values. Corresponds to 853 * the optional {@code acr_values} 854 * parameter. {@code null} if not specified. 855 * @param claims The individual claims to be returned. 856 * Corresponds to the optional 857 * {@code claims} parameter. {@code null} if 858 * not specified. 859 * @param requestObject The request object. Corresponds to the 860 * optional {@code request} parameter. Must 861 * not be specified together with a request 862 * object URI. {@code null} if not 863 * specified. 864 * @param requestURI The request object URI. Corresponds to 865 * the optional {@code request_uri} 866 * parameter. Must not be specified together 867 * with a request object. {@code null} if 868 * not specified. 869 * @param codeChallenge The code challenge for PKCE, {@code null} 870 * if not specified. 871 * @param codeChallengeMethod The code challenge method for PKCE, 872 * {@code null} if not specified. 873 */ 874 public AuthenticationRequest(final URI uri, 875 final ResponseType rt, 876 final ResponseMode rm, 877 final Scope scope, 878 final ClientID clientID, 879 final URI redirectURI, 880 final State state, 881 final Nonce nonce, 882 final Display display, 883 final Prompt prompt, 884 final int maxAge, 885 final List<LangTag> uiLocales, 886 final List<LangTag> claimsLocales, 887 final JWT idTokenHint, 888 final String loginHint, 889 final List<ACR> acrValues, 890 final ClaimsRequest claims, 891 final JWT requestObject, 892 final URI requestURI, 893 final CodeChallenge codeChallenge, 894 final CodeChallengeMethod codeChallengeMethod) { 895 896 this(uri, rt, rm, scope, clientID, redirectURI, state, 897 nonce, display, prompt, maxAge, uiLocales, claimsLocales, 898 idTokenHint, loginHint, acrValues, claims, 899 requestObject, requestURI, codeChallenge, codeChallengeMethod, 900 Collections.<String, String>emptyMap()); 901 } 902 903 904 /** 905 * Creates a new OpenID Connect authentication request with additional 906 * custom parameters. 907 * 908 * @param uri The URI of the OAuth 2.0 authorisation 909 * endpoint. May be {@code null} if the 910 * {@link #toHTTPRequest} method will not be 911 * used. 912 * @param rt The response type set. Corresponds to the 913 * {@code response_type} parameter. Must 914 * specify a valid OpenID Connect response 915 * type. Must not be {@code null}. 916 * @param rm The response mode. Corresponds to the 917 * optional {@code response_mode} parameter. 918 * Use of this parameter is not recommended 919 * unless a non-default response mode is 920 * requested (e.g. form_post). 921 * @param scope The request scope. Corresponds to the 922 * {@code scope} parameter. Must contain an 923 * {@link OIDCScopeValue#OPENID openid value}. 924 * Must not be {@code null}. 925 * @param clientID The client identifier. Corresponds to the 926 * {@code client_id} parameter. Must not be 927 * {@code null}. 928 * @param redirectURI The redirection URI. Corresponds to the 929 * {@code redirect_uri} parameter. Must not 930 * be {@code null} unless set by means of 931 * the optional {@code request_object} / 932 * {@code request_uri} parameter. 933 * @param state The state. Corresponds to the recommended 934 * {@code state} parameter. {@code null} if 935 * not specified. 936 * @param nonce The nonce. Corresponds to the 937 * {@code nonce} parameter. May be 938 * {@code null} for code flow. 939 * @param display The requested display type. Corresponds 940 * to the optional {@code display} 941 * parameter. 942 * {@code null} if not specified. 943 * @param prompt The requested prompt. Corresponds to the 944 * optional {@code prompt} parameter. 945 * {@code null} if not specified. 946 * @param maxAge The required maximum authentication age, 947 * in seconds. Corresponds to the optional 948 * {@code max_age} parameter. -1 if not 949 * specified, zero implies 950 * {@code prompt=login}. 951 * @param uiLocales The preferred languages and scripts for 952 * the user interface. Corresponds to the 953 * optional {@code ui_locales} parameter. 954 * {@code null} if not specified. 955 * @param claimsLocales The preferred languages and scripts for 956 * claims being returned. Corresponds to the 957 * optional {@code claims_locales} 958 * parameter. {@code null} if not specified. 959 * @param idTokenHint The ID Token hint. Corresponds to the 960 * optional {@code id_token_hint} parameter. 961 * {@code null} if not specified. 962 * @param loginHint The login hint. Corresponds to the 963 * optional {@code login_hint} parameter. 964 * {@code null} if not specified. 965 * @param acrValues The requested Authentication Context 966 * Class Reference values. Corresponds to 967 * the optional {@code acr_values} 968 * parameter. {@code null} if not specified. 969 * @param claims The individual claims to be returned. 970 * Corresponds to the optional 971 * {@code claims} parameter. {@code null} if 972 * not specified. 973 * @param requestObject The request object. Corresponds to the 974 * optional {@code request} parameter. Must 975 * not be specified together with a request 976 * object URI. {@code null} if not 977 * specified. 978 * @param requestURI The request object URI. Corresponds to 979 * the optional {@code request_uri} 980 * parameter. Must not be specified together 981 * with a request object. {@code null} if 982 * not specified. 983 * @param codeChallenge The code challenge for PKCE, {@code null} 984 * if not specified. 985 * @param codeChallengeMethod The code challenge method for PKCE, 986 * {@code null} if not specified. 987 * @param customParams Additional custom parameters, empty map 988 * or {@code null} if none. 989 */ 990 public AuthenticationRequest(final URI uri, 991 final ResponseType rt, 992 final ResponseMode rm, 993 final Scope scope, 994 final ClientID clientID, 995 final URI redirectURI, 996 final State state, 997 final Nonce nonce, 998 final Display display, 999 final Prompt prompt, 1000 final int maxAge, 1001 final List<LangTag> uiLocales, 1002 final List<LangTag> claimsLocales, 1003 final JWT idTokenHint, 1004 final String loginHint, 1005 final List<ACR> acrValues, 1006 final ClaimsRequest claims, 1007 final JWT requestObject, 1008 final URI requestURI, 1009 final CodeChallenge codeChallenge, 1010 final CodeChallengeMethod codeChallengeMethod, 1011 final Map<String,String> customParams) { 1012 1013 super(uri, rt, rm, clientID, redirectURI, scope, state, codeChallenge, codeChallengeMethod, customParams); 1014 1015 // Redirect URI required unless set in request_object / request_uri 1016 if (redirectURI == null && requestObject == null && requestURI == null) 1017 throw new IllegalArgumentException("The redirection URI must not be null"); 1018 1019 OIDCResponseTypeValidator.validate(rt); 1020 1021 if (scope == null) 1022 throw new IllegalArgumentException("The scope must not be null"); 1023 1024 if (! scope.contains(OIDCScopeValue.OPENID)) 1025 throw new IllegalArgumentException("The scope must include an \"openid\" token"); 1026 1027 1028 // Nonce required in the implicit and hybrid flows 1029 if (nonce == null && (rt.impliesImplicitFlow() || rt.impliesHybridFlow())) 1030 throw new IllegalArgumentException("Nonce is required in implicit / hybrid protocol flow"); 1031 1032 this.nonce = nonce; 1033 1034 // Optional parameters 1035 this.display = display; 1036 this.prompt = prompt; 1037 this.maxAge = maxAge; 1038 1039 if (uiLocales != null) 1040 this.uiLocales = Collections.unmodifiableList(uiLocales); 1041 else 1042 this.uiLocales = null; 1043 1044 if (claimsLocales != null) 1045 this.claimsLocales = Collections.unmodifiableList(claimsLocales); 1046 else 1047 this.claimsLocales = null; 1048 1049 this.idTokenHint = idTokenHint; 1050 this.loginHint = loginHint; 1051 1052 if (acrValues != null) 1053 this.acrValues = Collections.unmodifiableList(acrValues); 1054 else 1055 this.acrValues = null; 1056 1057 this.claims = claims; 1058 1059 if (requestObject != null && requestURI != null) 1060 throw new IllegalArgumentException("Either a request object or a request URI must be specified, but not both"); 1061 1062 this.requestObject = requestObject; 1063 this.requestURI = requestURI; 1064 } 1065 1066 1067 /** 1068 * Returns the registered (standard) OpenID Connect authentication 1069 * request parameter names. 1070 * 1071 * @return The registered OpenID Connect authentication request 1072 * parameter names, as a unmodifiable set. 1073 */ 1074 public static Set<String> getRegisteredParameterNames() { 1075 1076 return REGISTERED_PARAMETER_NAMES; 1077 } 1078 1079 1080 /** 1081 * Gets the nonce. Corresponds to the conditionally optional 1082 * {@code nonce} parameter. 1083 * 1084 * @return The nonce, {@code null} if not specified. 1085 */ 1086 public Nonce getNonce() { 1087 1088 return nonce; 1089 } 1090 1091 1092 /** 1093 * Gets the requested display type. Corresponds to the optional 1094 * {@code display} parameter. 1095 * 1096 * @return The requested display type, {@code null} if not specified. 1097 */ 1098 public Display getDisplay() { 1099 1100 return display; 1101 } 1102 1103 1104 /** 1105 * Gets the requested prompt. Corresponds to the optional 1106 * {@code prompt} parameter. 1107 * 1108 * @return The requested prompt, {@code null} if not specified. 1109 */ 1110 public Prompt getPrompt() { 1111 1112 return prompt; 1113 } 1114 1115 1116 /** 1117 * Gets the required maximum authentication age. Corresponds to the 1118 * optional {@code max_age} parameter. 1119 * 1120 * @return The maximum authentication age, in seconds; -1 if not 1121 * specified, zero implies {@code prompt=login}. 1122 */ 1123 public int getMaxAge() { 1124 1125 return maxAge; 1126 } 1127 1128 1129 /** 1130 * Gets the end-user's preferred languages and scripts for the user 1131 * interface, ordered by preference. Corresponds to the optional 1132 * {@code ui_locales} parameter. 1133 * 1134 * @return The preferred UI locales, {@code null} if not specified. 1135 */ 1136 public List<LangTag> getUILocales() { 1137 1138 return uiLocales; 1139 } 1140 1141 1142 /** 1143 * Gets the end-user's preferred languages and scripts for the claims 1144 * being returned, ordered by preference. Corresponds to the optional 1145 * {@code claims_locales} parameter. 1146 * 1147 * @return The preferred claims locales, {@code null} if not specified. 1148 */ 1149 public List<LangTag> getClaimsLocales() { 1150 1151 return claimsLocales; 1152 } 1153 1154 1155 /** 1156 * Gets the ID Token hint. Corresponds to the conditionally optional 1157 * {@code id_token_hint} parameter. 1158 * 1159 * @return The ID Token hint, {@code null} if not specified. 1160 */ 1161 public JWT getIDTokenHint() { 1162 1163 return idTokenHint; 1164 } 1165 1166 1167 /** 1168 * Gets the login hint. Corresponds to the optional {@code login_hint} 1169 * parameter. 1170 * 1171 * @return The login hint, {@code null} if not specified. 1172 */ 1173 public String getLoginHint() { 1174 1175 return loginHint; 1176 } 1177 1178 1179 /** 1180 * Gets the requested Authentication Context Class Reference values. 1181 * Corresponds to the optional {@code acr_values} parameter. 1182 * 1183 * @return The requested ACR values, {@code null} if not specified. 1184 */ 1185 public List<ACR> getACRValues() { 1186 1187 return acrValues; 1188 } 1189 1190 1191 /** 1192 * Gets the individual claims to be returned. Corresponds to the 1193 * optional {@code claims} parameter. 1194 * 1195 * @return The individual claims to be returned, {@code null} if not 1196 * specified. 1197 */ 1198 public ClaimsRequest getClaims() { 1199 1200 return claims; 1201 } 1202 1203 1204 /** 1205 * Gets the request object. Corresponds to the optional {@code request} 1206 * parameter. 1207 * 1208 * @return The request object, {@code null} if not specified. 1209 */ 1210 public JWT getRequestObject() { 1211 1212 return requestObject; 1213 } 1214 1215 1216 /** 1217 * Gets the request object URI. Corresponds to the optional 1218 * {@code request_uri} parameter. 1219 * 1220 * @return The request object URI, {@code null} if not specified. 1221 */ 1222 public URI getRequestURI() { 1223 1224 return requestURI; 1225 } 1226 1227 1228 /** 1229 * Returns {@code true} if this authentication request specifies an 1230 * OpenID Connect request object (directly through the {@code request} 1231 * parameter or by reference through the {@code request_uri} parameter). 1232 * 1233 * @return {@code true} if a request object is specified, else 1234 * {@code false}. 1235 */ 1236 public boolean specifiesRequestObject() { 1237 1238 return requestObject != null || requestURI != null; 1239 } 1240 1241 1242 @Override 1243 public Map<String,String> toParameters() { 1244 1245 Map <String,String> params = super.toParameters(); 1246 1247 if (nonce != null) 1248 params.put("nonce", nonce.toString()); 1249 1250 if (display != null) 1251 params.put("display", display.toString()); 1252 1253 if (prompt != null) 1254 params.put("prompt", prompt.toString()); 1255 1256 if (maxAge >= 0) 1257 params.put("max_age", "" + maxAge); 1258 1259 if (uiLocales != null) { 1260 1261 StringBuilder sb = new StringBuilder(); 1262 1263 for (LangTag locale: uiLocales) { 1264 1265 if (sb.length() > 0) 1266 sb.append(' '); 1267 1268 sb.append(locale.toString()); 1269 } 1270 1271 params.put("ui_locales", sb.toString()); 1272 } 1273 1274 if (claimsLocales != null) { 1275 1276 StringBuilder sb = new StringBuilder(); 1277 1278 for (LangTag locale: claimsLocales) { 1279 1280 if (sb.length() > 0) 1281 sb.append(' '); 1282 1283 sb.append(locale.toString()); 1284 } 1285 1286 params.put("claims_locales", sb.toString()); 1287 } 1288 1289 if (idTokenHint != null) { 1290 1291 try { 1292 params.put("id_token_hint", idTokenHint.serialize()); 1293 1294 } catch (IllegalStateException e) { 1295 1296 throw new SerializeException("Couldn't serialize ID token hint: " + e.getMessage(), e); 1297 } 1298 } 1299 1300 if (loginHint != null) 1301 params.put("login_hint", loginHint); 1302 1303 if (acrValues != null) { 1304 1305 StringBuilder sb = new StringBuilder(); 1306 1307 for (ACR acr: acrValues) { 1308 1309 if (sb.length() > 0) 1310 sb.append(' '); 1311 1312 sb.append(acr.toString()); 1313 } 1314 1315 params.put("acr_values", sb.toString()); 1316 } 1317 1318 1319 if (claims != null) 1320 params.put("claims", claims.toJSONObject().toString()); 1321 1322 if (requestObject != null) { 1323 1324 try { 1325 params.put("request", requestObject.serialize()); 1326 1327 } catch (IllegalStateException e) { 1328 1329 throw new SerializeException("Couldn't serialize request object to JWT: " + e.getMessage(), e); 1330 } 1331 } 1332 1333 if (requestURI != null) 1334 params.put("request_uri", requestURI.toString()); 1335 1336 return params; 1337 } 1338 1339 1340 /** 1341 * Parses an OpenID Connect authentication request from the specified 1342 * parameters. 1343 * 1344 * <p>Example parameters: 1345 * 1346 * <pre> 1347 * response_type = token id_token 1348 * client_id = s6BhdRkqt3 1349 * redirect_uri = https://client.example.com/cb 1350 * scope = openid profile 1351 * state = af0ifjsldkj 1352 * nonce = -0S6_WzA2Mj 1353 * </pre> 1354 * 1355 * @param params The parameters. Must not be {@code null}. 1356 * 1357 * @return The OpenID Connect authentication request. 1358 * 1359 * @throws ParseException If the parameters couldn't be parsed to an 1360 * OpenID Connect authentication request. 1361 */ 1362 public static AuthenticationRequest parse(final Map<String,String> params) 1363 throws ParseException { 1364 1365 return parse(null, params); 1366 } 1367 1368 1369 /** 1370 * Parses an OpenID Connect authentication request from the specified 1371 * parameters. 1372 * 1373 * <p>Example parameters: 1374 * 1375 * <pre> 1376 * response_type = token id_token 1377 * client_id = s6BhdRkqt3 1378 * redirect_uri = https://client.example.com/cb 1379 * scope = openid profile 1380 * state = af0ifjsldkj 1381 * nonce = -0S6_WzA2Mj 1382 * </pre> 1383 * 1384 * @param uri The URI of the OAuth 2.0 authorisation endpoint. May 1385 * be {@code null} if the {@link #toHTTPRequest} method 1386 * will not be used. 1387 * @param params The parameters. Must not be {@code null}. 1388 * 1389 * @return The OpenID Connect authentication request. 1390 * 1391 * @throws ParseException If the parameters couldn't be parsed to an 1392 * OpenID Connect authentication request. 1393 */ 1394 public static AuthenticationRequest parse(final URI uri, final Map<String,String> params) 1395 throws ParseException { 1396 1397 // Parse and validate the core OAuth 2.0 autz request params in 1398 // the context of OIDC 1399 AuthorizationRequest ar = AuthorizationRequest.parse(uri, params); 1400 1401 ClientID clientID = ar.getClientID(); 1402 State state = ar.getState(); 1403 ResponseMode rm = ar.getResponseMode(); 1404 1405 // Required in OIDC, check later after optional request_object / request_uri is parsed 1406 URI redirectURI = ar.getRedirectionURI(); 1407 1408 ResponseType rt = ar.getResponseType(); 1409 1410 try { 1411 OIDCResponseTypeValidator.validate(rt); 1412 1413 } catch (IllegalArgumentException e) { 1414 String msg = "Unsupported \"response_type\" parameter: " + e.getMessage(); 1415 throw new ParseException(msg, OAuth2Error.UNSUPPORTED_RESPONSE_TYPE.appendDescription(": " + msg), 1416 clientID, redirectURI, ar.impliedResponseMode(), state); 1417 } 1418 1419 // Required in OIDC, must include "openid" parameter 1420 Scope scope = ar.getScope(); 1421 1422 if (scope == null) { 1423 String msg = "Missing \"scope\" parameter"; 1424 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1425 clientID, redirectURI, ar.impliedResponseMode(), state); 1426 } 1427 1428 if (! scope.contains(OIDCScopeValue.OPENID)) { 1429 String msg = "The scope must include an \"openid\" value"; 1430 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1431 clientID, redirectURI, ar.impliedResponseMode(), state); 1432 } 1433 1434 1435 // Parse the remaining OIDC parameters 1436 Nonce nonce = Nonce.parse(params.get("nonce")); 1437 1438 // Nonce required in the implicit and hybrid flows 1439 if (nonce == null && (rt.impliesImplicitFlow() || rt.impliesHybridFlow())) { 1440 String msg = "Missing \"nonce\" parameter: Required in the implicit and hybrid flows"; 1441 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1442 clientID, redirectURI, ar.impliedResponseMode(), state); 1443 } 1444 1445 Display display = null; 1446 1447 if (params.containsKey("display")) { 1448 try { 1449 display = Display.parse(params.get("display")); 1450 1451 } catch (ParseException e) { 1452 String msg = "Invalid \"display\" parameter: " + e.getMessage(); 1453 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1454 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1455 } 1456 } 1457 1458 1459 Prompt prompt; 1460 1461 try { 1462 prompt = Prompt.parse(params.get("prompt")); 1463 1464 } catch (ParseException e) { 1465 String msg = "Invalid \"prompt\" parameter: " + e.getMessage(); 1466 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1467 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1468 } 1469 1470 1471 String v = params.get("max_age"); 1472 1473 int maxAge = -1; 1474 1475 if (StringUtils.isNotBlank(v)) { 1476 1477 try { 1478 maxAge = Integer.parseInt(v); 1479 1480 } catch (NumberFormatException e) { 1481 String msg = "Invalid \"max_age\" parameter: " + v; 1482 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1483 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1484 } 1485 } 1486 1487 1488 v = params.get("ui_locales"); 1489 1490 List<LangTag> uiLocales = null; 1491 1492 if (StringUtils.isNotBlank(v)) { 1493 1494 uiLocales = new LinkedList<>(); 1495 1496 StringTokenizer st = new StringTokenizer(v, " "); 1497 1498 while (st.hasMoreTokens()) { 1499 1500 try { 1501 uiLocales.add(LangTag.parse(st.nextToken())); 1502 1503 } catch (LangTagException e) { 1504 String msg = "Invalid \"ui_locales\" parameter: " + e.getMessage(); 1505 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1506 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1507 } 1508 } 1509 } 1510 1511 1512 v = params.get("claims_locales"); 1513 1514 List<LangTag> claimsLocales = null; 1515 1516 if (StringUtils.isNotBlank(v)) { 1517 1518 claimsLocales = new LinkedList<>(); 1519 1520 StringTokenizer st = new StringTokenizer(v, " "); 1521 1522 while (st.hasMoreTokens()) { 1523 1524 try { 1525 claimsLocales.add(LangTag.parse(st.nextToken())); 1526 1527 } catch (LangTagException e) { 1528 String msg = "Invalid \"claims_locales\" parameter: " + e.getMessage(); 1529 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1530 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1531 } 1532 } 1533 } 1534 1535 1536 v = params.get("id_token_hint"); 1537 1538 JWT idTokenHint = null; 1539 1540 if (StringUtils.isNotBlank(v)) { 1541 1542 try { 1543 idTokenHint = JWTParser.parse(v); 1544 1545 } catch (java.text.ParseException e) { 1546 String msg = "Invalid \"id_token_hint\" parameter: " + e.getMessage(); 1547 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1548 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1549 } 1550 } 1551 1552 String loginHint = params.get("login_hint"); 1553 1554 1555 v = params.get("acr_values"); 1556 1557 List<ACR> acrValues = null; 1558 1559 if (StringUtils.isNotBlank(v)) { 1560 1561 acrValues = new LinkedList<>(); 1562 1563 StringTokenizer st = new StringTokenizer(v, " "); 1564 1565 while (st.hasMoreTokens()) { 1566 1567 acrValues.add(new ACR(st.nextToken())); 1568 } 1569 } 1570 1571 1572 v = params.get("claims"); 1573 1574 ClaimsRequest claims = null; 1575 1576 if (StringUtils.isNotBlank(v)) { 1577 1578 JSONObject jsonObject; 1579 1580 try { 1581 jsonObject = JSONObjectUtils.parse(v); 1582 1583 } catch (ParseException e) { 1584 String msg = "Invalid \"claims\" parameter: " + e.getMessage(); 1585 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1586 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1587 } 1588 1589 // Parse exceptions silently ignored 1590 claims = ClaimsRequest.parse(jsonObject); 1591 } 1592 1593 1594 v = params.get("request_uri"); 1595 1596 URI requestURI = null; 1597 1598 if (StringUtils.isNotBlank(v)) { 1599 1600 try { 1601 requestURI = new URI(v); 1602 1603 } catch (URISyntaxException e) { 1604 String msg = "Invalid \"request_uri\" parameter: " + e.getMessage(); 1605 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1606 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1607 } 1608 } 1609 1610 v = params.get("request"); 1611 1612 JWT requestObject = null; 1613 1614 if (StringUtils.isNotBlank(v)) { 1615 1616 // request_object and request_uri must not be defined at the same time 1617 if (requestURI != null) { 1618 String msg = "Invalid request: Found mutually exclusive \"request\" and \"request_uri\" parameters"; 1619 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1620 clientID, redirectURI, ar.impliedResponseMode(), state, null); 1621 } 1622 1623 try { 1624 requestObject = JWTParser.parse(v); 1625 1626 } catch (java.text.ParseException e) { 1627 String msg = "Invalid \"request_object\" parameter: " + e.getMessage(); 1628 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1629 clientID, redirectURI, ar.impliedResponseMode(), state, e); 1630 } 1631 } 1632 1633 1634 // Redirect URI required unless request_object / request_uri present 1635 if (redirectURI == null && requestObject == null && requestURI == null) { 1636 String msg = "Missing \"redirect_uri\" parameter"; 1637 throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), 1638 clientID, null, ar.impliedResponseMode(), state); 1639 } 1640 1641 // Parse additional custom parameters 1642 Map<String,String> customParams = null; 1643 1644 for (Map.Entry<String,String> p: params.entrySet()) { 1645 1646 if (! REGISTERED_PARAMETER_NAMES.contains(p.getKey())) { 1647 // We have a custom parameter 1648 if (customParams == null) { 1649 customParams = new HashMap<>(); 1650 } 1651 customParams.put(p.getKey(), p.getValue()); 1652 } 1653 } 1654 1655 1656 return new AuthenticationRequest( 1657 uri, rt, rm, scope, clientID, redirectURI, state, nonce, 1658 display, prompt, maxAge, uiLocales, claimsLocales, 1659 idTokenHint, loginHint, acrValues, claims, requestObject, requestURI, 1660 ar.getCodeChallenge(), ar.getCodeChallengeMethod(), 1661 customParams); 1662 } 1663 1664 1665 /** 1666 * Parses an OpenID Connect authentication request from the specified 1667 * URI query string. 1668 * 1669 * <p>Example URI query string: 1670 * 1671 * <pre> 1672 * response_type=token%20id_token 1673 * &client_id=s6BhdRkqt3 1674 * &redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb 1675 * &scope=openid%20profile 1676 * &state=af0ifjsldkj 1677 * &nonce=n-0S6_WzA2Mj 1678 * </pre> 1679 * 1680 * @param query The URI query string. Must not be {@code null}. 1681 * 1682 * @return The OpenID Connect authentication request. 1683 * 1684 * @throws ParseException If the query string couldn't be parsed to an 1685 * OpenID Connect authentication request. 1686 */ 1687 public static AuthenticationRequest parse(final String query) 1688 throws ParseException { 1689 1690 return parse(null, URLUtils.parseParameters(query)); 1691 } 1692 1693 1694 /** 1695 * Parses an OpenID Connect authentication request from the specified 1696 * URI query string. 1697 * 1698 * <p>Example URI query string: 1699 * 1700 * <pre> 1701 * response_type=token%20id_token 1702 * &client_id=s6BhdRkqt3 1703 * &redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb 1704 * &scope=openid%20profile 1705 * &state=af0ifjsldkj 1706 * &nonce=n-0S6_WzA2Mj 1707 * </pre> 1708 * 1709 * @param uri The URI of the OAuth 2.0 authorisation endpoint. May be 1710 * {@code null} if the {@link #toHTTPRequest} method will 1711 * not be used. 1712 * @param query The URI query string. Must not be {@code null}. 1713 * 1714 * @return The OpenID Connect authentication request. 1715 * 1716 * @throws ParseException If the query string couldn't be parsed to an 1717 * OpenID Connect authentication request. 1718 */ 1719 public static AuthenticationRequest parse(final URI uri, final String query) 1720 throws ParseException { 1721 1722 return parse(uri, URLUtils.parseParameters(query)); 1723 } 1724 1725 1726 /** 1727 * Parses an OpenID Connect authentication request from the specified 1728 * URI. 1729 * 1730 * <p>Example URI: 1731 * 1732 * <pre> 1733 * https://server.example.com/authorize? 1734 * response_type=token%20id_token 1735 * &client_id=s6BhdRkqt3 1736 * &redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb 1737 * &scope=openid%20profile 1738 * &state=af0ifjsldkj 1739 * &nonce=n-0S6_WzA2Mj 1740 * </pre> 1741 * 1742 * @param uri The URI. Must not be {@code null}. 1743 * 1744 * @return The OpenID Connect authentication request. 1745 * 1746 * @throws ParseException If the query string couldn't be parsed to an 1747 * OpenID Connect authentication request. 1748 */ 1749 public static AuthenticationRequest parse(final URI uri) 1750 throws ParseException { 1751 1752 return parse(URIUtils.getBaseURI(uri), URLUtils.parseParameters(uri.getRawQuery())); 1753 } 1754 1755 1756 /** 1757 * Parses an authentication request from the specified HTTP GET or HTTP 1758 * POST request. 1759 * 1760 * <p>Example HTTP request (GET): 1761 * 1762 * <pre> 1763 * https://server.example.com/op/authorize? 1764 * response_type=code%20id_token 1765 * &client_id=s6BhdRkqt3 1766 * &redirect_uri=https%3A%2F%2Fclient.example.com%2Fcb 1767 * &scope=openid 1768 * &nonce=n-0S6_WzA2Mj 1769 * &state=af0ifjsldkj 1770 * </pre> 1771 * 1772 * @param httpRequest The HTTP request. Must not be {@code null}. 1773 * 1774 * @return The OpenID Connect authentication request. 1775 * 1776 * @throws ParseException If the HTTP request couldn't be parsed to an 1777 * OpenID Connect authentication request. 1778 */ 1779 public static AuthenticationRequest parse(final HTTPRequest httpRequest) 1780 throws ParseException { 1781 1782 String query = httpRequest.getQuery(); 1783 1784 if (query == null) 1785 throw new ParseException("Missing URI query string"); 1786 1787 URI endpointURI; 1788 1789 try { 1790 endpointURI = httpRequest.getURL().toURI(); 1791 1792 } catch (URISyntaxException e) { 1793 1794 throw new ParseException(e.getMessage(), e); 1795 } 1796 1797 return parse(endpointURI, query); 1798 } 1799}