001package com.nimbusds.oauth2.sdk.client;
002
003
004import java.net.MalformedURLException;
005import java.net.URI;
006import java.net.URISyntaxException;
007import java.net.URL;
008
009import net.jcip.annotations.Immutable;
010
011import net.minidev.json.JSONObject;
012
013import com.nimbusds.oauth2.sdk.ParseException;
014import com.nimbusds.oauth2.sdk.ProtectedResourceRequest;
015import com.nimbusds.oauth2.sdk.SerializeException;
016import com.nimbusds.oauth2.sdk.auth.Secret;
017import com.nimbusds.oauth2.sdk.http.CommonContentTypes;
018import com.nimbusds.oauth2.sdk.http.HTTPRequest;
019import com.nimbusds.oauth2.sdk.id.ClientID;
020import com.nimbusds.oauth2.sdk.token.BearerAccessToken;
021import com.nimbusds.oauth2.sdk.util.JSONObjectUtils;
022
023
024/**
025 * Client registration request.
026 * 
027 * <p>Example HTTP request:
028 *
029 * <pre>
030 * PUT /register/s6BhdRkqt3 HTTP/1.1
031 * Accept: application/json
032 * Host: server.example.com
033 * Authorization: Bearer reg-23410913-abewfq.123483
034 *
035 * {
036 *  "client_id"                  :"s6BhdRkqt3",
037 *  "client_secret"              : "cf136dc3c1fc93f31185e5885805d",
038 *  "redirect_uris"              : [ "https://client.example.org/callback",
039 *                                   "https://client.example.org/alt" ],
040 *  "scope"                      : "read write dolphin",
041 *  "grant_types"                : [ "authorization_code", "refresh_token" ]
042 *  "token_endpoint_auth_method" : "client_secret_basic",
043 *  "jwks_uri"                   : "https://client.example.org/my_public_keys.jwks"
044 *  "client_name"                : "My New Example",
045 *  "client_name#fr"             : "Mon Nouvel Exemple",
046 *  "logo_uri"                   : "https://client.example.org/newlogo.png"
047 *  "logo_uri#fr"                : "https://client.example.org/fr/newlogo.png"
048 * }
049 *
050 * </pre>
051 *
052 * <p>Related specifications:
053 *
054 * <ul>
055 *     <li>OAuth 2.0 Dynamic Client Registration Management Protocol
056 *         (draft-ietf-oauth-dyn-reg-management-04), section 2.3.
057 *     <li>OAuth 2.0 Dynamic Client Registration Protocol 
058 *         (draft-ietf-oauth-dyn-reg-20), section 2.
059 * </ul>
060 */
061@Immutable
062public class ClientUpdateRequest extends ProtectedResourceRequest {
063        
064        
065        /**
066         * The registered client ID.
067         */
068        private final ClientID id;
069        
070        
071        /**
072         * The client metadata.
073         */
074        private final ClientMetadata metadata;
075        
076        
077        /**
078         * The optional client secret.
079         */
080        private final Secret secret;
081        
082        
083        /**
084         * Creates a new client update request.
085         *
086         * @param uri         The URI of the client update endpoint. May be
087         *                    {@code null} if the {@link #toHTTPRequest()}
088         *                    method will not be used.
089         * @param accessToken The client registration access token. Must not be
090         *                    {@code null}.
091         * @param metadata    The client metadata. Must not be {@code null} and 
092         *                    must specify one or more redirection URIs.
093         * @param secret      The optional client secret, {@code null} if not
094         *                    specified.
095         */
096        public ClientUpdateRequest(final URI uri,
097                                   final ClientID id,
098                                   final BearerAccessToken accessToken,
099                                   final ClientMetadata metadata, 
100                                   final Secret secret) {
101
102                super(uri, accessToken);
103                
104                if (id == null)
105                        throw new IllegalArgumentException("The client identifier must not be null");
106                
107                this.id = id;
108
109                if (metadata == null)
110                        throw new IllegalArgumentException("The client metadata must not be null");
111                
112                this.metadata = metadata;
113                
114                this.secret = secret;
115        }
116        
117        
118        /**
119         * Gets the client ID. Corresponds to the {@code client_id} client
120         * registration parameter.
121         *
122         * @return The client ID, {@code null} if not specified.
123         */
124        public ClientID getClientID() {
125
126                return id;
127        }
128        
129        
130        /**
131         * Gets the associated client metadata.
132         *
133         * @return The client metadata.
134         */
135        public ClientMetadata getClientMetadata() {
136
137                return metadata;
138        }
139        
140        
141        /**
142         * Gets the client secret. Corresponds to the {@code client_secret} 
143         * registration parameters.
144         *
145         * @return The client secret, {@code null} if not specified.
146         */
147        public Secret getClientSecret() {
148
149                return secret;
150        }
151        
152        
153        @Override
154        public HTTPRequest toHTTPRequest()
155                throws SerializeException{
156                
157                if (getEndpointURI() == null)
158                        throw new SerializeException("The endpoint URI is not specified");
159
160                URL endpointURL;
161
162                try {
163                        endpointURL = getEndpointURI().toURL();
164
165                } catch (MalformedURLException e) {
166
167                        throw new SerializeException(e.getMessage(), e);
168                }
169
170                HTTPRequest httpRequest = new HTTPRequest(HTTPRequest.Method.PUT, endpointURL);
171
172                httpRequest.setAuthorization(getAccessToken().toAuthorizationHeader());
173
174                httpRequest.setContentType(CommonContentTypes.APPLICATION_JSON);
175                
176                JSONObject jsonObject = metadata.toJSONObject();
177                
178                jsonObject.put("client_id", id.getValue());
179                
180                if (secret != null)
181                        jsonObject.put("client_secret", secret.getValue());
182
183                httpRequest.setQuery(jsonObject.toString());
184
185                return httpRequest;
186        }
187        
188        
189        /**
190         * Parses a client update request from the specified HTTP PUT request.
191         *
192         * @param httpRequest The HTTP request. Must not be {@code null}.
193         *
194         * @return The client update request.
195         *
196         * @throws ParseException If the HTTP request couldn't be parsed to a 
197         *                        client update request.
198         */
199        public static ClientUpdateRequest parse(final HTTPRequest httpRequest)
200                throws ParseException {
201
202                httpRequest.ensureMethod(HTTPRequest.Method.PUT);
203                
204                BearerAccessToken accessToken = BearerAccessToken.parse(httpRequest.getAuthorization());
205                
206                JSONObject jsonObject = httpRequest.getQueryAsJSONObject();
207                
208                ClientID id = new ClientID(JSONObjectUtils.getString(jsonObject, "client_id"));
209
210                ClientMetadata metadata = ClientMetadata.parse(jsonObject);
211                
212                Secret clientSecret = null;
213                
214                if (jsonObject.get("client_secret") != null)
215                        clientSecret = new Secret(JSONObjectUtils.getString(jsonObject, "client_secret"));
216                        
217                URI endpointURI;
218
219                try {
220                        endpointURI = httpRequest.getURL().toURI();
221
222                } catch (URISyntaxException e) {
223
224                        throw new ParseException(e.getMessage(), e);
225                }
226
227                return new ClientUpdateRequest(endpointURI, id, accessToken, metadata, clientSecret);
228        }
229}