001package com.nimbusds.oauth2.sdk;
002
003
004import java.net.MalformedURLException;
005import java.net.URL;
006import java.util.LinkedHashMap;
007import java.util.Map;
008
009import net.jcip.annotations.Immutable;
010
011import com.nimbusds.oauth2.sdk.id.ClientID;
012
013
014/**
015 * Authorisation code grant. Used in access token requests with an
016 * authorisation code. This class is immutable.
017 *
018 * <p>Related specifications:
019 *
020 * <ul>
021 *     <li>OAuth 2.0 (RFC 6749), section 4.1.3.
022 * </ul>
023 *
024 * @author Vladimir Dzhuvinov
025 */
026@Immutable
027public final class AuthorizationCodeGrant extends AuthorizationGrant {
028
029
030        /**
031         * The associated grant type.
032         */
033        public static final GrantType GRANT_TYPE = GrantType.AUTHORIZATION_CODE;
034
035
036        /**
037         * The authorisation code received from the authorisation server.
038         */
039        private final AuthorizationCode code;
040
041
042        /**
043         * The conditionally required redirection URI in the initial
044         * authorisation request.
045         */
046        private final URL redirectURI;
047
048
049        /**
050         * The conditionally required client ID.
051         */
052        private final ClientID clientID;
053
054
055        /**
056         * Creates a new authorisation code grant. This constructor is
057         * intended for an authenticated access token requests (doesn't require
058         * the client identifier to be specified).
059         *
060         * @param code        The authorisation code. Must not be {@code null}.
061         * @param redirectURI The redirection URI of the original authorisation
062         *                    request. Required if the {redirect_uri}
063         *                    parameter was included in the authorisation
064         *                    request, else {@code null}.
065         */
066        public AuthorizationCodeGrant(final AuthorizationCode code,
067                                      final URL redirectURI) {
068
069                super(GRANT_TYPE);
070
071                if (code == null)
072                        throw new IllegalArgumentException("The authorisation code must not be null");
073
074                this.code = code;
075
076                this.redirectURI = redirectURI;
077
078                this.clientID = null;
079        }
080
081
082        /**
083         * Creates a new authorisation code grant. This constructor is
084         * intended for an unauthenticated access token request and requires
085         * the client identifier to be specified.
086         *
087         * @param code        The authorisation code. Must not be {@code null}.
088         * @param redirectURI The redirection URI of the original authorisation
089         *                    request, {@code null} if the {@code redirect_uri}
090         *                    parameter was not included in the authorisation
091         *                    request.
092         * @param clientID    The client identifier. Must not be {@code null}.
093         */
094        public AuthorizationCodeGrant(final AuthorizationCode code,
095                                      final URL redirectURI,
096                                      final ClientID clientID) {
097
098                super(GrantType.AUTHORIZATION_CODE);
099
100                if (code == null)
101                        throw new IllegalArgumentException("The authorisation code must not be null");
102
103                this.code = code;
104
105                this.redirectURI = redirectURI;
106
107                if (clientID == null)
108                        throw new IllegalArgumentException("The client identifier must not be null");
109
110                this.clientID = clientID;
111        }
112
113
114        /**
115         * Gets the authorisation code.
116         *
117         * @return The authorisation code.
118         */
119        public AuthorizationCode getAuthorizationCode() {
120
121                return code;
122        }
123
124
125        /**
126         * Gets the redirection URI of the original authorisation request.
127         *
128         * @return The redirection URI, {@code null} if the
129         *        {@code redirect_uri} parameter was not included in the
130         *        original authorisation request.
131         */
132        public URL getRedirectionURI() {
133
134                return redirectURI;
135        }
136
137
138        /**
139         * Gets the client identifier.
140         *
141         * @return The client identifier, {@code null} if not specified
142         *         (implies an authenticated access token request).
143         */
144        public ClientID getClientID() {
145
146                return clientID;
147        }
148
149
150        @Override
151        public Map<String,String> toParameters() {
152
153                Map<String,String> params = new LinkedHashMap<String,String>();
154
155                params.put("grant_type", GRANT_TYPE.getValue());
156
157                params.put("code", code.getValue());
158
159                if (redirectURI != null)
160                        params.put("redirect_uri", redirectURI.toString());
161
162                if (clientID != null)
163                        params.put("client_id", clientID.getValue());
164
165                return params;
166        }
167
168
169        /**
170         * Parses an authorisation code grant from the specified parameters.
171         *
172         * <p>Example:
173         *
174         * <pre>
175         * grant_type=authorization_code
176         * code=SplxlOBeZQQYbYS6WxSbIA
177         * redirect_uri=https://Fclient.example.com/cb
178         * </pre>
179         *
180         * @param params The parameters.
181         *
182         * @return The authorisation code grant.
183         *
184         * @throws ParseException If parsing failed.
185         */
186        public static AuthorizationCodeGrant parse(final Map<String,String> params)
187                throws ParseException {
188
189                // Parse grant type
190                String grantTypeString = params.get("grant_type");
191
192                if (grantTypeString == null)
193                        throw new ParseException("Missing \"grant_type\" parameter", OAuth2Error.INVALID_REQUEST);
194
195                GrantType grantType = new GrantType(grantTypeString);
196
197                if (! grantType.equals(GRANT_TYPE))
198                        throw new ParseException("The \"grant_type\" must be " + GRANT_TYPE, OAuth2Error.INVALID_GRANT);
199
200
201                // Parse authorisation code
202                String codeString = params.get("code");
203
204                if (codeString == null || codeString.trim().isEmpty())
205                        throw new ParseException("Missing or empty \"code\" parameter", OAuth2Error.INVALID_REQUEST);
206
207                AuthorizationCode code = new AuthorizationCode(codeString);
208
209
210                // Parse optional redirection URI
211                String redirectURIString = params.get("redirect_uri");
212
213                URL redirectURI = null;
214
215                if (redirectURIString != null) {
216
217                        try {
218                                redirectURI = new URL(redirectURIString);
219
220                        } catch (MalformedURLException e) {
221
222                                throw new ParseException("Invalid \"redirect_uri\" parameter: " + e.getMessage(), OAuth2Error.INVALID_REQUEST, e);
223                        }
224                }
225
226
227                // Parse optional client ID
228                String clientIDString = params.get("client_id");
229
230                ClientID clientID = null;
231
232                if (clientIDString != null && clientIDString.trim().length() > 0)
233                        clientID = new ClientID(clientIDString);
234
235                if (clientID == null)
236                        return new AuthorizationCodeGrant(code, redirectURI);
237                else
238                        return new AuthorizationCodeGrant(code, redirectURI, clientID);
239        }
240}