001/*
002 * oauth2-oidc-sdk
003 *
004 * Copyright 2012-2016, Connect2id Ltd and contributors.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.oauth2.sdk.auth;
019
020
021import com.nimbusds.common.contenttype.ContentType;
022import com.nimbusds.oauth2.sdk.ParseException;
023import com.nimbusds.oauth2.sdk.SerializeException;
024import com.nimbusds.oauth2.sdk.http.HTTPRequest;
025import com.nimbusds.oauth2.sdk.id.ClientID;
026import com.nimbusds.oauth2.sdk.util.MultivaluedMapUtils;
027import com.nimbusds.oauth2.sdk.util.URLUtils;
028import net.jcip.annotations.Immutable;
029
030import java.util.*;
031
032
033/**
034 * Client secret post authentication at the Token endpoint. Implements
035 * {@link ClientAuthenticationMethod#CLIENT_SECRET_POST}.
036 *
037 * <p>Related specifications:
038 *
039 * <ul>
040 *     <li>OAuth 2.0 (RFC 6749), sections 2.3.1 and 3.2.1.
041 *     <li>OpenID Connect Core 1.0, section 9.
042 * </ul>
043 */
044@Immutable
045public final class ClientSecretPost extends PlainClientSecret {
046
047
048        /**
049         * Creates a new client secret post authentication.
050         *
051         * @param clientID The client identifier. Must not be {@code null}.
052         * @param secret   The client secret. Must not be {@code null}.
053         */
054        public ClientSecretPost(final ClientID clientID, final Secret secret) {
055        
056                super(ClientAuthenticationMethod.CLIENT_SECRET_POST, clientID, secret);
057        }
058        
059        
060        @Override
061        public Set<String> getFormParameterNames() {
062                
063                return Collections.unmodifiableSet(new HashSet<String>(Arrays.asList("client_id", "client_secret")));
064        }
065        
066        
067        /**
068         * Returns the parameter representation of this client secret post
069         * authentication. Note that the parameters are not 
070         * {@code application/x-www-form-urlencoded} encoded.
071         *
072         * <p>Parameters map:
073         *
074         * <pre>
075         * "client_id" = [client-identifier]
076         * "client_secret" = [client-secret]
077         * </pre>
078         *
079         * @return The parameters map, with keys "client_id" and 
080         *         "client_secret".
081         */
082        public Map<String,List<String>> toParameters() {
083        
084                Map<String,List<String>> params = new HashMap<>();
085                params.put("client_id", Collections.singletonList(getClientID().getValue()));
086                params.put("client_secret", Collections.singletonList(getClientSecret().getValue()));
087                return params;
088        }
089        
090        
091        @Override
092        public void applyTo(final HTTPRequest httpRequest) {
093        
094                if (httpRequest.getMethod() != HTTPRequest.Method.POST)
095                        throw new SerializeException("The HTTP request method must be POST");
096                
097                ContentType ct = httpRequest.getEntityContentType();
098                
099                if (ct == null)
100                        throw new SerializeException("Missing HTTP Content-Type header");
101                
102                if (! ct.matches(ContentType.APPLICATION_URLENCODED))
103                        throw new SerializeException("The HTTP Content-Type header must be " + ContentType.APPLICATION_URLENCODED);
104
105                Map<String, List<String>> params = new LinkedHashMap<>();
106                try {
107                        params.putAll(httpRequest.getBodyAsFormParameters());
108                } catch (ParseException e) {
109                        throw new SerializeException(e.getMessage(), e);
110                }
111                params.putAll(toParameters());
112                
113                String queryString = URLUtils.serializeParameters(params);
114                
115                httpRequest.setBody(queryString);
116        }
117        
118        
119        /**
120         * Parses a client secret post authentication from the specified 
121         * parameters map. Note that the parameters must not be
122         * {@code application/x-www-form-urlencoded} encoded.
123         *
124         * @param params The parameters map to parse. The client secret post
125         *               parameters must be keyed under "client_id" and 
126         *               "client_secret". The map must not be {@code null}.
127         *
128         * @return The client secret post authentication.
129         *
130         * @throws ParseException If the parameters map couldn't be parsed to a 
131         *                        client secret post authentication.
132         */
133        public static ClientSecretPost parse(final Map<String,List<String>> params)
134                throws ParseException {
135        
136                String clientIDString = MultivaluedMapUtils.getFirstValue(params, "client_id");
137                
138                if (clientIDString == null)
139                        throw new ParseException("Malformed client secret post authentication: Missing client_id parameter");
140                
141                String secretValue = MultivaluedMapUtils.getFirstValue(params, "client_secret");
142                
143                if (secretValue == null)
144                        throw new ParseException("Malformed client secret post authentication: Missing client_secret parameter");
145                
146                return new ClientSecretPost(new ClientID(clientIDString), new Secret(secretValue));
147        }
148        
149        
150        /**
151         * Parses a client secret post authentication from the specified 
152         * {@code application/x-www-form-urlencoded} encoded parameters string.
153         *
154         * @param paramsString The parameters string to parse. The client secret
155         *                     post parameters must be keyed under "client_id" 
156         *                     and "client_secret". The string must not be 
157         *                     {@code null}.
158         *
159         * @return The client secret post authentication.
160         *
161         * @throws ParseException If the parameters string couldn't be parsed to
162         *                        a client secret post authentication.
163         */
164        public static ClientSecretPost parse(final String paramsString)
165                throws ParseException {
166                
167                Map<String,List<String>> params = URLUtils.parseParameters(paramsString);
168                
169                return parse(params);
170        }
171        
172        
173        /**
174         * Parses a client secret post authentication from the specified HTTP
175         * POST request.
176         *
177         * @param httpRequest The HTTP POST request to parse. Must not be 
178         *                    {@code null} and must contain a valid 
179         *                    {@code application/x-www-form-urlencoded} encoded 
180         *                    parameters string in the entity body. The client 
181         *                    secret post parameters must be keyed under 
182         *                    "client_id" and "client_secret".
183         *
184         * @return The client secret post authentication.
185         *
186         * @throws ParseException If the HTTP request header couldn't be parsed
187         *                        to a valid client secret post authentication.
188         */
189        public static ClientSecretPost parse(final HTTPRequest httpRequest)
190                throws ParseException {
191                
192                httpRequest.ensureMethod(HTTPRequest.Method.POST);
193                httpRequest.ensureEntityContentType(ContentType.APPLICATION_URLENCODED);
194                
195                return parse(httpRequest.getBodyAsFormParameters());
196        }
197}