001/*
002 * Copyright 2015 The AppAuth for Android Authors. All Rights Reserved.
003 *
004 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except
005 * in compliance with the License. You may obtain a copy of the License at
006 *
007 * http://www.apache.org/licenses/LICENSE-2.0
008 *
009 * Unless required by applicable law or agreed to in writing, software distributed under the
010 * License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either
011 * express or implied. See the License for the specific language governing permissions and
012 * limitations under the License.
013 */
014
015package net.openid.appauth;
016
017import static net.openid.appauth.AdditionalParamsProcessor.builtInParams;
018import static net.openid.appauth.AdditionalParamsProcessor.checkAdditionalParams;
019import static net.openid.appauth.Preconditions.checkArgument;
020import static net.openid.appauth.Preconditions.checkNotEmpty;
021import static net.openid.appauth.Preconditions.checkNotNull;
022import static net.openid.appauth.Preconditions.checkNullOrNotEmpty;
023
024import android.net.Uri;
025import android.text.TextUtils;
026import androidx.annotation.NonNull;
027import androidx.annotation.Nullable;
028import androidx.annotation.VisibleForTesting;
029
030import net.openid.appauth.internal.UriUtil;
031import org.json.JSONException;
032import org.json.JSONObject;
033
034import java.util.Arrays;
035import java.util.Collections;
036import java.util.HashMap;
037import java.util.Map;
038import java.util.Map.Entry;
039import java.util.Set;
040
041/**
042 * An OAuth2 authorization request.
043 *
044 * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 4
045 * <https://tools.ietf.org/html/rfc6749#section-4>"
046 * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 4.1.1
047 * <https://tools.ietf.org/html/rfc6749#section-4.1.1>"
048 */
049public class AuthorizationRequest implements AuthorizationManagementRequest {
050
051    /**
052     * SHA-256 based code verifier challenge method.
053     *
054     * @see "Proof Key for Code Exchange by OAuth Public Clients (RFC 7636), Section 4.3
055     * <https://tools.ietf.org/html/rfc7636#section-4.3>"
056     */
057    public static final String CODE_CHALLENGE_METHOD_S256 = "S256";
058
059    /**
060     * Plain-text code verifier challenge method. This is only used by AppAuth for Android if
061     * SHA-256 is not supported on this platform.
062     *
063     * @see "Proof Key for Code Exchange by OAuth Public Clients (RFC 7636), Section 4.4
064     * <https://tools.ietf.org/html/rfc7636#section-4.4>"
065     */
066    public static final String CODE_CHALLENGE_METHOD_PLAIN = "plain";
067
068    /**
069     * All spec-defined values for the OpenID Connect 1.0 `display` parameter.
070     *
071     * @see Builder#setDisplay(String)
072     * @see "OpenID Connect Core 1.0, Section 3.1.2.1
073     * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
074     */
075    // SuppressWarnings justification: the constants defined are not directly used by the library,
076    // existing only for convenience of the developer.
077    @SuppressWarnings("unused")
078    public static final class Display {
079
080        /**
081         * The Authorization Server _SHOULD_ display the authentication and consent UI
082         * consistent with a full User Agent page view. If the display parameter is not specified,
083         * this is the default display mode.
084         */
085        public static final String PAGE = "page";
086
087        /**
088         * The Authorization Server _SHOULD_ display the authentication and consent UI
089         * consistent with a popup User Agent window. The popup User Agent window should be of an
090         * appropriate size for a login-focused dialog and should not obscure the entire window that
091         * it is popping up over.
092         */
093        public static final String POPUP = "popup";
094
095        /**
096         * The Authorization Server _SHOULD_ display the authentication and consent UI
097         * consistent with a device that leverages a touch interface.
098         */
099        public static final String TOUCH = "touch";
100
101        /**
102         * The Authorization Server _SHOULD_ display the authentication and consent UI
103         * consistent with a "feature phone" type display.
104         */
105        public static final String WAP = "wap";
106    }
107
108    /**
109     * All spec-defined values for the OpenID Connect 1.0 `prompt` parameter.
110     *
111     * @see Builder#setPrompt(String)
112     * @see "OpenID Connect Core 1.0, Section 3.1.2.1
113     * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
114     */
115    // SuppressWarnings justification: the constants defined are not directly used by the library,
116    // existing only for convenience of the developer.
117    @SuppressWarnings("unused")
118    public static final class Prompt {
119
120        /**
121         * The Authorization Server _MUST NOT_ display any authentication or consent user
122         * interface pages. An error is returned if an End-User is not already authenticated or the
123         * Client does not have pre-configured consent for the requested Claims or does not fulfill
124         * other conditions for processing the request. The error code will typically be
125         * `login_required`, `interaction_required`, or another code defined in
126         * [OpenID Connect Core 1.0, Section 3.1.2.6](
127         * https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.6). This can be
128         * used as a method to check for existing authentication and/or consent.
129         *
130         * @see "OpenID Connect Core 1.0, Section 3.1.2.1
131         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
132         * @see "OpenID Connect Core 1.0, Section 3.1.2.6
133         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.6>"
134         */
135        public static final String NONE = "none";
136
137        /**
138         * The Authorization Server _SHOULD_ prompt the End-User for re-authentication. If
139         * it cannot re-authenticate the End-User, it _MUST_ return an error, typically
140         * `login_required`.
141         *
142         * @see "OpenID Connect Core 1.0, Section 3.1.2.1
143         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
144         * @see "OpenID Connect Core 1.0, Section 3.1.2.6
145         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.6>"
146         */
147        public static final String LOGIN = "login";
148
149        /**
150         * The Authorization Server _SHOULD_ prompt the End-User for consent before
151         * returning information to the Client. If it cannot obtain consent, it _MUST_
152         * return an error, typically `consent_required`.
153         *
154         * @see "OpenID Connect Core 1.0, Section 3.1.2.1
155         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
156         * @see "OpenID Connect Core 1.0, Section 3.1.2.6
157         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.6>"
158         */
159        public static final String CONSENT = "consent";
160
161        /**
162         * The Authorization Server _SHOULD_ prompt the End-User to select a user account.
163         * This enables an End-User who has multiple accounts at the Authorization Server to select
164         * amongst the multiple accounts that they might have current sessions for. If it cannot
165         * obtain an account selection choice made by the End-User, it MUST return an error,
166         * typically `account_selection_required`.
167         *
168         * @see "OpenID Connect Core 1.0, Section 3.1.2.1
169         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
170         * @see "OpenID Connect Core 1.0, Section 3.1.2.6
171         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.6>"
172         */
173        public static final String SELECT_ACCOUNT = "select_account";
174    }
175
176    /**
177     * All spec-defined values for the OAuth2 / OpenID Connect 1.0 `scope` parameter.
178     *
179     * @see Builder#setScope(String)
180     * @see "OpenID Connect Core 1.0, Section 5.4
181     * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.5.4>"
182     */
183    // SuppressWarnings justification: the constants defined are not directly used by the library,
184    // existing only for convenience of the developer.
185    @SuppressWarnings("unused")
186    public static final class Scope {
187        /**
188         * A scope for the authenticated user's mailing address.
189         *
190         * @see "OpenID Connect Core 1.0, Section 5.4
191         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.5.4>"
192         */
193        public static final String ADDRESS = "address";
194
195        /**
196         * A scope for the authenticated user's email address.
197         *
198         * @see "OpenID Connect Core 1.0, Section 5.4
199         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.5.4>"
200         */
201        public static final String EMAIL = "email";
202
203        /**
204         * A scope for requesting an OAuth 2.0 refresh token to be issued, that can be used to
205         * obtain an Access Token that grants access to the End-User's UserInfo Endpoint even
206         * when the End-User is not present (not logged in).
207         *
208         * @see "OpenID Connect Core 1.0, Section 11
209         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.11>"
210         */
211        public static final String OFFLINE_ACCESS = "offline_access";
212
213        /**
214         * A scope for OpenID based authorization.
215         *
216         * @see "OpenID Connect Core 1.0, Section 3.1.2.1
217         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
218         */
219        public static final String OPENID = "openid";
220
221        /**
222         * A scope for the authenticated user's phone number.
223         *
224         * @see "OpenID Connect Core 1.0, Section 5.4
225         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.5.4>"
226         */
227        public static final String PHONE = "phone";
228
229        /**
230         * A scope for the authenticated user's basic profile information.
231         *
232         * @see "OpenID Connect Core 1.0, Section 5.4
233         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.5.4>"
234         */
235        public static final String PROFILE = "profile";
236    }
237
238    /**
239     * All spec-defined values for the OAuth2 / OpenID Connect `response_mode` parameter.
240     *
241     * @see Builder#setResponseMode(String)
242     * @see "OAuth 2.0 Multiple Response Type Encoding Practices, Section 2.1
243     * <http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#rfc.section.2.1>"
244     * @see "OpenID Connect Core 1.0, Section 3.1.2.1
245     * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
246     */
247    // SuppressWarnings justification: the constants defined are not directly used by the library,
248    // existing only for convenience of the developer.
249    @SuppressWarnings("unused")
250    public static final class ResponseMode {
251        /**
252         * Instructs the authorization server to send response parameters using
253         * the query portion of the redirect URI.
254         *
255         * @see "OAuth 2.0 Multiple Response Type Encoding Practices, Section 2.1
256         * <http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#rfc.section.2.1>"
257         */
258        public static final String QUERY = "query";
259
260        /**
261         * Instructs the authorization server to send response parameters using
262         * the fragment portion of the redirect URI.
263         * @see "OAuth 2.0 Multiple Response Type Encoding Practices, Section 2.1
264         * <http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#rfc.section.2.1>"
265         */
266        public static final String FRAGMENT = "fragment";
267    }
268
269    @VisibleForTesting
270    static final String PARAM_CLIENT_ID = "client_id";
271
272    @VisibleForTesting
273    static final String PARAM_CODE_CHALLENGE = "code_challenge";
274
275    @VisibleForTesting
276    static final String PARAM_CODE_CHALLENGE_METHOD = "code_challenge_method";
277
278    @VisibleForTesting
279    static final String PARAM_DISPLAY = "display";
280
281    @VisibleForTesting
282    static final String PARAM_LOGIN_HINT = "login_hint";
283
284    @VisibleForTesting
285    static final String PARAM_PROMPT = "prompt";
286
287    @VisibleForTesting
288    static final String PARAM_UI_LOCALES = "ui_locales";
289
290    @VisibleForTesting
291    static final String PARAM_REDIRECT_URI = "redirect_uri";
292
293    @VisibleForTesting
294    static final String PARAM_RESPONSE_MODE = "response_mode";
295
296    @VisibleForTesting
297    static final String PARAM_RESPONSE_TYPE = "response_type";
298
299    @VisibleForTesting
300    static final String PARAM_SCOPE = "scope";
301
302    @VisibleForTesting
303    static final String PARAM_STATE = "state";
304
305    @VisibleForTesting
306    static final String PARAM_NONCE = "nonce";
307
308    @VisibleForTesting
309    static final String PARAM_CLAIMS = "claims";
310
311    @VisibleForTesting
312    static final String PARAM_CLAIMS_LOCALES = "claims_locales";
313
314    private static final Set<String> BUILT_IN_PARAMS = builtInParams(
315            PARAM_CLIENT_ID,
316            PARAM_CODE_CHALLENGE,
317            PARAM_CODE_CHALLENGE_METHOD,
318            PARAM_DISPLAY,
319            PARAM_LOGIN_HINT,
320            PARAM_PROMPT,
321            PARAM_UI_LOCALES,
322            PARAM_REDIRECT_URI,
323            PARAM_RESPONSE_MODE,
324            PARAM_RESPONSE_TYPE,
325            PARAM_SCOPE,
326            PARAM_STATE,
327            PARAM_CLAIMS,
328            PARAM_CLAIMS_LOCALES);
329
330    private static final String KEY_CONFIGURATION = "configuration";
331    private static final String KEY_CLIENT_ID = "clientId";
332    private static final String KEY_DISPLAY = "display";
333    private static final String KEY_LOGIN_HINT = "login_hint";
334    private static final String KEY_PROMPT = "prompt";
335    private static final String KEY_UI_LOCALES = "ui_locales";
336    private static final String KEY_RESPONSE_TYPE = "responseType";
337    private static final String KEY_REDIRECT_URI = "redirectUri";
338    private static final String KEY_SCOPE = "scope";
339    private static final String KEY_STATE = "state";
340    private static final String KEY_NONCE = "nonce";
341    private static final String KEY_CODE_VERIFIER = "codeVerifier";
342    private static final String KEY_CODE_VERIFIER_CHALLENGE = "codeVerifierChallenge";
343    private static final String KEY_CODE_VERIFIER_CHALLENGE_METHOD = "codeVerifierChallengeMethod";
344    private static final String KEY_RESPONSE_MODE = "responseMode";
345    private static final String KEY_CLAIMS = "claims";
346    private static final String KEY_CLAIMS_LOCALES = "claimsLocales";
347    private static final String KEY_ADDITIONAL_PARAMETERS = "additionalParameters";
348
349    /**
350     * The service's {@link AuthorizationServiceConfiguration configuration}.
351     * This configuration specifies how to connect to a particular OAuth provider.
352     * Configurations may be
353     * {@link
354     * AuthorizationServiceConfiguration#AuthorizationServiceConfiguration(Uri, Uri, Uri, Uri)}
355     * created manually}, or {@link AuthorizationServiceConfiguration#fetchFromUrl(Uri,
356     * AuthorizationServiceConfiguration.RetrieveConfigurationCallback)} via an OpenID Connect
357     * Discovery Document}.
358     */
359    @NonNull
360    public final AuthorizationServiceConfiguration configuration;
361
362    /**
363     * The client identifier.
364     *
365     * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 4
366     * <https://tools.ietf.org/html/rfc6749#section-4>"
367     * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 4.1.1
368     * <https://tools.ietf.org/html/rfc6749#section-4.1.1>"
369     */
370    @NonNull
371    public final String clientId;
372
373    /**
374     * The OpenID Connect 1.0 `display` parameter. This is a string that specifies how the
375     * Authorization Server displays the authentication and consent user interface pages to the
376     * End-User.
377     *
378     * @see "OpenID Connect Core 1.0, Section 3.1.2.1
379     * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
380     */
381    @Nullable
382    public final String display;
383
384
385    /**
386     * The OpenID Connect 1.0 `login_hint` parameter. This is a string hint to the
387     * Authorization Server about the login identifier the End-User might use to log in, typically
388     * collected directly from the user in an identifier-first authentication flow.
389     *
390     * @see "OpenID Connect Core 1.0, Section 3.1.2.1
391     * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
392     */
393    @Nullable
394    public final String loginHint;
395
396    /**
397     * The OpenID Connect 1.0 `prompt` parameter. This is a space delimited, case sensitive
398     * list of ASCII strings that specifies whether the Authorization Server prompts the End-User
399     * for re-authentication and consent.
400     *
401     * @see Prompt
402     * @see "OpenID Connect Core 1.0, Section 3.1.2.1
403     * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
404     */
405    @Nullable
406    public final String prompt;
407
408    /**
409     * The OpenID Connect 1.0 `ui_locales` parameter. This is a space-separated list of
410     * BCP47 [RFC5646] language tag values, ordered by preference. It represents End-User's
411     * preferred languages and scripts for the user interface.
412     *
413     * @see "OpenID Connect Core 1.0, Section 3.1.2.1
414     * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
415     */
416    @Nullable
417    public final String uiLocales;
418
419    /**
420     * The expected response type.
421     *
422     * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 3.1.1
423     * <https://tools.ietf.org/html/rfc6749#section-3.1.1>"
424     * @see "OpenID Connect Core 1.0, Section 3
425     * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3>"
426     */
427    @NonNull
428    public final String responseType;
429
430    /**
431     * The client's redirect URI.
432     *
433     * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 3.1.2
434     * <https://tools.ietf.org/html/rfc6749#section-3.1.2>"
435     */
436    @NonNull
437    public final Uri redirectUri;
438
439    /**
440     * The optional set of scopes expressed as a space-delimited, case-sensitive string.
441     *
442     * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 3.1.2
443     * <https://tools.ietf.org/html/rfc6749#section-3.1.2>"
444     * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 3.3
445     * <https://tools.ietf.org/html/rfc6749#section-3.3>"
446     */
447    @Nullable
448    public final String scope;
449
450    /**
451     * An opaque value used by the client to maintain state between the request and callback. If
452     * this value is not explicitly set, this library will automatically add state and perform
453     * appropriate  validation of the state in the authorization response. It is recommended that
454     * the default implementation of this parameter be used wherever possible. Typically used to
455     * prevent CSRF attacks, as recommended in
456     * [RFC6819 Section 5.3.5](https://tools.ietf.org/html/rfc6819#section-5.3.5).
457     *
458     * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 4.1.1
459     * <https://tools.ietf.org/html/rfc6749#section-4.1.1>"
460     * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 5.3.5
461     * <https://tools.ietf.org/html/rfc6749#section-5.3.5>"
462     */
463    @Nullable
464    public final String state;
465
466    /**
467     * String value used to associate a Client session with an ID Token, and to mitigate replay
468     * attacks. The value is passed through unmodified from the Authentication Request to the ID
469     * Token. If this value is not explicitly set, this library will automatically add nonce and
470     * perform appropriate validation of the ID Token. It is recommended that the default
471     * implementation of this parameter be used wherever possible.
472     *
473     * @see "OpenID Connect Core 1.0, Section 3.1.2.1
474     * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
475     */
476    @Nullable
477    public final String nonce;
478
479    /**
480     * The proof key for code exchange. This is an opaque value used to associate an authorization
481     * request with a subsequent code exchange, in order to prevent any eavesdropping party from
482     * intercepting and using the code before the original requestor. If PKCE is disabled due to
483     * a non-compliant authorization server which rejects requests with PKCE parameters present,
484     * this value will be `null`.
485     *
486     * @see Builder#setCodeVerifier(String)
487     * @see Builder#setCodeVerifier(String, String, String)
488     * @see "Proof Key for Code Exchange by OAuth Public Clients (RFC 7636)
489     * <https://tools.ietf.org/html/rfc7636>"
490     */
491    @Nullable
492    public final String codeVerifier;
493
494    /**
495     * The challenge derived from the {@link #codeVerifier code verifier}, using the
496     * {@link #codeVerifierChallengeMethod challenge method}. If a code verifier is not being
497     * used for this request, this value will be `null`.
498     *
499     * @see Builder#setCodeVerifier(String)
500     * @see Builder#setCodeVerifier(String, String, String)
501     * @see "Proof Key for Code Exchange by OAuth Public Clients (RFC 7636)
502     * <https://tools.ietf.org/html/rfc7636>"
503     */
504    @Nullable
505    public final String codeVerifierChallenge;
506
507    /**
508     * The challenge method used to generate a {@link #codeVerifierChallenge challenge} from
509     * the {@link #codeVerifier code verifier}. If a code verifier is not being used for this
510     * request, this value will be `null`.
511     *
512     * @see Builder#setCodeVerifier(String)
513     * @see Builder#setCodeVerifier(String, String, String)
514     * @see "Proof Key for Code Exchange by OAuth Public Clients (RFC 7636)
515     * <https://tools.ietf.org/html/rfc7636>"
516     */
517    @Nullable
518    public final String codeVerifierChallengeMethod;
519
520    /**
521     * Instructs the authorization service on the mechanism to be used for returning
522     * response parameters from the authorization endpoint. This use of this parameter is
523     * _not recommended_ when the response mode that would be requested is the default mode
524     * specified for the response type.
525     *
526     * @see "OpenID Connect Core 1.0, Section 3.1.2.1
527     * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
528     */
529    @Nullable
530    public final String responseMode;
531
532    /**
533     * Requests that specific Claims be returned.
534     * The value is a JSON object listing the requested Claims.
535     *
536     * @see "OpenID Connect Core 1.0, Section 5.5
537     * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.5.5>"
538     */
539    @Nullable
540    public final JSONObject claims;
541
542    /**
543     * End-User's preferred languages and scripts for Claims being returned, represented as a
544     * space-separated list of BCP47 [RFC5646] language tag values, ordered by preference.
545     *
546     * @see "OpenID Connect Core 1.0, Section 5.2
547     * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.5.2>"
548     */
549    @Nullable
550    public final String claimsLocales;
551
552    /**
553     * Additional parameters to be passed as part of the request.
554     *
555     * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 3.1
556     * <https://tools.ietf.org/html/rfc6749#section-3.1>"
557     */
558    @NonNull
559    public final Map<String, String> additionalParameters;
560
561    /**
562     * Creates instances of {@link AuthorizationRequest}.
563     */
564    public static final class Builder {
565
566        // SuppressWarnings justification: static analysis incorrectly determines that this field
567        // is not initialized, as it is indirectly initialized by setConfiguration
568        @NonNull
569        @SuppressWarnings("NullableProblems")
570        private AuthorizationServiceConfiguration mConfiguration;
571
572        // SuppressWarnings justification: static analysis incorrectly determines that this field
573        // is not initialized, as it is indirectly initialized by setClientId
574        @NonNull
575        @SuppressWarnings("NullableProblems")
576        private String mClientId;
577
578        @Nullable
579        private String mDisplay;
580
581        @Nullable
582        private String mLoginHint;
583
584        @Nullable
585        private String mPrompt;
586
587        @Nullable
588        private String mUiLocales;
589
590        // SuppressWarnings justification: static analysis incorrectly determines that this field
591        // is not initialized, as it is indirectly initialized by setResponseType
592        @NonNull
593        @SuppressWarnings("NullableProblems")
594        private String mResponseType;
595
596        // SuppressWarnings justification: static analysis incorrectly determines that this field
597        // is not initialized, as it is indirectly initialized by setRedirectUri
598        @NonNull
599        @SuppressWarnings("NullableProblems")
600        private Uri mRedirectUri;
601
602        @Nullable
603        private String mScope;
604
605        @Nullable
606        private String mState;
607
608        @Nullable
609        private String mNonce;
610
611        @Nullable
612        private String mCodeVerifier;
613
614        @Nullable
615        private String mCodeVerifierChallenge;
616
617        @Nullable
618        private String mCodeVerifierChallengeMethod;
619
620        @Nullable
621        private String mResponseMode;
622
623        @Nullable
624        private JSONObject mClaims;
625
626        @Nullable
627        private String mClaimsLocales;
628
629        @NonNull
630        private Map<String, String> mAdditionalParameters = new HashMap<>();
631
632        /**
633         * Creates an authorization request builder with the specified mandatory properties,
634         * and preset values for {@link AuthorizationRequest#state},
635         * {@link AuthorizationRequest#nonce} and {@link AuthorizationRequest#codeVerifier}.
636         */
637        public Builder(
638                @NonNull AuthorizationServiceConfiguration configuration,
639                @NonNull String clientId,
640                @NonNull String responseType,
641                @NonNull Uri redirectUri) {
642            setAuthorizationServiceConfiguration(configuration);
643            setClientId(clientId);
644            setResponseType(responseType);
645            setRedirectUri(redirectUri);
646            setState(AuthorizationManagementUtil.generateRandomState());
647            setNonce(AuthorizationManagementUtil.generateRandomState());
648            setCodeVerifier(CodeVerifierUtil.generateRandomCodeVerifier());
649        }
650
651        /**
652         * Specifies the service configuration to be used in dispatching this request.
653         */
654        public Builder setAuthorizationServiceConfiguration(
655                @NonNull AuthorizationServiceConfiguration configuration) {
656            mConfiguration = checkNotNull(configuration,
657                    "configuration cannot be null");
658            return this;
659        }
660
661        /**
662         * Specifies the client ID. Cannot be null or empty.
663         *
664         * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 4
665         * <https://tools.ietf.org/html/rfc6749#section-4>"
666         * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 4.1.1
667         * <https://tools.ietf.org/html/rfc6749#section-4.1.1>"
668         */
669        @NonNull
670        public Builder setClientId(@NonNull String clientId) {
671            mClientId = checkNotEmpty(clientId, "client ID cannot be null or empty");
672            return this;
673        }
674
675        /**
676         * Specifies the OpenID Connect 1.0 `display` parameter.
677         *
678         * @see Display
679         * @see "OpenID Connect Core 1.0, Section 3.1.2.1
680         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
681         */
682        public Builder setDisplay(@Nullable String display) {
683            mDisplay = checkNullOrNotEmpty(display, "display must be null or not empty");
684            return this;
685        }
686
687        /**
688         * Specifies the OpenID Connect 1.0 `login_hint` parameter.
689         *
690         * @see "OpenID Connect Core 1.0, Section 3.1.2.1
691         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
692         */
693        public Builder setLoginHint(@Nullable String loginHint) {
694            mLoginHint = checkNullOrNotEmpty(loginHint, "login hint must be null or not empty");
695            return this;
696        }
697
698        /**
699         * Specifies the encoded OpenID Connect 1.0 `prompt` parameter, which is a
700         * space-delimited set of case sensitive ASCII prompt values. Replaces any previously
701         * specified prompt values.
702         *
703         * @see Prompt
704         * @see "OpenID Connect Core 1.0, Section 3.1.2.1
705         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
706         */
707        @NonNull
708        public Builder setPrompt(@Nullable String prompt) {
709            mPrompt = checkNullOrNotEmpty(prompt, "prompt must be null or non-empty");
710            return this;
711        }
712
713        /**
714         * Specifies the set of OpenID Connect 1.0 `prompt` parameter values, which are
715         * space-delimited, case sensitive ASCII prompt values. Replaces any previously
716         * specified prompt values.
717         *
718         * @see Prompt
719         * @see "OpenID Connect Core 1.0, Section 3.1.2.1
720         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
721         */
722        @NonNull
723        public Builder setPromptValues(@Nullable String... promptValues) {
724            if (promptValues == null) {
725                mPrompt = null;
726                return this;
727            }
728
729            return setPromptValues(Arrays.asList(promptValues));
730        }
731
732        /**
733         * Specifies the set of OpenID Connect 1.0 `prompt` parameter values, which are
734         * space-delimited, case sensitive ASCII prompt values. Replaces any previously
735         * specified prompt values.
736         *
737         * @see Prompt
738         * @see "OpenID Connect Core 1.0, Section 3.1.2.1
739         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
740         */
741        @NonNull
742        public Builder setPromptValues(@Nullable Iterable<String> promptValues) {
743            mPrompt = AsciiStringListUtil.iterableToString(promptValues);
744            return this;
745        }
746
747        /**
748         * Specifies the OpenID Connect 1.0 `ui_locales` parameter, which is a space-separated list
749         * of BCP47 [RFC5646] language tag values, ordered by preference. It represents End-User's
750         * preferred languages and scripts for the user interface. Replaces any previously
751         * specified ui_locales values.
752         *
753         * @see "OpenID Connect Core 1.0, Section 3.1.2.1
754         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
755         */
756        public Builder setUiLocales(@Nullable String uiLocales) {
757            mUiLocales = checkNullOrNotEmpty(uiLocales, "uiLocales must be null or not empty");
758            return this;
759        }
760
761        /**
762         * Specifies the OpenID Connect 1.0 `ui_locales` parameter, which is a space-separated list
763         * of BCP47 [RFC5646] language tag values, ordered by preference. It represents End-User's
764         * preferred languages and scripts for the user interface. Replaces any previously
765         * specified ui_locales values.
766         *
767         * @see "OpenID Connect Core 1.0, Section 3.1.2.1
768         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
769         */
770        @NonNull
771        public Builder setUiLocalesValues(@Nullable String... uiLocalesValues) {
772            if (uiLocalesValues == null) {
773                mUiLocales = null;
774                return this;
775            }
776
777            return setUiLocalesValues(Arrays.asList(uiLocalesValues));
778        }
779
780        /**
781         * Specifies the OpenID Connect 1.0 `ui_locales` parameter, which is a space-separated list
782         * of BCP47 [RFC5646] language tag values, ordered by preference. It represents End-User's
783         * preferred languages and scripts for the user interface. Replaces any previously
784         * specified ui_locales values.
785         *
786         * @see "OpenID Connect Core 1.0, Section 3.1.2.1
787         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
788         */
789        @NonNull
790        public Builder setUiLocalesValues(@Nullable Iterable<String> uiLocalesValues) {
791            mUiLocales = AsciiStringListUtil.iterableToString(uiLocalesValues);
792            return this;
793        }
794
795        /**
796         * Specifies the expected response type. Cannot be null or empty.
797         *
798         * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 2.2
799         * <https://tools.ietf.org/html/rfc6749#section-2.2>"
800         * @see "OpenID Connect Core 1.0, Section 3
801         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3>"
802         */
803        @NonNull
804        public Builder setResponseType(@NonNull String responseType) {
805            mResponseType = checkNotEmpty(responseType,
806                    "expected response type cannot be null or empty");
807            return this;
808        }
809
810        /**
811         * Specifies the client's redirect URI. Cannot be null or empty.
812         *
813         * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 3.1.2
814         * <https://tools.ietf.org/html/rfc6749#section-3.1.2>"
815         */
816        @NonNull
817        public Builder setRedirectUri(@NonNull Uri redirectUri) {
818            mRedirectUri = checkNotNull(redirectUri, "redirect URI cannot be null or empty");
819            return this;
820        }
821
822        /**
823         * Specifies the encoded scope string, which is a space-delimited set of
824         * case-sensitive scope identifiers. Replaces any previously specified scope.
825         *
826         * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 3.3
827         * <https://tools.ietf.org/html/rfc6749#section-3.3>"
828         */
829        @NonNull
830        public Builder setScope(@Nullable String scope) {
831            if (TextUtils.isEmpty(scope)) {
832                mScope = null;
833            } else {
834                setScopes(scope.split(" +"));
835            }
836            return this;
837        }
838
839        /**
840         * Specifies the set of case-sensitive scopes. Replaces any previously specified set of
841         * scopes. If no arguments are provided, the scope string will be set to `null`.
842         * Individual scope strings cannot be null or empty.
843         *
844         * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 3.3
845         * <https://tools.ietf.org/html/rfc6749#section-3.3>"
846         */
847        @NonNull
848        public Builder setScopes(String... scopes) {
849            if (scopes == null) {
850                scopes = new String[0];
851            }
852            setScopes(Arrays.asList(scopes));
853            return this;
854        }
855
856        /**
857         * Specifies the set of case-sensitive scopes. Replaces any previously specified set of
858         * scopes. If the iterable is empty, the scope string will be set to `null`.
859         * Individual scope strings cannot be null or empty.
860         *
861         * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 3.3
862         * <https://tools.ietf.org/html/rfc6749#section-3.3>"
863         */
864        @NonNull
865        public Builder setScopes(@Nullable Iterable<String> scopes) {
866            mScope = AsciiStringListUtil.iterableToString(scopes);
867            return this;
868        }
869
870        /**
871         * Specifies the opaque value used by the client to maintain state between the request and
872         * callback. If this value is not explicitly set, this library will automatically add state
873         * and perform appropriate validation of the state in the authorization response. It is
874         * recommended that the default implementation of this parameter be used wherever possible.
875         * Typically used to prevent CSRF attacks, as recommended in
876         * [RFC6819 Section 5.3.5](https://tools.ietf.org/html/rfc6819#section-5.3.5).
877         *
878         * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 4.1.1
879         * <https://tools.ietf.org/html/rfc6749#section-4.1.1>"
880         * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 5.3.5
881         * <https://tools.ietf.org/html/rfc6749#section-5.3.5>"
882         */
883        @NonNull
884        public Builder setState(@Nullable String state) {
885            mState = checkNullOrNotEmpty(state, "state cannot be empty if defined");
886            return this;
887        }
888
889        /**
890         * Specifies the String value used to associate a Client session with an ID Token, and to
891         * mitigate replay attacks. The value is passed through unmodified from the Authentication
892         * Request to the ID Token. If this value is not explicitly set, this library will
893         * automatically add nonce and perform appropriate validation of the ID Token. It is
894         * recommended that the default implementation of this parameter be used wherever possible.
895         *
896         * @see "OpenID Connect Core 1.0, Section 3.1.2.1
897         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
898         */
899        @NonNull
900        public Builder setNonce(@Nullable String nonce) {
901            mNonce = checkNullOrNotEmpty(nonce, "nonce cannot be empty if defined");
902            return this;
903        }
904
905        /**
906         * Specifies the code verifier to use for this authorization request. The default challenge
907         * method (typically {@link #CODE_CHALLENGE_METHOD_S256}) implemented by
908         * {@link CodeVerifierUtil} will be used, and a challenge will be generated using this
909         * method. If the use of a code verifier is not desired, set the code verifier
910         * to `null`.
911         *
912         * @see "Proof Key for Code Exchange by OAuth Public Clients (RFC 7636), Section 4.3
913         * <https://tools.ietf.org/html/rfc7636#section-4.3>"
914         */
915        @NonNull
916        public Builder setCodeVerifier(@Nullable String codeVerifier) {
917            if (codeVerifier != null) {
918                CodeVerifierUtil.checkCodeVerifier(codeVerifier);
919                mCodeVerifier = codeVerifier;
920                mCodeVerifierChallenge = CodeVerifierUtil.deriveCodeVerifierChallenge(codeVerifier);
921                mCodeVerifierChallengeMethod = CodeVerifierUtil.getCodeVerifierChallengeMethod();
922            } else {
923                mCodeVerifier = null;
924                mCodeVerifierChallenge = null;
925                mCodeVerifierChallengeMethod = null;
926            }
927
928            return this;
929        }
930
931        /**
932         * Specifies the code verifier, challenge and method strings to use for this authorization
933         * request. If these values are not explicitly set, they will be automatically generated
934         * and used. It is recommended that this default behavior be used wherever possible. If
935         * a null code verifier is set (to indicate that a code verifier is not to be used), then
936         * the challenge and method must also be null. If a non-null code verifier is set, the
937         * code verifier challenge and method must also be set.
938         *
939         * @see "Proof Key for Code Exchange by OAuth Public Clients (RFC 7636), Section 4.3
940         * <https://tools.ietf.org/html/rfc7636#section-4.3>"
941         */
942        @NonNull
943        public Builder setCodeVerifier(
944                @Nullable String codeVerifier,
945                @Nullable String codeVerifierChallenge,
946                @Nullable String codeVerifierChallengeMethod) {
947            if (codeVerifier != null) {
948                CodeVerifierUtil.checkCodeVerifier(codeVerifier);
949                checkNotEmpty(codeVerifierChallenge,
950                        "code verifier challenge cannot be null or empty if verifier is set");
951                checkNotEmpty(codeVerifierChallengeMethod,
952                        "code verifier challenge method cannot be null or empty if verifier "
953                                + "is set");
954            } else {
955                checkArgument(codeVerifierChallenge == null,
956                        "code verifier challenge must be null if verifier is null");
957                checkArgument(codeVerifierChallengeMethod == null,
958                        "code verifier challenge method must be null if verifier is null");
959            }
960
961            mCodeVerifier = codeVerifier;
962            mCodeVerifierChallenge = codeVerifierChallenge;
963            mCodeVerifierChallengeMethod = codeVerifierChallengeMethod;
964
965            return this;
966        }
967
968        /**
969         * Specifies the response mode to be used for returning authorization response parameters
970         * from the authorization endpoint.
971         *
972         * @see "OpenID Connect Core 1.0, Section 3.1.2.1
973         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.3.1.2.1>"
974         * @see "OAuth 2.0 Multiple Response Type Encoding Practices, Section 2
975         * <http://openid.net/specs/oauth-v2-multiple-response-types-1_0.html#rfc.section.2>"
976         */
977        @NonNull
978        public Builder setResponseMode(@Nullable String responseMode) {
979            checkNullOrNotEmpty(responseMode, "responseMode must not be empty");
980            mResponseMode = responseMode;
981            return this;
982        }
983
984        /**
985         * Requests that specific Claims be returned.
986         * The value is a JSON object listing the requested Claims.
987         *
988         * @see "OpenID Connect Core 1.0, Section 5.5
989         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.5.5>"
990         */
991        @NonNull
992        public Builder setClaims(@Nullable JSONObject claims) {
993            mClaims = claims;
994            return this;
995        }
996
997        /**
998         * End-User's preferred languages and scripts for Claims being returned, represented as a
999         * space-separated list of BCP47 [RFC5646] language tag values, ordered by preference.
1000         *
1001         * @see "OpenID Connect Core 1.0, Section 5.2
1002         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.5.2>"
1003         */
1004        public Builder setClaimsLocales(@Nullable String claimsLocales) {
1005            mClaimsLocales = checkNullOrNotEmpty(
1006                    claimsLocales,
1007                    "claimsLocales must be null or not empty");
1008            return this;
1009        }
1010
1011        /**
1012         * End-User's preferred languages and scripts for Claims being returned, represented as a
1013         * space-separated list of BCP47 [RFC5646] language tag values, ordered by preference.
1014         *
1015         * @see "OpenID Connect Core 1.0, Section 5.2
1016         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.5.2>"
1017         */
1018        @NonNull
1019        public Builder setClaimsLocalesValues(@Nullable String... claimsLocalesValues) {
1020            if (claimsLocalesValues == null) {
1021                mClaimsLocales = null;
1022                return this;
1023            }
1024
1025            return setClaimsLocalesValues(Arrays.asList(claimsLocalesValues));
1026        }
1027
1028        /**
1029         * End-User's preferred languages and scripts for Claims being returned, represented as a
1030         * space-separated list of BCP47 [RFC5646] language tag values, ordered by preference.
1031         *
1032         * @see "OpenID Connect Core 1.0, Section 5.2
1033         * <https://openid.net/specs/openid-connect-core-1_0.html#rfc.section.5.2>"
1034         */
1035        @NonNull
1036        public Builder setClaimsLocalesValues(@Nullable Iterable<String> claimsLocalesValues) {
1037            mClaimsLocales = AsciiStringListUtil.iterableToString(claimsLocalesValues);
1038            return this;
1039        }
1040
1041        /**
1042         * Specifies additional parameters. Replaces any previously provided set of parameters.
1043         * Parameter keys and values cannot be null or empty.
1044         *
1045         * @see "The OAuth 2.0 Authorization Framework (RFC 6749), Section 3.1
1046         * <https://tools.ietf.org/html/rfc6749#section-3.1>"
1047         */
1048        @NonNull
1049        public Builder setAdditionalParameters(@Nullable Map<String, String> additionalParameters) {
1050            mAdditionalParameters = checkAdditionalParams(additionalParameters, BUILT_IN_PARAMS);
1051            return this;
1052        }
1053
1054        /**
1055         * Constructs the authorization request. At a minimum the following fields must have been
1056         * set:
1057         *
1058         * - The client ID
1059         * - The expected response type
1060         * - The redirect URI
1061         *
1062         * Failure to specify any of these parameters will result in a runtime exception.
1063         */
1064        @NonNull
1065        public AuthorizationRequest build() {
1066            return new AuthorizationRequest(
1067                    mConfiguration,
1068                    mClientId,
1069                    mResponseType,
1070                    mRedirectUri,
1071                    mDisplay,
1072                    mLoginHint,
1073                    mPrompt,
1074                    mUiLocales,
1075                    mScope,
1076                    mState,
1077                    mNonce,
1078                    mCodeVerifier,
1079                    mCodeVerifierChallenge,
1080                    mCodeVerifierChallengeMethod,
1081                    mResponseMode,
1082                    mClaims,
1083                    mClaimsLocales,
1084                    Collections.unmodifiableMap(new HashMap<>(mAdditionalParameters)));
1085        }
1086    }
1087
1088    private AuthorizationRequest(
1089            @NonNull AuthorizationServiceConfiguration configuration,
1090            @NonNull String clientId,
1091            @NonNull String responseType,
1092            @NonNull Uri redirectUri,
1093            @Nullable String display,
1094            @Nullable String loginHint,
1095            @Nullable String prompt,
1096            @Nullable String uiLocales,
1097            @Nullable String scope,
1098            @Nullable String state,
1099            @Nullable String nonce,
1100            @Nullable String codeVerifier,
1101            @Nullable String codeVerifierChallenge,
1102            @Nullable String codeVerifierChallengeMethod,
1103            @Nullable String responseMode,
1104            @Nullable JSONObject claims,
1105            @Nullable String claimsLocales,
1106            @NonNull Map<String, String> additionalParameters) {
1107        // mandatory fields
1108        this.configuration = configuration;
1109        this.clientId = clientId;
1110        this.responseType = responseType;
1111        this.redirectUri = redirectUri;
1112        this.additionalParameters = additionalParameters;
1113
1114        // optional fields
1115        this.display = display;
1116        this.loginHint = loginHint;
1117        this.prompt = prompt;
1118        this.uiLocales = uiLocales;
1119        this.scope = scope;
1120        this.state = state;
1121        this.nonce = nonce;
1122        this.codeVerifier = codeVerifier;
1123        this.codeVerifierChallenge = codeVerifierChallenge;
1124        this.codeVerifierChallengeMethod = codeVerifierChallengeMethod;
1125        this.responseMode = responseMode;
1126        this.claims = claims;
1127        this.claimsLocales = claimsLocales;
1128    }
1129
1130    /**
1131     * Derives the set of scopes from the consolidated, space-delimited scopes in the
1132     * {@link #scope} field. If no scopes were specified for this request, the method will
1133     * return `null`.
1134     */
1135    @Nullable
1136    public Set<String> getScopeSet() {
1137        return AsciiStringListUtil.stringToSet(scope);
1138    }
1139
1140    /**
1141     * Derives the set of prompt values from the consolidated, space-delimited prompt values in
1142     * the {@link #prompt} field. If no prompt values were specified for this request, the method
1143     * will return `null`.
1144     */
1145    public Set<String> getPromptValues() {
1146        return AsciiStringListUtil.stringToSet(prompt);
1147    }
1148
1149    /**
1150     * Derives the set of ui_locales values from the consolidated, space-separated list of
1151     * BCP47 [RFC5646] language tag values in the {@link #uiLocales} field. If no ui_locales values
1152     * were specified for this request, the method will return `null`.
1153     */
1154    public Set<String> getUiLocales() {
1155        return AsciiStringListUtil.stringToSet(uiLocales);
1156    }
1157
1158    @Override
1159    @Nullable
1160    public String getState() {
1161        return state;
1162    }
1163
1164    /**
1165     * Derives the set of claims_locales values from the consolidated, space-separated list of
1166     * BCP47 [RFC5646] language tag values in the {@link #claimsLocales} field. If no claims_locales
1167     * values were specified for this request, the method will return `null`.
1168     */
1169    public Set<String> getClaimsLocales() {
1170        return AsciiStringListUtil.stringToSet(claimsLocales);
1171    }
1172
1173    /**
1174     * Produces a request URI, that can be used to dispatch the authorization request.
1175     */
1176    @Override
1177    @NonNull
1178    public Uri toUri() {
1179        Uri.Builder uriBuilder = configuration.authorizationEndpoint.buildUpon()
1180                .appendQueryParameter(PARAM_REDIRECT_URI, redirectUri.toString())
1181                .appendQueryParameter(PARAM_CLIENT_ID, clientId)
1182                .appendQueryParameter(PARAM_RESPONSE_TYPE, responseType);
1183
1184        UriUtil.appendQueryParameterIfNotNull(uriBuilder, PARAM_DISPLAY, display);
1185        UriUtil.appendQueryParameterIfNotNull(uriBuilder, PARAM_LOGIN_HINT, loginHint);
1186        UriUtil.appendQueryParameterIfNotNull(uriBuilder, PARAM_PROMPT, prompt);
1187        UriUtil.appendQueryParameterIfNotNull(uriBuilder, PARAM_UI_LOCALES, uiLocales);
1188        UriUtil.appendQueryParameterIfNotNull(uriBuilder, PARAM_STATE, state);
1189        UriUtil.appendQueryParameterIfNotNull(uriBuilder, PARAM_NONCE, nonce);
1190        UriUtil.appendQueryParameterIfNotNull(uriBuilder, PARAM_SCOPE, scope);
1191        UriUtil.appendQueryParameterIfNotNull(uriBuilder, PARAM_RESPONSE_MODE, responseMode);
1192
1193        if (codeVerifier != null) {
1194            uriBuilder.appendQueryParameter(PARAM_CODE_CHALLENGE, codeVerifierChallenge)
1195                    .appendQueryParameter(PARAM_CODE_CHALLENGE_METHOD, codeVerifierChallengeMethod);
1196        }
1197
1198        UriUtil.appendQueryParameterIfNotNull(uriBuilder, PARAM_CLAIMS, claims);
1199        UriUtil.appendQueryParameterIfNotNull(uriBuilder, PARAM_CLAIMS_LOCALES, claimsLocales);
1200
1201        for (Entry<String, String> entry : additionalParameters.entrySet()) {
1202            uriBuilder.appendQueryParameter(entry.getKey(), entry.getValue());
1203        }
1204
1205        return uriBuilder.build();
1206    }
1207
1208    /**
1209     * Produces a JSON representation of the authorization request for persistent storage or local
1210     * transmission (e.g. between activities).
1211     */
1212    @Override
1213    @NonNull
1214    public JSONObject jsonSerialize() {
1215        JSONObject json = new JSONObject();
1216        JsonUtil.put(json, KEY_CONFIGURATION, configuration.toJson());
1217        JsonUtil.put(json, KEY_CLIENT_ID, clientId);
1218        JsonUtil.put(json, KEY_RESPONSE_TYPE, responseType);
1219        JsonUtil.put(json, KEY_REDIRECT_URI, redirectUri.toString());
1220        JsonUtil.putIfNotNull(json, KEY_DISPLAY, display);
1221        JsonUtil.putIfNotNull(json, KEY_LOGIN_HINT, loginHint);
1222        JsonUtil.putIfNotNull(json, KEY_SCOPE, scope);
1223        JsonUtil.putIfNotNull(json, KEY_PROMPT, prompt);
1224        JsonUtil.putIfNotNull(json, KEY_UI_LOCALES, uiLocales);
1225        JsonUtil.putIfNotNull(json, KEY_STATE, state);
1226        JsonUtil.putIfNotNull(json, KEY_NONCE, nonce);
1227        JsonUtil.putIfNotNull(json, KEY_CODE_VERIFIER, codeVerifier);
1228        JsonUtil.putIfNotNull(json, KEY_CODE_VERIFIER_CHALLENGE, codeVerifierChallenge);
1229        JsonUtil.putIfNotNull(json, KEY_CODE_VERIFIER_CHALLENGE_METHOD,
1230                codeVerifierChallengeMethod);
1231        JsonUtil.putIfNotNull(json, KEY_RESPONSE_MODE, responseMode);
1232        JsonUtil.putIfNotNull(json, KEY_CLAIMS, claims);
1233        JsonUtil.putIfNotNull(json, KEY_CLAIMS_LOCALES, claimsLocales);
1234        JsonUtil.put(json, KEY_ADDITIONAL_PARAMETERS,
1235                JsonUtil.mapToJsonObject(additionalParameters));
1236        return json;
1237    }
1238
1239    /**
1240     * Produces a JSON string representation of the request for persistent storage or
1241     * local transmission (e.g. between activities). This method is just a convenience wrapper
1242     * for {@link #jsonSerialize()}, converting the JSON object to its string form.
1243     */
1244    @Override
1245    public String jsonSerializeString() {
1246        return jsonSerialize().toString();
1247    }
1248
1249    /**
1250     * Reads an authorization request from a JSON string representation produced by
1251     * {@link #jsonSerialize()}.
1252     * @throws JSONException if the provided JSON does not match the expected structure.
1253     */
1254    @NonNull
1255    public static AuthorizationRequest jsonDeserialize(@NonNull JSONObject json)
1256            throws JSONException {
1257        checkNotNull(json, "json cannot be null");
1258        return new AuthorizationRequest(
1259                AuthorizationServiceConfiguration.fromJson(json.getJSONObject(KEY_CONFIGURATION)),
1260                JsonUtil.getString(json, KEY_CLIENT_ID),
1261                JsonUtil.getString(json, KEY_RESPONSE_TYPE),
1262                JsonUtil.getUri(json, KEY_REDIRECT_URI),
1263                JsonUtil.getStringIfDefined(json, KEY_DISPLAY),
1264                JsonUtil.getStringIfDefined(json, KEY_LOGIN_HINT),
1265                JsonUtil.getStringIfDefined(json, KEY_PROMPT),
1266                JsonUtil.getStringIfDefined(json, KEY_UI_LOCALES),
1267                JsonUtil.getStringIfDefined(json, KEY_SCOPE),
1268                JsonUtil.getStringIfDefined(json, KEY_STATE),
1269                JsonUtil.getStringIfDefined(json, KEY_NONCE),
1270                JsonUtil.getStringIfDefined(json, KEY_CODE_VERIFIER),
1271                JsonUtil.getStringIfDefined(json, KEY_CODE_VERIFIER_CHALLENGE),
1272                JsonUtil.getStringIfDefined(json, KEY_CODE_VERIFIER_CHALLENGE_METHOD),
1273                JsonUtil.getStringIfDefined(json, KEY_RESPONSE_MODE),
1274                JsonUtil.getJsonObjectIfDefined(json, KEY_CLAIMS),
1275                JsonUtil.getStringIfDefined(json, KEY_CLAIMS_LOCALES),
1276                JsonUtil.getStringMap(json, KEY_ADDITIONAL_PARAMETERS));
1277    }
1278
1279    /**
1280     * Reads an authorization request from a JSON string representation produced by
1281     * {@link #jsonSerializeString()}. This method is just a convenience wrapper for
1282     * {@link #jsonDeserialize(JSONObject)}, converting the JSON string to its JSON object form.
1283     * @throws JSONException if the provided JSON does not match the expected structure.
1284     */
1285    @NonNull
1286    public static AuthorizationRequest jsonDeserialize(@NonNull String jsonStr)
1287            throws JSONException {
1288        checkNotNull(jsonStr, "json string cannot be null");
1289        return jsonDeserialize(new JSONObject(jsonStr));
1290    }
1291}