All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.nimbusds.oauth2.sdk.TokenRequest Maven / Gradle / Ivy

Go to download

OAuth 2.0 SDK with OpenID Connection extensions for developing client and server applications.

There is a newer version: 11.20.1
Show newest version
/*
 * oauth2-oidc-sdk
 *
 * Copyright 2012-2023, Connect2id Ltd and contributors.
 *
 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
 * this file except in compliance with the License. You may obtain a copy of the
 * License at
 *
 *    http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software distributed
 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
 * specific language governing permissions and limitations under the License.
 */

package com.nimbusds.oauth2.sdk;


import com.nimbusds.common.contenttype.ContentType;
import com.nimbusds.oauth2.sdk.auth.ClientAuthentication;
import com.nimbusds.oauth2.sdk.auth.ClientSecretBasic;
import com.nimbusds.oauth2.sdk.http.HTTPRequest;
import com.nimbusds.oauth2.sdk.id.ClientID;
import com.nimbusds.oauth2.sdk.rar.AuthorizationDetail;
import com.nimbusds.oauth2.sdk.token.AccessToken;
import com.nimbusds.oauth2.sdk.token.RefreshToken;
import com.nimbusds.oauth2.sdk.util.*;
import com.nimbusds.openid.connect.sdk.nativesso.DeviceSecret;
import net.jcip.annotations.Immutable;

import java.net.URI;
import java.net.URISyntaxException;
import java.util.*;


