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