001package com.nimbusds.oauth2.sdk;
002
003
004import java.net.URI;
005import java.net.URISyntaxException;
006import java.util.LinkedHashMap;
007import java.util.Map;
008
009import net.jcip.annotations.Immutable;
010
011
012/**
013 * Authorisation code grant. Used in access token requests with an
014 * authorisation code.
015 *
016 * <p>Related specifications:
017 *
018 * <ul>
019 *     <li>OAuth 2.0 (RFC 6749), section 4.1.3.
020 * </ul>
021 */
022@Immutable
023public class AuthorizationCodeGrant extends AuthorizationGrant {
024
025
026        /**
027         * The grant type.
028         */
029        public static final GrantType GRANT_TYPE = GrantType.AUTHORIZATION_CODE;
030
031
032        /**
033         * The authorisation code received from the authorisation server.
034         */
035        private final AuthorizationCode code;
036
037
038        /**
039         * The conditionally required redirection URI in the initial
040         * authorisation request.
041         */
042        private final URI redirectURI;
043
044
045        /**
046         * Creates a new authorisation code grant.
047         *
048         * @param code        The authorisation code. Must not be {@code null}.
049         * @param redirectURI The redirection URI of the original authorisation
050         *                    request. Required if the {redirect_uri}
051         *                    parameter was included in the authorisation
052         *                    request, else {@code null}.
053         */
054        public AuthorizationCodeGrant(final AuthorizationCode code,
055                                      final URI redirectURI) {
056
057                super(GRANT_TYPE);
058
059                if (code == null)
060                        throw new IllegalArgumentException("The authorisation code must not be null");
061
062                this.code = code;
063
064                this.redirectURI = redirectURI;
065        }
066
067
068        /**
069         * Gets the authorisation code.
070         *
071         * @return The authorisation code.
072         */
073        public AuthorizationCode getAuthorizationCode() {
074
075                return code;
076        }
077
078
079        /**
080         * Gets the redirection URI of the original authorisation request.
081         *
082         * @return The redirection URI, {@code null} if the
083         *         {@code redirect_uri} parameter was not included in the
084         *         original authorisation request.
085         */
086        public URI getRedirectionURI() {
087
088                return redirectURI;
089        }
090
091
092        @Override
093        public Map<String,String> toParameters() {
094
095                Map<String,String> params = new LinkedHashMap<>();
096                params.put("grant_type", GRANT_TYPE.getValue());
097                params.put("code", code.getValue());
098
099                if (redirectURI != null)
100                        params.put("redirect_uri", redirectURI.toString());
101
102                return params;
103        }
104
105
106        /**
107         * Parses an authorisation code grant from the specified parameters.
108         *
109         * <p>Example:
110         *
111         * <pre>
112         * grant_type=authorization_code
113         * code=SplxlOBeZQQYbYS6WxSbIA
114         * redirect_uri=https://Fclient.example.com/cb
115         * </pre>
116         *
117         * @param params The parameters.
118         *
119         * @return The authorisation code grant.
120         *
121         * @throws ParseException If parsing failed.
122         */
123        public static AuthorizationCodeGrant parse(final Map<String,String> params)
124                throws ParseException {
125
126                // Parse grant type
127                String grantTypeString = params.get("grant_type");
128
129                if (grantTypeString == null) {
130                        String msg = "Missing \"grant_type\" parameter";
131                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg));
132                }
133
134                if (! GrantType.parse(grantTypeString).equals(GRANT_TYPE)) {
135                        String msg = "The \"grant_type\" must be \"" + GRANT_TYPE + "\"";
136                        throw new ParseException(msg, OAuth2Error.UNSUPPORTED_GRANT_TYPE.appendDescription(": " + msg));
137                }
138
139                // Parse authorisation code
140                String codeString = params.get("code");
141
142                if (codeString == null || codeString.trim().isEmpty()) {
143                        String msg = "Missing or empty \"code\" parameter";
144                        throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg));
145                }
146
147                AuthorizationCode code = new AuthorizationCode(codeString);
148
149                // Parse optional redirection URI
150                String redirectURIString = params.get("redirect_uri");
151
152                URI redirectURI = null;
153
154                if (redirectURIString != null) {
155                        try {
156                                redirectURI = new URI(redirectURIString);
157                        } catch (URISyntaxException e) {
158                                String msg = "Invalid \"redirect_uri\" parameter: " + e.getMessage();
159                                throw new ParseException(msg, OAuth2Error.INVALID_REQUEST.appendDescription(": " + msg), e);
160                        }
161                }
162
163                return new AuthorizationCodeGrant(code, redirectURI);
164        }
165}