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