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 java.util.*;
022
023import net.jcip.annotations.Immutable;
024
025import com.nimbusds.common.contenttype.ContentType;
026import com.nimbusds.oauth2.sdk.ParseException;
027import com.nimbusds.oauth2.sdk.SerializeException;
028import com.nimbusds.oauth2.sdk.http.HTTPRequest;
029import com.nimbusds.oauth2.sdk.id.ClientID;
030import com.nimbusds.oauth2.sdk.util.MultivaluedMapUtils;
031import com.nimbusds.oauth2.sdk.util.URLUtils;
032
033
034/**
035 * Client secret post authentication at the Token endpoint. Implements
036 * {@link ClientAuthenticationMethod#CLIENT_SECRET_POST}.
037 *
038 * <p>Related specifications:
039 *
040 * <ul>
041 *     <li>OAuth 2.0 (RFC 6749), sections 2.3.1 and 3.2.1.
042 *     <li>OpenID Connect Core 1.0, section 9.
043 * </ul>
044 */
045@Immutable
046public final class ClientSecretPost extends PlainClientSecret {
047
048
049        /**
050         * Creates a new client secret post authentication.
051         *
052         * @param clientID The client identifier. Must not be {@code null}.
053         * @param secret   The client secret. Must not be {@code null}.
054         */
055        public ClientSecretPost(final ClientID clientID, final Secret secret) {
056        
057                super(ClientAuthenticationMethod.CLIENT_SECRET_POST, clientID, secret);
058        }
059        
060        
061        @Override
062        public Set<String> getFormParameterNames() {
063                
064                return Collections.unmodifiableSet(new HashSet<String>(Arrays.asList("client_id", "client_secret")));
065        }
066        
067        
068        /**
069         * Returns the parameter representation of this client secret post
070         * authentication. Note that the parameters are not 
071         * {@code application/x-www-form-urlencoded} encoded.
072         *
073         * <p>Parameters map:
074         *
075         * <pre>
076         * "client_id" = [client-identifier]
077         * "client_secret" = [client-secret]
078         * </pre>
079         *
080         * @return The parameters map, with keys "client_id" and 
081         *         "client_secret".
082         */
083        public Map<String,List<String>> toParameters() {
084        
085                Map<String,List<String>> params = new HashMap<>();
086                params.put("client_id", Collections.singletonList(getClientID().getValue()));
087                params.put("client_secret", Collections.singletonList(getClientSecret().getValue()));
088                return params;
089        }
090        
091        
092        @Override
093        public void applyTo(final HTTPRequest httpRequest) {
094        
095                if (httpRequest.getMethod() != HTTPRequest.Method.POST)
096                        throw new SerializeException("The HTTP request method must be POST");
097                
098                ContentType ct = httpRequest.getEntityContentType();
099                
100                if (ct == null)
101                        throw new SerializeException("Missing HTTP Content-Type header");
102                
103                if (! ct.matches(ContentType.APPLICATION_URLENCODED))
104                        throw new SerializeException("The HTTP Content-Type header must be " + ContentType.APPLICATION_URLENCODED);
105                
106                Map<String,List<String>> params = httpRequest.getQueryParameters();
107                
108                params.putAll(toParameters());
109                
110                String queryString = URLUtils.serializeParameters(params);
111                
112                httpRequest.setQuery(queryString);
113        }
114        
115        
116        /**
117         * Parses a client secret post authentication from the specified 
118         * parameters map. Note that the parameters must not be
119         * {@code application/x-www-form-urlencoded} encoded.
120         *
121         * @param params The parameters map to parse. The client secret post
122         *               parameters must be keyed under "client_id" and 
123         *               "client_secret". The map must not be {@code null}.
124         *
125         * @return The client secret post authentication.
126         *
127         * @throws ParseException If the parameters map couldn't be parsed to a 
128         *                        client secret post authentication.
129         */
130        public static ClientSecretPost parse(final Map<String,List<String>> params)
131                throws ParseException {
132        
133                String clientIDString = MultivaluedMapUtils.getFirstValue(params, "client_id");
134                
135                if (clientIDString == null)
136                        throw new ParseException("Malformed client secret post authentication: Missing client_id parameter");
137                
138                String secretValue = MultivaluedMapUtils.getFirstValue(params, "client_secret");
139                
140                if (secretValue == null)
141                        throw new ParseException("Malformed client secret post authentication: Missing client_secret parameter");
142                
143                return new ClientSecretPost(new ClientID(clientIDString), new Secret(secretValue));
144        }
145        
146        
147        /**
148         * Parses a client secret post authentication from the specified 
149         * {@code application/x-www-form-urlencoded} encoded parameters string.
150         *
151         * @param paramsString The parameters string to parse. The client secret
152         *                     post parameters must be keyed under "client_id" 
153         *                     and "client_secret". The string must not be 
154         *                     {@code null}.
155         *
156         * @return The client secret post authentication.
157         *
158         * @throws ParseException If the parameters string couldn't be parsed to
159         *                        a client secret post authentication.
160         */
161        public static ClientSecretPost parse(final String paramsString)
162                throws ParseException {
163                
164                Map<String,List<String>> params = URLUtils.parseParameters(paramsString);
165                
166                return parse(params);
167        }
168        
169        
170        /**
171         * Parses a client secret post authentication from the specified HTTP
172         * POST request.
173         *
174         * @param httpRequest The HTTP POST request to parse. Must not be 
175         *                    {@code null} and must contain a valid 
176         *                    {@code application/x-www-form-urlencoded} encoded 
177         *                    parameters string in the entity body. The client 
178         *                    secret post parameters must be keyed under 
179         *                    "client_id" and "client_secret".
180         *
181         * @return The client secret post authentication.
182         *
183         * @throws ParseException If the HTTP request header couldn't be parsed
184         *                        to a valid client secret post authentication.
185         */
186        public static ClientSecretPost parse(final HTTPRequest httpRequest)
187                throws ParseException {
188                
189                httpRequest.ensureMethod(HTTPRequest.Method.POST);
190                httpRequest.ensureEntityContentType(ContentType.APPLICATION_URLENCODED);
191                
192                return parse(httpRequest.getQueryParameters());
193        }
194}