001package com.nimbusds.oauth2.sdk;
002
003
004import java.util.Arrays;
005import java.util.HashSet;
006import java.util.StringTokenizer;
007
008import net.jcip.annotations.Immutable;
009import net.jcip.annotations.NotThreadSafe;
010
011import org.apache.commons.lang3.StringUtils;
012
013import com.nimbusds.oauth2.sdk.id.Identifier;
014
015
016/**
017 * Authorisation response type. Can be single-valued or multiple-valued.
018 *
019 * <p>The following helper methods can be used to find out the OAuth 2.0
020 * protocol flow that a particular response type implies:
021 *
022 * <ul>
023 *     <li>{@link #impliesImplicitFlow}
024 *     <li>{@link #impliesCodeFlow}
025 * </ul>
026 *
027 * <p>Example response type implying an authorisation code flow:
028 *
029 * <pre>
030 * ResponseType() rt = new ResponseType();
031 * rt.add(ResponseType.Value.CODE);
032 * </pre>
033 *
034 * <p>Example response type from OpenID Connect specifying an ID token and an 
035 * access token (implies implicit flow):
036 *
037 * <pre>
038 * ResponseType() rt = new ResponseType();
039 * rt.add(OIDCResponseTypeValue.ID_TOKEN);
040 * rt.add(ResponseType.Value.TOKEN);
041 * </pre>
042 *
043 * <p>Related specifications:
044 *
045 * <ul>
046 *     <li>OAuth 2.0 (RFC 6749), sections 3.1.1 and 4.1.1.
047 *     <li>OAuth 2.0 Multiple Response Type Encoding Practices.
048 * </ul>
049 */
050@NotThreadSafe
051public class ResponseType extends HashSet<ResponseType.Value> {
052        
053        
054        /**
055         * Authorisation response type value.
056         */
057        @Immutable
058        public static final class Value extends Identifier {
059
060                /**
061                 * Authorisation code.
062                 */
063                public static final Value CODE = new Value("code");
064
065                
066                /**
067                 * Access token, with optional refresh token.
068                 */
069                public static final Value TOKEN = new Value("token");
070
071                
072                /**
073                 * Creates a new response type value.
074                 *
075                 * @param value The response type value. Must not be
076                 *              {@code null} or empty string.
077                 */
078                public Value(final String value) {
079
080                        super(value);
081                }
082                
083                
084                @Override
085                public boolean equals(final Object object) {
086
087                        return object instanceof Value &&
088                               this.toString().equals(object.toString());
089                }
090        }
091
092        
093        /**
094         *  Gets the default response type.
095         * 
096         * @return The default response type, consisting of the value
097         *         {@link ResponseType.Value#CODE}.
098         */
099        public static ResponseType getDefault() {
100                
101                ResponseType defaultResponseType = new ResponseType();
102                defaultResponseType.add(ResponseType.Value.CODE);
103                return defaultResponseType;
104        }
105
106        
107        /**
108         * Creates a new empty response type.
109         */
110        public ResponseType() {
111                
112        }
113
114
115        /**
116         * Creates a new response type with the specified string values.
117         *
118         * @param values The string values. Must not be {@code null}.
119         */
120        public ResponseType(final String ... values) {
121
122                for (String v: values)
123                        add(new Value(v));
124        }
125
126
127        /**
128         * Creates a new response type with the specified values.
129         *
130         * @param values The values. Must not be {@code null}.
131         */
132        public ResponseType(final Value ... values) {
133
134                addAll(Arrays.asList(values));
135        }
136        
137        
138        /**
139         * Parses a set of authorisation response types.
140         *
141         * <p>Example serialised response type sets:
142         *
143         * <pre>
144         * code
145         * token
146         * id_token
147         * id_token token
148         * code token
149         * code id_token
150         * code id_token token
151         * </pre>
152         *
153         * @param s Space-delimited list of one or more authorisation response 
154         *          types.
155         *
156         * @return The authorisation response types set.
157         *
158         * @throws ParseException If the parsed string is {@code null} or 
159         *                        empty.
160         */
161        public static ResponseType parse(final String s)
162                throws ParseException {
163        
164                if (StringUtils.isBlank(s))
165                        throw new ParseException("Null or empty response type string");
166        
167                ResponseType rt = new ResponseType();
168                
169                StringTokenizer st = new StringTokenizer(s, " ");
170
171                while (st.hasMoreTokens())
172                        rt.add(new ResponseType.Value(st.nextToken()));
173                
174                return rt;
175        }
176        
177        
178        /**
179         * Returns {@code true} if this response type implies a code flow.
180         *
181         * @return {@code true} if a code flow is implied, else {@code false}.
182         */
183        public boolean impliesCodeFlow() {
184
185                return this.contains(Value.CODE) && this.size() == 1;
186        }
187        
188        
189        /**
190         * Returns {@code true} if this response type implies an implicit flow.
191         *
192         * @return {@code true} if an implicit flow is implied, else 
193         *         {@code false}.
194         */
195        public boolean impliesImplicitFlow() {
196        
197                return ! impliesCodeFlow();
198        }
199
200
201        /**
202         * Checks if this response type contains the specified string value.
203         *
204         * @param value The string value. Must not be {@code null}.
205         *
206         * @return {@code true} if the value is contained, else {@code false}.
207         */
208        public boolean contains(final String value) {
209
210                return contains(new Value(value));
211        }
212        
213        
214        /**
215         * Returns the string representation of this  authorisation response 
216         * type.
217         *
218         * <p>Example serialised response types:
219         *
220         * <pre>
221         * code
222         * token
223         * id_token
224         * id_token token
225         * code token
226         * code id_token
227         * code id_token token
228         * </pre>
229         *
230         * @return Space delimited string representing the authorisation 
231         *         response type.
232         */
233        @Override
234        public String toString() {
235        
236                StringBuilder sb = new StringBuilder();
237
238                for (ResponseType.Value v: this) {
239
240                        if (sb.length() > 0)
241                                sb.append(' ');
242
243                        sb.append(v.getValue());
244                }
245
246                return sb.toString();
247        }
248}