001/*
002 * oauth2-oidc-sdk
003 *
004 * Copyright 2012-2023, Connect2id Ltd and contributors.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.oauth2.sdk;
019
020
021import com.nimbusds.oauth2.sdk.id.Identifier;
022import com.nimbusds.oauth2.sdk.util.MultivaluedMapUtils;
023import net.jcip.annotations.Immutable;
024
025import java.util.*;
026
027
028/**
029 * Authorisation grant type.
030 */
031@Immutable
032public final class GrantType extends Identifier {
033
034        
035        /**
036         * Authorisation code, as specified in RFC 6749.
037         */
038        public static final GrantType AUTHORIZATION_CODE = new GrantType("authorization_code", "code", false, true, ParameterRequirement.NOT_ALLOWED, new HashSet<>(Arrays.asList("code", "redirect_uri", "code_verifier")));
039
040
041        /**
042         * Implicit, as specified in RFC 6749.
043         */
044        public static final GrantType IMPLICIT = new GrantType("implicit", false, true, ParameterRequirement.NOT_ALLOWED, Collections.<String>emptySet());
045        
046        
047        /**
048         * Refresh token, as specified in RFC 6749.
049         */
050        public static final GrantType REFRESH_TOKEN = new GrantType("refresh_token", "refresh token", false, false, ParameterRequirement.OPTIONAL, Collections.singleton("refresh_token"));
051
052
053        /**
054         * Password, as specified in RFC 6749.
055         */
056        public static final GrantType PASSWORD = new GrantType("password", false, false, ParameterRequirement.OPTIONAL, new HashSet<>(Arrays.asList("username", "password")));
057
058
059        /**
060         * Client credentials, as specified in RFC 6749.
061         */
062        public static final GrantType CLIENT_CREDENTIALS = new GrantType(
063                "client_credentials",
064                "client credentials", true, true, ParameterRequirement.OPTIONAL,
065                Collections.<String>emptySet());
066
067
068        /**
069         * JWT bearer, as specified in RFC 7523.
070         */
071        public static final GrantType JWT_BEARER = new GrantType(
072                "urn:ietf:params:oauth:grant-type:jwt-bearer",
073                "JWT bearer", false, false, ParameterRequirement.OPTIONAL,
074                Collections.singleton("assertion"));
075
076
077        /**
078         * SAML 2.0 bearer, as specified in RFC 7522.
079         */
080        public static final GrantType SAML2_BEARER = new GrantType(
081                "urn:ietf:params:oauth:grant-type:saml2-bearer",
082                "SAML 2.0 bearer", false, false, ParameterRequirement.OPTIONAL,
083                Collections.singleton("assertion"));
084
085
086        /**
087         * Device authorisation grant, as specified in RFC 8628.
088         */
089        public static final GrantType DEVICE_CODE = new GrantType(
090                "urn:ietf:params:oauth:grant-type:device_code",
091                "device code", false, true, ParameterRequirement.NOT_ALLOWED,
092                Collections.singleton("device_code"));
093
094
095        /**
096         * Client Initiated Back-channel Authentication (CIBA), as specified in
097         * OpenID Connect Client Initiated Backchannel Authentication Flow -
098         * Core 1.0.
099         */
100        public static final GrantType CIBA = new GrantType(
101                "urn:openid:params:grant-type:ciba",
102                "CIBA", true, true, ParameterRequirement.NOT_ALLOWED,
103                Collections.singleton("auth_req_id"));
104
105        
106        /**
107         * Token exchange, as specified in RFC 8693.
108         */
109        public static final GrantType TOKEN_EXCHANGE = new GrantType(
110                "urn:ietf:params:oauth:grant-type:token-exchange",
111                "token exchange", false, false,  ParameterRequirement.OPTIONAL,
112                new HashSet<>(Arrays.asList(
113                                "audience", "requested_token_type", "subject_token", "subject_token_type", "actor_token", "actor_token_type"
114                )));
115        
116        
117        private static final long serialVersionUID = -5367937758427680765L;
118
119
120        /**
121         * Short name for the grant.
122         */
123        private final String shortName;
124        
125        
126        /**
127         * The client authentication requirement.
128         */
129        private final boolean requiresClientAuth;
130
131
132        /**
133         * The client identifier requirement.
134         */
135        private final boolean requiresClientID;
136
137
138        /**
139         * The scope parameter requirement in token requests.
140         */
141        private final ParameterRequirement scopeRequirementInTokenRequest;
142
143
144        /**
145         * The names of the token request parameters specific to this grant
146         * type.
147         */
148        private final Set<String> requestParamNames;
149
150
151        /**
152         * Creates a new OAuth 2.0 authorisation grant type with the specified
153         * value. The client authentication and identifier requirements are set
154         * to {@code false}. The scope parameter in token requests is not
155         * allowed.
156         *
157         * @param value The authorisation grant type value. Must not be
158         *              {@code null} or empty string.
159         */
160        public GrantType(final String value) {
161
162                this(value, false, false, ParameterRequirement.NOT_ALLOWED, Collections.<String>emptySet());
163        }
164
165
166        /**
167         * Creates a new OAuth 2.0 authorisation grant type with the specified
168         * value.
169         *
170         * @param value                          The authorisation grant type
171         *                                       value. Must not be
172         *                                       {@code null} or empty string.
173         * @param requiresClientAuth             The client authentication
174         *                                       requirement.
175         * @param requiresClientID               The client identifier
176         *                                       requirement.
177         * @param scopeRequirementInTokenRequest The scope parameter
178         *                                       requirement in token requests.
179         *                                       Must not be {@code null}.
180         * @param requestParamNames              The names of the token request
181         *                                       parameters specific to this
182         *                                       grant type, empty set or
183         *                                       {@code null} if none.
184         */
185        private GrantType(final String value,
186                          final boolean requiresClientAuth,
187                          final boolean requiresClientID,
188                          final ParameterRequirement scopeRequirementInTokenRequest,
189                          final Set<String> requestParamNames) {
190
191                this(value, value, requiresClientAuth, requiresClientID, scopeRequirementInTokenRequest, requestParamNames);
192        }
193
194
195        /**
196         * Creates a new OAuth 2.0 authorisation grant type with the specified
197         * value.
198         *
199         * @param value                          The authorisation grant type
200         *                                       value. Must not be
201         *                                       {@code null} or empty string.
202         * @param shortName                      Short (display) name for the
203         *                                       grant. Must not be
204         *                                       {@code null}.
205         * @param requiresClientAuth             The client authentication
206         *                                       requirement.
207         * @param requiresClientID               The client identifier
208         *                                       requirement.
209         * @param scopeRequirementInTokenRequest The scope parameter
210         *                                       requirement in token requests.
211         *                                       Must not be {@code null}.
212         * @param requestParamNames              The names of the token request
213         *                                       parameters specific to this
214         *                                       grant type, empty set or
215         *                                       {@code null} if none.
216         */
217        private GrantType(final String value,
218                          final String shortName,
219                          final boolean requiresClientAuth,
220                          final boolean requiresClientID,
221                          final ParameterRequirement scopeRequirementInTokenRequest,
222                          final Set<String> requestParamNames) {
223
224                super(value);
225
226                this.shortName = Objects.requireNonNull(shortName);
227
228                this.requiresClientAuth = requiresClientAuth;
229
230                this.requiresClientID = requiresClientID;
231
232                Objects.requireNonNull(scopeRequirementInTokenRequest);
233                this.scopeRequirementInTokenRequest = scopeRequirementInTokenRequest;
234
235                this.requestParamNames = requestParamNames == null ? Collections.<String>emptySet() : Collections.unmodifiableSet(requestParamNames);
236        }
237
238
239        /**
240         * Returns a short (display) name for the grant.
241         *
242         * @return The short name.
243         */
244        public String getShortName() {
245
246                return shortName;
247        }
248
249
250        /**
251         * Gets the client authentication requirement.
252         *
253         * @return {@code true} if explicit client authentication is always
254         *         required for this grant type, else {@code false}.
255         */
256        public boolean requiresClientAuthentication() {
257
258                return requiresClientAuth;
259        }
260
261
262        /**
263         * Gets the client identifier requirement.
264         *
265         * @return {@code true} if a client identifier must always be
266         *         communicated for this grant type (either as part of the
267         *         client authentication, or as a parameter in the token
268         *         request), else {@code false}.
269         */
270        public boolean requiresClientID() {
271
272                return requiresClientID;
273        }
274
275        /**
276         * Gets the scope parameter requirement in token requests.
277         *
278         * @return The scope parameter requirement.
279         */
280        public ParameterRequirement getScopeRequirementInTokenRequest() {
281
282                return scopeRequirementInTokenRequest;
283        }
284
285
286        /**
287         * Gets the names of the token request parameters specific to this
288         * grant type.
289         *
290         * @return The parameter names, empty set if none.
291         */
292        public Set<String> getRequestParameterNames() {
293
294                return requestParamNames;
295        }
296
297
298        @Override
299        public boolean equals(final Object object) {
300        
301                return object instanceof GrantType && this.toString().equals(object.toString());
302        }
303
304
305        /**
306         * Parses a grant type from the specified string.
307         *
308         * @param value The string to parse.
309         *
310         * @return The grant type.
311         *
312         * @throws ParseException If string is {@code null}, blank or empty.
313         */
314        public static GrantType parse(final String value)
315                throws ParseException {
316
317                GrantType grantType;
318
319                try {
320                        grantType = new GrantType(value);
321
322                } catch (IllegalArgumentException e) {
323
324                        throw new ParseException(e.getMessage());
325                }
326
327                if (grantType.equals(GrantType.AUTHORIZATION_CODE)) {
328
329                        return GrantType.AUTHORIZATION_CODE;
330
331                } else if (grantType.equals(GrantType.IMPLICIT)) {
332
333                        return GrantType.IMPLICIT;
334
335                } else if (grantType.equals(GrantType.REFRESH_TOKEN)) {
336
337                        return GrantType.REFRESH_TOKEN;
338
339                } else if (grantType.equals(GrantType.PASSWORD)) {
340
341                        return GrantType.PASSWORD;
342
343                } else if (grantType.equals(GrantType.CLIENT_CREDENTIALS)) {
344
345                        return GrantType.CLIENT_CREDENTIALS;
346
347                } else if (grantType.equals(GrantType.JWT_BEARER)) {
348
349                        return GrantType.JWT_BEARER;
350
351                } else if (grantType.equals(GrantType.SAML2_BEARER)) {
352
353                        return GrantType.SAML2_BEARER;
354
355                } else if (grantType.equals(GrantType.DEVICE_CODE)) {
356
357                        return GrantType.DEVICE_CODE;
358
359                } else if (grantType.equals(GrantType.CIBA)) {
360
361                        return GrantType.CIBA;
362
363                } else if (grantType.equals(GrantType.TOKEN_EXCHANGE)) {
364
365                        return GrantType.TOKEN_EXCHANGE;
366
367                } else {
368
369                        return grantType;
370                }
371        }
372        
373        
374        /**
375         * Ensures the specified grant type is set in a list of parameters.
376         *
377         * @param grantType The grant type. Must not be {@code null}.
378         * @param params    The parameters. Must not be {@code null}.
379         *
380         * @throws ParseException If the grant type is not set.
381         */
382        public static void ensure(final GrantType grantType, final Map<String, List<String>> params)
383                throws ParseException {
384                
385                // Parse grant type
386                String grantTypeString = MultivaluedMapUtils.getFirstValue(params, "grant_type");
387                
388                if (grantTypeString == null) {
389                        String msg = "Missing grant_type parameter";
390                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg));
391                }
392                
393                if (! GrantType.parse(grantTypeString).equals(grantType)) {
394                        String msg = "The grant_type must be " + grantType + "";
395                        throw new ParseException(msg, OAuth2Error.UNSUPPORTED_GRANT_TYPE.appendDescription(": " + msg));
396                }
397        }
398}