001package com.nimbusds.oauth2.sdk.auth;
002
003
004import java.util.HashMap;
005import java.util.Map;
006
007import javax.mail.internet.ContentType;
008
009import net.jcip.annotations.Immutable;
010
011import com.nimbusds.oauth2.sdk.ParseException;
012import com.nimbusds.oauth2.sdk.SerializeException;
013import com.nimbusds.oauth2.sdk.id.ClientID;
014import com.nimbusds.oauth2.sdk.http.CommonContentTypes;
015import com.nimbusds.oauth2.sdk.http.HTTPRequest;
016import com.nimbusds.oauth2.sdk.util.URLUtils;
017
018
019/**
020 * Client secret post authentication at the Token endpoint. Implements
021 * {@link ClientAuthenticationMethod#CLIENT_SECRET_POST}. This class is
022 * immutable.
023 *
024 * <p>Related specifications:
025 *
026 * <ul>
027 *     <li>OAuth 2.0 (RFC 6749), section 2.3.1.
028 * </ul>
029 *
030 * @author Vladimir Dzhuvinov
031 */
032@Immutable
033public final class ClientSecretPost extends ClientAuthentication {
034
035
036        /**
037         * The client ID.
038         */
039        private final ClientID clientID;
040        
041        
042        /**
043         * The client secret.
044         */
045        private final Secret secret;
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);
057        
058                if (clientID == null)
059                        throw new IllegalArgumentException("The client ID must not be null");
060                
061                this.clientID = clientID;
062                
063                if (secret == null)
064                        throw new IllegalArgumentException("The client secret must not be null");
065                
066                this.secret = secret;
067        }
068        
069        
070        /**
071         * Gets the client identifier.
072         *
073         * @return The client identifier.
074         */
075        public ClientID getClientID() {
076        
077                return clientID;
078        }
079        
080        
081        /**
082         * Gets the client secret.
083         *
084         * @return The client secret.
085         */
086        public Secret getClientSecret() {
087        
088                return secret;
089        }
090        
091        
092        /**
093         * Returns the parameter representation of this client secret post
094         * authentication. Note that the parameters are not 
095         * {@code application/x-www-form-urlencoded} encoded.
096         *
097         * <p>Parameters map:
098         *
099         * <pre>
100         * "client_id" -> [client-identifier]
101         * "client_secret" -> [client-secret]
102         * </pre>
103         *
104         * @return The parameters map, with keys "client_id" and 
105         *         "client_secret".
106         */
107        public Map<String,String> toParameters() {
108        
109                Map<String,String> params = new HashMap<String,String>();
110                
111                params.put("client_id", clientID.toString());
112                params.put("client_secret", secret.getValue());
113                
114                return params;
115        }
116        
117        
118        @Override
119        public void applyTo(final HTTPRequest httpRequest)
120                throws SerializeException {
121        
122                if (httpRequest.getMethod() != HTTPRequest.Method.POST)
123                        throw new SerializeException("The HTTP request method must be POST");
124                
125                ContentType ct = httpRequest.getContentType();
126                
127                if (ct == null)
128                        throw new SerializeException("Missing HTTP Content-Type header");
129                
130                if (! ct.match(CommonContentTypes.APPLICATION_URLENCODED))
131                        throw new SerializeException("The HTTP Content-Type header must be " + CommonContentTypes.APPLICATION_URLENCODED);
132                
133                Map <String,String> params = httpRequest.getQueryParameters();
134                
135                params.putAll(toParameters());
136                
137                String queryString = URLUtils.serializeParameters(params);
138                
139                httpRequest.setQuery(queryString);
140        }
141        
142        
143        /**
144         * Parses a client secret post authentication from the specified 
145         * parameters map. Note that the parameters must not be
146         * {@code application/x-www-form-urlencoded} encoded.
147         *
148         * @param params The parameters map to parse. The client secret post
149         *               parameters must be keyed under "client_id" and 
150         *               "client_secret". The map must not be {@code null}.
151         *
152         * @return The client secret post authentication.
153         *
154         * @throws ParseException If the parameters map couldn't be parsed to a 
155         *                        client secret post authentication.
156         */
157        public static ClientSecretPost parse(final Map<String,String> params)
158                throws ParseException {
159        
160                String clientIDString = params.get("client_id");
161                
162                if (clientIDString == null)
163                        throw new ParseException("Missing \"client_id\" parameter");
164                
165                String secretValue = params.get("client_secret");
166                
167                if (secretValue == null)
168                        throw new ParseException("Missing \"client_secret\" parameter");
169                
170                return new ClientSecretPost(new ClientID(clientIDString), new Secret(secretValue));
171        }
172        
173        
174        /**
175         * Parses a client secret post authentication from the specified 
176         * {@code application/x-www-form-urlencoded} encoded parameters string.
177         *
178         * @param paramsString The parameters string to parse. The client secret
179         *                     post parameters must be keyed under "client_id" 
180         *                     and "client_secret". The string must not be 
181         *                     {@code null}.
182         *
183         * @return The client secret post authentication.
184         *
185         * @throws ParseException If the parameters string couldn't be parsed to
186         *                        a client secret post authentication.
187         */
188        public static ClientSecretPost parse(final String paramsString)
189                throws ParseException {
190                
191                Map<String,String> params = URLUtils.parseParameters(paramsString);
192                
193                return parse(params);
194        }
195        
196        
197        /**
198         * Parses a client secret post authentication from the specified HTTP
199         * POST request.
200         *
201         * @param httpRequest The HTTP POST request to parse. Must not be 
202         *                    {@code null} and must contain a valid 
203         *                    {@code application/x-www-form-urlencoded} encoded 
204         *                    parameters string in the entity body. The client 
205         *                    secret post parameters must be keyed under 
206         *                    "client_id" and "client_secret".
207         *
208         * @return The client secret post authentication.
209         *
210         * @throws ParseException If the HTTP request header couldn't be parsed
211         *                        to a valid client secret post authentication.
212         */
213        public static ClientSecretPost parse(final HTTPRequest httpRequest)
214                throws ParseException {
215                
216                httpRequest.ensureMethod(HTTPRequest.Method.POST);
217                httpRequest.ensureContentType(CommonContentTypes.APPLICATION_URLENCODED);
218                
219                return parse(httpRequest.getQueryParameters());
220        }
221}