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