/**
 * Token request. Used to obtain an {@link AccessToken access token} and an
 * optional {@link RefreshToken refresh token} at the tokens endpoint of an
 * authorisation server. Supports custom request parameters.
 *
 * 

Example token request with an authorisation code grant: * *

 * POST /token HTTP/1.1
 * Host: server.example.com
 * Content-Type: application/x-www-form-urlencoded
 * Authorization: Basic czZCaGRSa3F0MzpnWDFmQmF0M2JW
 *
 * grant_type=authorization_code
 * &code=SplxlOBeZQQYbYS6WxSbIA
 * &redirect_uri=https%3A%2F%2Fclient.example.org%2Fcb
 * 
* *

Related specifications: * *

    *
  • OAuth 2.0 (RFC 6749) *
  • OAuth 2.0 Rich Authorization Requests (RFC 9396) *
  • Resource Indicators for OAuth 2.0 (RFC 8707) *
  • OAuth 2.0 Incremental Authorization (draft-ietf-oauth-incremental-authz) *
  • OpenID Connect Native SSO for Mobile Apps 1.0 *
*/ @Immutable public class TokenRequest extends AbstractOptionallyIdentifiedRequest { /** * The registered parameter names. */ private static final Set REGISTERED_PARAMETER_NAMES; static { Set p = new HashSet<>(); p.add("grant_type"); p.add("client_id"); p.add("client_secret"); p.add("client_assertion_type"); p.add("client_assertion"); p.add("scope"); p.add("authorization_details"); p.add("resource"); p.add("existing_grant"); p.add("device_secret"); REGISTERED_PARAMETER_NAMES = Collections.unmodifiableSet(p); } /** * The authorisation grant. */ private final AuthorizationGrant authzGrant; /** * The scope (optional). */ private final Scope scope; /** * The RAR details (optional). */ private final List authorizationDetails; /** * The resource URI(s) (optional). */ private final List resources; /** * Existing refresh token for incremental authorisation of a public * client (optional). */ private final RefreshToken existingGrant; /** * Device secret for native SSO (optional). */ private final DeviceSecret deviceSecret; /** * Custom request parameters. */ private final Map> customParams; private static final Set ALLOWED_REPEATED_PARAMS = new HashSet<>(Arrays.asList( "resource", // https://www.rfc-editor.org/rfc/rfc8707.html#section-2.2 "audience" // https://www.rfc-editor.org/rfc/rfc8693.html#name-relationship-between-resour )); /** * Builder for constructing token requests. */ public static class Builder { /** * The endpoint URI (optional). */ private final URI endpoint; /** * The client authentication, {@code null} if none. */ private final ClientAuthentication clientAuth; /** * The client identifier, {@code null} if not specified. */ private final ClientID clientID; /** * The authorisation grant. */ private final AuthorizationGrant authzGrant; /** * The scope (optional). */ private Scope scope; /** * The RAR details (optional). */ private List authorizationDetails; /** * The resource URI(s) (optional). */ private List resources; /** * Existing refresh token for incremental authorisation of a * public client (optional). */ private RefreshToken existingGrant; /** * Device secret for native SSO (optional). */ private DeviceSecret deviceSecret; /** * Custom parameters. */ private final Map> customParams = new HashMap<>(); /** * Creates a new builder for a token request with client * authentication. * * @param endpoint The URI of the token endpoint. May be * {@code null} if the {@link #toHTTPRequest} * method is not going to be used. * @param clientAuth The client authentication. Must not be * {@code null}. * @param authzGrant The authorisation grant. Must not be * {@code null}. */ public Builder(final URI endpoint, final ClientAuthentication clientAuth, final AuthorizationGrant authzGrant) { this.endpoint = endpoint; this.clientAuth = Objects.requireNonNull(clientAuth); clientID = null; this.authzGrant = Objects.requireNonNull(authzGrant); } /** * Creates a new builder for a token request with no (explicit) * client authentication. The grant itself may be used to * authenticate the client. * * @param endpoint The URI of the token endpoint. May be * {@code null} if the {@link #toHTTPRequest} * method is not going to be used. * @param clientID The client identifier. Must not be * {@code null}. * @param authzGrant The authorisation grant. Must not be * {@code null}. */ public Builder(final URI endpoint, final ClientID clientID, final AuthorizationGrant authzGrant) { this.endpoint = endpoint; clientAuth = null; this.clientID = Objects.requireNonNull(clientID); this.authzGrant = Objects.requireNonNull(authzGrant); } /** * Creates a new builder for a token request with no (explicit) * client authentication, the client identifier is inferred * from the authorisation grant. * * @param endpoint The URI of the token endpoint. May be * {@code null} if the {@link #toHTTPRequest} * method is not going to be used. * @param authzGrant The authorisation grant. Must not be * {@code null}. */ public Builder(final URI endpoint, final AuthorizationGrant authzGrant) { this.endpoint = endpoint; clientAuth = null; clientID = null; this.authzGrant = Objects.requireNonNull(authzGrant); } /** * Sets the scope. Corresponds to the optional {@code scope} * parameter. * * @param scope The scope, {@code null} if not specified. * * @return This builder. */ public Builder scope(final Scope scope) { this.scope = scope; return this; } /** * Sets the Rich Authorisation Request (RAR) details. * Corresponds to the optional {@code authorization_details} * parameter. * * @param authorizationDetails The authorisation details, * {@code null} if not specified. * * @return This builder. */ public Builder authorizationDetails(final List authorizationDetails) { this.authorizationDetails = authorizationDetails; return this; } /** * Sets the resource server URI. Corresponds to the optional * {@code resource} parameter. * * @param resource The resource URI, {@code null} if not * specified. * * @return This builder. */ public Builder resource(final URI resource) { if (resource != null) { this.resources = Collections.singletonList(resource); } else { this.resources = null; } return this; } /** * Sets the resource server URI(s). Corresponds to the optional * {@code resource} parameter. * * @param resources The resource URI(s), {@code null} if not * specified. * * @return This builder. */ public Builder resources(final URI ... resources) { if (resources != null) { this.resources = Arrays.asList(resources); } else { this.resources = null; } return this; } /** * Sets the existing refresh token for incremental * authorisation of a public client. Corresponds to the * optional {@code existing_grant} parameter. * * @param existingGrant Existing refresh token for incremental * authorisation of a public client, * {@code null} if not specified. * * @return This builder. */ public Builder existingGrant(final RefreshToken existingGrant) { this.existingGrant = existingGrant; return this; } /** * Sets the device secret for native SSO. Corresponds to the * optional {@code device_secret} parameter. * * @param deviceSecret The device secret, {@code null} if not * specified. * * @return This builder. */ public Builder deviceSecret(final DeviceSecret deviceSecret) { this.deviceSecret = deviceSecret; return this; } /** * Sets a custom parameter. * * @param name The parameter name. Must not be {@code null}. * @param values The parameter values, {@code null} if not * specified. * * @return This builder. */ public Builder customParameter(final String name, final String ... values) { if (values == null || values.length == 0) { customParams.remove(name); } else { customParams.put(name, Arrays.asList(values)); } return this; } /** * Builds a new token request. * * @return The token request. */ public TokenRequest build() { try { if (clientAuth != null) { return new TokenRequest( endpoint, clientAuth, authzGrant, scope, authorizationDetails, resources, deviceSecret, customParams); } return new TokenRequest( endpoint, clientID, authzGrant, scope, authorizationDetails, resources, existingGrant, deviceSecret, customParams); } catch (IllegalArgumentException e) { throw new IllegalStateException(e.getMessage(), e); } } } /** * Creates a new token request with client authentication. * * @param endpoint The URI of the token endpoint. May be * {@code null} if the {@link #toHTTPRequest} method * is not going to be used. * @param clientAuth The client authentication. Must not be * {@code null}. * @param authzGrant The authorisation grant. Must not be {@code null}. * @param scope The requested scope, {@code null} if not * specified. */ public TokenRequest(final URI endpoint, final ClientAuthentication clientAuth, final AuthorizationGrant authzGrant, final Scope scope) { this(endpoint, clientAuth, authzGrant, scope, null, null); } /** * Creates a new token request with client authentication and extension * and custom parameters. * * @param endpoint The URI of the token endpoint. May be * {@code null} if the {@link #toHTTPRequest} * method is not going to be used. * @param clientAuth The client authentication. Must not be * {@code null}. * @param authzGrant The authorisation grant. Must not be * {@code null}. * @param scope The requested scope, {@code null} if not * specified. * @param resources The resource URI(s), {@code null} if not * specified. * @param customParams Custom parameters to be included in the request * body, empty map or {@code null} if none. */ @Deprecated public TokenRequest(final URI endpoint, final ClientAuthentication clientAuth, final AuthorizationGrant authzGrant, final Scope scope, final List resources, final Map> customParams) { this(endpoint, clientAuth, authzGrant, scope, null, resources, customParams); } /** * Creates a new token request with client authentication and extension * and custom parameters. * * @param endpoint The URI of the token endpoint. May be * {@code null} if the * {@link #toHTTPRequest} method is not * going be used. * @param clientAuth The client authentication. Must not be * {@code null}. * @param authzGrant The authorisation grant. Must not be * {@code null}. * @param scope The requested scope, {@code null} if not * specified. * @param authorizationDetails The Rich Authorisation Request (RAR) * details, {@code null} if not specified. * @param resources The resource URI(s), {@code null} if not * specified. * @param customParams Custom parameters to be included in the * request body, empty map or {@code null} * if none. */ @Deprecated public TokenRequest(final URI endpoint, final ClientAuthentication clientAuth, final AuthorizationGrant authzGrant, final Scope scope, final List authorizationDetails, final List resources, final Map> customParams) { this(endpoint, clientAuth, authzGrant, scope, authorizationDetails, resources, null, customParams); } /** * Creates a new token request with client authentication. * * @param endpoint The URI of the token endpoint. May be * {@code null} if the {@link #toHTTPRequest} method * is not going to be used. * @param clientAuth The client authentication. Must not be * {@code null}. * @param authzGrant The authorisation grant. Must not be {@code null}. */ @Deprecated public TokenRequest(final URI endpoint, final ClientAuthentication clientAuth, final AuthorizationGrant authzGrant) { this(endpoint, clientAuth, authzGrant, null); } /** * Creates a new token request with no (explicit) client * authentication. The grant itself may be used to authenticate the * client. * * @param endpoint The URI of the token endpoint. May be * {@code null} if the {@link #toHTTPRequest} method * is not going to be used. * @param clientID The client identifier, {@code null} if not * specified. * @param authzGrant The authorisation grant. Must not be {@code null}. * @param scope The requested scope, {@code null} if not * specified. */ public TokenRequest(final URI endpoint, final ClientID clientID, final AuthorizationGrant authzGrant, final Scope scope) { this(endpoint, clientID, authzGrant, scope, null, null,null); } /** * Creates a new token request, with no (explicit) client * authentication and extension and custom parameters. The grant itself * may be used to authenticate the client. * * @param endpoint The URI of the token endpoint. May be * {@code null} if the {@link #toHTTPRequest} * method is not going to be used. * @param clientID The client identifier, {@code null} if not * specified. * @param authzGrant The authorisation grant. Must not be * {@code null}. * @param scope The requested scope, {@code null} if not * specified. * @param resources The resource URI(s), {@code null} if not * specified. * @param existingGrant Existing refresh token for incremental * authorisation of a public client, {@code null} * if not specified. * @param customParams Custom parameters to be included in the request * body, empty map or {@code null} if none. */ @Deprecated public TokenRequest(final URI endpoint, final ClientID clientID, final AuthorizationGrant authzGrant, final Scope scope, final List resources, final RefreshToken existingGrant, final Map> customParams) { this(endpoint, clientID, authzGrant, scope, null, resources, existingGrant, customParams); } /** * Creates a new token request, with no (explicit) client * authentication and extension and custom parameters. The grant itself * may be used to authenticate the client. * * @param endpoint The URI of the token endpoint. May be * {@code null} if the * {@link #toHTTPRequest} * method is not going to be used. * @param clientID The client identifier, {@code null} if * not specified. * @param authzGrant The authorisation grant. Must not be * {@code null}. * @param scope The requested scope, {@code null} if not * specified. * @param authorizationDetails The Rich Authorisation Request (RAR) * details, {@code null} if not specified. * @param resources The resource URI(s), {@code null} if not * specified. * @param existingGrant Existing refresh token for incremental * authorisation of a public client, * {@code null} if not specified. * @param customParams Custom parameters to be included in the * request body, empty map or {@code null} * if none. */ @Deprecated public TokenRequest(final URI endpoint, final ClientID clientID, final AuthorizationGrant authzGrant, final Scope scope, final List authorizationDetails, final List resources, final RefreshToken existingGrant, final Map> customParams) { this(endpoint, clientID, authzGrant, scope, authorizationDetails, resources, existingGrant, null, customParams); } /** * Creates a new token request, with no (explicit) client * authentication. The grant itself may be used to authenticate the * client. * * @param endpoint The URI of the token endpoint. May be * {@code null} if the {@link #toHTTPRequest} method * is not going to be used. * @param clientID The client identifier, {@code null} if not * specified. * @param authzGrant The authorisation grant. Must not be {@code null}. */ @Deprecated public TokenRequest(final URI endpoint, final ClientID clientID, final AuthorizationGrant authzGrant) { this(endpoint, clientID, authzGrant, null); } /** * Creates a new token request with no (explicit) client * authentication, the client identifier is inferred from the * authorisation grant. * * @param endpoint The URI of the token endpoint. May be * {@code null} if the {@link #toHTTPRequest} method * is not going to be used. * @param authzGrant The authorisation grant. Must not be {@code null}. * @param scope The requested scope, {@code null} if not * specified. */ public TokenRequest(final URI endpoint, final AuthorizationGrant authzGrant, final Scope scope) { this(endpoint, (ClientID)null, authzGrant, scope); } /** * Creates a new token request with no (explicit) client * authentication, the client identifier is inferred from the * authorisation grant. * * @param endpoint The URI of the token endpoint. May be * {@code null} if the {@link #toHTTPRequest} method * is not going to be used. * @param authzGrant The authorisation grant. Must not be {@code null}. */ @Deprecated public TokenRequest(final URI endpoint, final AuthorizationGrant authzGrant) { this(endpoint, (ClientID)null, authzGrant, null); } /** * Creates a new token request with client authentication and extension * and custom parameters. * * @param endpoint The URI of the token endpoint. May be * {@code null} if the * {@link #toHTTPRequest} method is not * going be used. * @param clientAuth The client authentication. Must not be * {@code null}. * @param authzGrant The authorisation grant. Must not be * {@code null}. * @param scope The requested scope, {@code null} if not * specified. * @param authorizationDetails The Rich Authorisation Request (RAR) * details, {@code null} if not specified. * @param resources The resource URI(s), {@code null} if not * specified. * @param deviceSecret The device secret, {@code null} if not * specified. * @param customParams Custom parameters to be included in the * request body, empty map or {@code null} * if none. */ public TokenRequest(final URI endpoint, final ClientAuthentication clientAuth, final AuthorizationGrant authzGrant, final Scope scope, final List authorizationDetails, final List resources, final DeviceSecret deviceSecret, final Map> customParams) { super(endpoint, Objects.requireNonNull(clientAuth)); this.authzGrant = Objects.requireNonNull(authzGrant); this.scope = scope; if (resources != null) { for (URI resourceURI: resources) { if (! ResourceUtils.isLegalResourceURI(resourceURI)) throw new IllegalArgumentException("Resource URI must be absolute and with no query or fragment: " + resourceURI); } } this.authorizationDetails = authorizationDetails; this.resources = resources; this.existingGrant = null; // only for public client this.deviceSecret = deviceSecret; if (MapUtils.isNotEmpty(customParams)) { this.customParams = customParams; } else { this.customParams = Collections.emptyMap(); } } /** * Creates a new token request, with no (explicit) client * authentication and extension and custom parameters. The grant itself * may be used to authenticate the client. * * @param endpoint The URI of the token endpoint. May be * {@code null} if the * {@link #toHTTPRequest} * method is not going to be used. * @param clientID The client identifier, {@code null} if * not specified. * @param authzGrant The authorisation grant. Must not be * {@code null}. * @param scope The requested scope, {@code null} if not * specified. * @param authorizationDetails The Rich Authorisation Request (RAR) * details, {@code null} if not specified. * @param resources The resource URI(s), {@code null} if not * specified. * @param existingGrant Existing refresh token for incremental * authorisation of a public client, * {@code null} if not specified. * @param deviceSecret The device secret, {@code null} if not * specified. * @param customParams Custom parameters to be included in the * request body, empty map or {@code null} * if none. */ public TokenRequest(final URI endpoint, final ClientID clientID, final AuthorizationGrant authzGrant, final Scope scope, final List authorizationDetails, final List resources, final RefreshToken existingGrant, final DeviceSecret deviceSecret, final Map> customParams) { super(endpoint, clientID); if (authzGrant.getType().requiresClientAuthentication()) { throw new IllegalArgumentException("The \"" + authzGrant.getType() + "\" grant type requires client authentication"); } if (authzGrant.getType().requiresClientID() && clientID == null) { throw new IllegalArgumentException("The \"" + authzGrant.getType() + "\" grant type requires a \"client_id\" parameter"); } this.authzGrant = authzGrant; this.scope = scope; if (resources != null) { for (URI resourceURI: resources) { if (! ResourceUtils.isLegalResourceURI(resourceURI)) throw new IllegalArgumentException("Resource URI must be absolute and with no query or fragment: " + resourceURI); } } this.authorizationDetails = authorizationDetails; this.resources = resources; this.existingGrant = existingGrant; this.deviceSecret = deviceSecret; if (MapUtils.isNotEmpty(customParams)) { this.customParams = customParams; } else { this.customParams = Collections.emptyMap(); } } /** * Returns the authorisation grant. * * @return The authorisation grant. */ public AuthorizationGrant getAuthorizationGrant() { return authzGrant; } /** * Returns the requested scope. Corresponds to the {@code scope} * parameter. * * @return The requested scope, {@code null} if not specified. */ public Scope getScope() { return scope; } /** * Returns the Rich Authorisation Request (RAR) details. Corresponds to * the {@code authorization_details} parameter. * * @return The authorisation details, {@code null} if not specified. */ public List getAuthorizationDetails() { return authorizationDetails; } /** * Returns the resource server URI. Corresponds to the {@code resource} * parameter. * * @return The resource URI(s), {@code null} if not specified. */ public List getResources() { return resources; } /** * Returns the existing refresh token for incremental authorisation of * a public client. Corresponds to the {@code existing_grant} * parameter. * * @return The existing grant, {@code null} if not specified. */ public RefreshToken getExistingGrant() { return existingGrant; } /** * Returns the device secret for native SSO. Corresponds to the * {@code device_secret} parameter. * * @return The device secret, {@code null} if not specified. */ public DeviceSecret getDeviceSecret() { return deviceSecret; } /** * Returns the additional custom parameters included in the request * body. * * @return The additional custom parameters as an unmodifiable map, * empty map if none. */ public Map> getCustomParameters () { return Collections.unmodifiableMap(customParams); } /** * Returns the specified custom parameter included in the request body. * * @param name The parameter name. Must not be {@code null}. * * @return The parameter value(s), {@code null} if not specified. */ public List getCustomParameter(final String name) { return customParams.get(name); } @Override public HTTPRequest toHTTPRequest() { if (getEndpointURI() == null) throw new SerializeException("The endpoint URI is not specified"); HTTPRequest httpRequest = new HTTPRequest(HTTPRequest.Method.POST, getEndpointURI()); httpRequest.setEntityContentType(ContentType.APPLICATION_URLENCODED); if (getClientAuthentication() != null) { getClientAuthentication().applyTo(httpRequest); } Map> params; try { params = new LinkedHashMap<>(httpRequest.getBodyAsFormParameters()); } catch (ParseException e) { throw new SerializeException(e.getMessage(), e); } params.putAll(getAuthorizationGrant().toParameters()); switch (getAuthorizationGrant().getType().getScopeRequirementInTokenRequest()) { case REQUIRED: if (CollectionUtils.isEmpty(getScope())) { throw new SerializeException("Scope is required for the " + getAuthorizationGrant().getType() + " grant"); } params.put("scope", Collections.singletonList(getScope().toString())); break; case OPTIONAL: if (CollectionUtils.isNotEmpty(getScope())) { params.put("scope", Collections.singletonList(getScope().toString())); } break; case NOT_ALLOWED: default: break; } if (getClientID() != null) { params.put("client_id", Collections.singletonList(getClientID().getValue())); } if (getAuthorizationDetails() != null) { params.put("authorization_details", Collections.singletonList(AuthorizationDetail.toJSONString(getAuthorizationDetails()))); } if (getResources() != null) { List values = new LinkedList<>(); for (URI uri: getResources()) { if (uri == null) continue; values.add(uri.toString()); } params.put("resource", values); } if (getExistingGrant() != null) { params.put("existing_grant", Collections.singletonList(getExistingGrant().getValue())); } if (getDeviceSecret() != null) { params.put("device_secret", Collections.singletonList(getDeviceSecret().getValue())); } if (! getCustomParameters().isEmpty()) { params.putAll(getCustomParameters()); } httpRequest.setBody(URLUtils.serializeParameters(params)); return httpRequest; } /** * Parses a token request from the specified HTTP request. * * @param httpRequest The HTTP request. Must not be {@code null}. * * @return The token request. * * @throws ParseException If the HTTP request couldn't be parsed to a * token request. */ public static TokenRequest parse(final HTTPRequest httpRequest) throws ParseException { // Only HTTP POST accepted URI endpoint = httpRequest.getURI(); httpRequest.ensureMethod(HTTPRequest.Method.POST); httpRequest.ensureEntityContentType(ContentType.APPLICATION_URLENCODED); // Parse client authentication, if any ClientAuthentication clientAuth; try { clientAuth = ClientAuthentication.parse(httpRequest); } catch (ParseException e) { throw new ParseException(e.getMessage(), OAuth2Error.INVALID_REQUEST.appendDescription(": " + e.getMessage())); } // No fragment! May use query component! Map> params = httpRequest.getBodyAsFormParameters(); Set repeatParams = MultivaluedMapUtils.getKeysWithMoreThanOneValue(params, ALLOWED_REPEATED_PARAMS); if (! repeatParams.isEmpty()) { String msg = "Parameter(s) present more than once: " + repeatParams; throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.setDescription(msg)); } // Multiple conflicting client auth methods (issue #203)? if (clientAuth instanceof ClientSecretBasic) { if (StringUtils.isNotBlank(MultivaluedMapUtils.getFirstValue(params, "client_assertion")) || StringUtils.isNotBlank(MultivaluedMapUtils.getFirstValue(params, "client_assertion_type"))) { String msg = "Multiple conflicting client authentication methods found: Basic and JWT assertion"; throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg)); } } // Parse grant AuthorizationGrant grant = AuthorizationGrant.parse(params); if (clientAuth == null && grant.getType().requiresClientAuthentication()) { String msg = "Missing client authentication"; throw new ParseException(msg, OAuth2Error.INVALID_CLIENT.appendDescription(": " + msg)); } // Parse client id ClientID clientID = null; if (clientAuth == null) { // Parse optional client ID String clientIDString = MultivaluedMapUtils.getFirstValue(params, "client_id"); if (StringUtils.isNotBlank(clientIDString)) clientID = new ClientID(clientIDString); if (clientID == null && grant.getType().requiresClientID()) { String msg = "Missing required client_id parameter"; throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg)); } } // Parse optional scope String scopeValue = MultivaluedMapUtils.getFirstValue(params, "scope"); ParameterRequirement scopeRequirement = grant.getType().getScopeRequirementInTokenRequest(); Scope scope = null; if (scopeValue != null && (ParameterRequirement.REQUIRED.equals(scopeRequirement) || ParameterRequirement.OPTIONAL.equals(scopeRequirement))) { scope = Scope.parse(scopeValue); } // Parse optional RAR String json = MultivaluedMapUtils.getFirstValue(params, "authorization_details"); List authorizationDetails = null; if (json != null) { authorizationDetails = AuthorizationDetail.parseList(json); } // Parse optional resource URIs List resources = null; List vList = params.get("resource"); if (vList != null) { resources = new LinkedList<>(); for (String uriValue: vList) { if (uriValue == null) continue; String errMsg = "Illegal resource parameter: Must be an absolute URI without a fragment: " + uriValue; URI resourceURI; try { resourceURI = new URI(uriValue); } catch (URISyntaxException e) { throw new ParseException(errMsg, OAuth2Error.INVALID_TARGET.setDescription(errMsg)); } if (! ResourceUtils.isLegalResourceURI(resourceURI)) { throw new ParseException(errMsg, OAuth2Error.INVALID_TARGET.setDescription(errMsg)); } resources.add(resourceURI); } } String rt = MultivaluedMapUtils.getFirstValue(params, "existing_grant"); RefreshToken existingGrant = StringUtils.isNotBlank(rt) ? new RefreshToken(rt) : null; DeviceSecret deviceSecret = DeviceSecret.parse(MultivaluedMapUtils.getFirstValue(params, "device_secret")); // Parse custom parameters Map> customParams = new HashMap<>(); for (Map.Entry> p: params.entrySet()) { if (REGISTERED_PARAMETER_NAMES.contains(p.getKey().toLowerCase())) { continue; // skip } if (! grant.getType().getRequestParameterNames().contains(p.getKey())) { // We have a custom (non-registered) parameter customParams.put(p.getKey(), p.getValue()); } } if (clientAuth != null) { return new TokenRequest(endpoint, clientAuth, grant, scope, authorizationDetails, resources, deviceSecret, customParams); } else { // public client return new TokenRequest(endpoint, clientID, grant, scope, authorizationDetails, resources, existingGrant, deviceSecret, customParams); } } }




© 2015 - 2024 Weber Informatics LLC | Privacy Policy