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", 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", 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("client_credentials", true, true, ParameterRequirement.OPTIONAL, Collections.<String>emptySet());
063
064
065        /**
066         * JWT bearer, as specified in RFC 7523.
067         */
068        public static final GrantType JWT_BEARER = new GrantType("urn:ietf:params:oauth:grant-type:jwt-bearer", false, false, ParameterRequirement.OPTIONAL, Collections.singleton("assertion"));
069
070
071        /**
072         * SAML 2.0 bearer, as specified in RFC 7522.
073         */
074        public static final GrantType SAML2_BEARER = new GrantType("urn:ietf:params:oauth:grant-type:saml2-bearer", false, false, ParameterRequirement.OPTIONAL, Collections.singleton("assertion"));
075
076
077        /**
078         * Device authorisation grant, as specified in RFC 8628.
079         */
080        public static final GrantType DEVICE_CODE = new GrantType("urn:ietf:params:oauth:grant-type:device_code", false, true, ParameterRequirement.NOT_ALLOWED, Collections.singleton("device_code"));
081
082
083        /**
084         * Client Initiated Back-channel Authentication (CIBA), as specified in
085         * OpenID Connect Client Initiated Backchannel Authentication Flow -
086         * Core 1.0.
087         */
088        public static final GrantType CIBA = new GrantType("urn:openid:params:grant-type:ciba", true, true, ParameterRequirement.NOT_ALLOWED, Collections.singleton("auth_req_id"));
089
090        
091        /**
092         * Token exchange, as specified in RFC 8693.
093         */
094        public static final GrantType TOKEN_EXCHANGE = new GrantType("urn:ietf:params:oauth:grant-type:token-exchange",
095                        false, false,  ParameterRequirement.OPTIONAL,
096                        new HashSet<>(Arrays.asList(
097                                        "audience", "requested_token_type", "subject_token", "subject_token_type", "actor_token", "actor_token_type"
098                        )));
099        
100        
101        private static final long serialVersionUID = -5367937758427680765L;
102        
103        
104        /**
105         * The client authentication requirement.
106         */
107        private final boolean requiresClientAuth;
108
109
110        /**
111         * The client identifier requirement.
112         */
113        private final boolean requiresClientID;
114
115
116        /**
117         * The scope parameter requirement in token requests.
118         */
119        private final ParameterRequirement scopeRequirementInTokenRequest;
120
121
122        /**
123         * The names of the token request parameters specific to this grant
124         * type.
125         */
126        private final Set<String> requestParamNames;
127
128
129        /**
130         * Creates a new OAuth 2.0 authorisation grant type with the specified
131         * value. The client authentication and identifier requirements are set
132         * to {@code false}. The scope parameter in token requests is not
133         * allowed.
134         *
135         * @param value The authorisation grant type value. Must not be
136         *              {@code null} or empty string.
137         */
138        public GrantType(final String value) {
139
140                this(value, false, false, ParameterRequirement.NOT_ALLOWED, Collections.<String>emptySet());
141        }
142
143
144        /**
145         * Creates a new OAuth 2.0 authorisation grant type with the specified
146         * value.
147         *
148         * @param value                          The authorisation grant type
149         *                                       value. Must not be
150         *                                       {@code null} or empty string.
151         * @param requiresClientAuth             The client authentication
152         *                                       requirement.
153         * @param requiresClientID               The client identifier
154         *                                       requirement.
155         * @param scopeRequirementInTokenRequest The scope parameter
156         *                                       requirement in token requests.
157         *                                       Must not be {@code null}.
158         * @param requestParamNames              The names of the token request
159         *                                       parameters specific to this
160         *                                       grant type, empty set or
161         *                                       {@code null} if none.
162         */
163        private GrantType(final String value,
164                          final boolean requiresClientAuth,
165                          final boolean requiresClientID,
166                          final ParameterRequirement scopeRequirementInTokenRequest,
167                          final Set<String> requestParamNames) {
168
169                super(value);
170
171                this.requiresClientAuth = requiresClientAuth;
172
173                this.requiresClientID = requiresClientID;
174
175                Objects.requireNonNull(scopeRequirementInTokenRequest);
176                this.scopeRequirementInTokenRequest = scopeRequirementInTokenRequest;
177
178                this.requestParamNames = requestParamNames == null ? Collections.<String>emptySet() : Collections.unmodifiableSet(requestParamNames);
179        }
180
181
182        /**
183         * Gets the client authentication requirement.
184         *
185         * @return {@code true} if explicit client authentication is always
186         *         required for this grant type, else {@code false}.
187         */
188        public boolean requiresClientAuthentication() {
189
190                return requiresClientAuth;
191        }
192
193
194        /**
195         * Gets the client identifier requirement.
196         *
197         * @return {@code true} if a client identifier must always be
198         *         communicated for this grant type (either as part of the
199         *         client authentication, or as a parameter in the token
200         *         request), else {@code false}.
201         */
202        public boolean requiresClientID() {
203
204                return requiresClientID;
205        }
206
207        /**
208         * Gets the scope parameter requirement in token requests.
209         *
210         * @return The scope parameter requirement.
211         */
212        public ParameterRequirement getScopeRequirementInTokenRequest() {
213
214                return scopeRequirementInTokenRequest;
215        }
216
217
218        /**
219         * Gets the names of the token request parameters specific to this
220         * grant type.
221         *
222         * @return The parameter names, empty set if none.
223         */
224        public Set<String> getRequestParameterNames() {
225
226                return requestParamNames;
227        }
228
229
230        @Override
231        public boolean equals(final Object object) {
232        
233                return object instanceof GrantType && this.toString().equals(object.toString());
234        }
235
236
237        /**
238         * Parses a grant type from the specified string.
239         *
240         * @param value The string to parse.
241         *
242         * @return The grant type.
243         *
244         * @throws ParseException If string is {@code null}, blank or empty.
245         */
246        public static GrantType parse(final String value)
247                throws ParseException {
248
249                GrantType grantType;
250
251                try {
252                        grantType = new GrantType(value);
253
254                } catch (IllegalArgumentException e) {
255
256                        throw new ParseException(e.getMessage());
257                }
258
259                if (grantType.equals(GrantType.AUTHORIZATION_CODE)) {
260
261                        return GrantType.AUTHORIZATION_CODE;
262
263                } else if (grantType.equals(GrantType.IMPLICIT)) {
264
265                        return GrantType.IMPLICIT;
266
267                } else if (grantType.equals(GrantType.REFRESH_TOKEN)) {
268
269                        return GrantType.REFRESH_TOKEN;
270
271                } else if (grantType.equals(GrantType.PASSWORD)) {
272
273                        return GrantType.PASSWORD;
274
275                } else if (grantType.equals(GrantType.CLIENT_CREDENTIALS)) {
276
277                        return GrantType.CLIENT_CREDENTIALS;
278
279                } else if (grantType.equals(GrantType.JWT_BEARER)) {
280
281                        return GrantType.JWT_BEARER;
282
283                } else if (grantType.equals(GrantType.SAML2_BEARER)) {
284
285                        return GrantType.SAML2_BEARER;
286
287                } else if (grantType.equals(GrantType.DEVICE_CODE)) {
288
289                        return GrantType.DEVICE_CODE;
290
291                } else if (grantType.equals(GrantType.CIBA)) {
292
293                        return GrantType.CIBA;
294
295                } else if (grantType.equals(GrantType.TOKEN_EXCHANGE)) {
296
297                        return GrantType.TOKEN_EXCHANGE;
298
299                } else {
300
301                        return grantType;
302                }
303        }
304        
305        
306        /**
307         * Ensures the specified grant type is set in a list of parameters.
308         *
309         * @param grantType The grant type. Must not be {@code null}.
310         * @param params    The parameters. Must not be {@code null}.
311         *
312         * @throws ParseException If the grant type is not set.
313         */
314        public static void ensure(final GrantType grantType, final Map<String, List<String>> params)
315                throws ParseException {
316                
317                // Parse grant type
318                String grantTypeString = MultivaluedMapUtils.getFirstValue(params, "grant_type");
319                
320                if (grantTypeString == null) {
321                        String msg = "Missing grant_type parameter";
322                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg));
323                }
324                
325                if (! GrantType.parse(grantTypeString).equals(grantType)) {
326                        String msg = "The grant_type must be " + grantType + "";
327                        throw new ParseException(msg, OAuth2Error.UNSUPPORTED_GRANT_TYPE.appendDescription(": " + msg));
328                }
329        }
330}