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. This
180         * is determined by the presence of a {@code code} value.
181         *
182         * @return {@code true} if a code flow is implied, else {@code false}.
183         */
184        public boolean impliesCodeFlow() {
185
186                return this.contains(Value.CODE);
187        }
188        
189        
190        /**
191         * Returns {@code true} if this response type implies an implicit flow.
192         * This is determined by the absence of a {@code code} value.
193         *
194         * @return {@code true} if an implicit flow is implied, else 
195         *         {@code false}.
196         */
197        public boolean impliesImplicitFlow() {
198        
199                return ! impliesCodeFlow();
200        }
201
202
203        /**
204         * Checks if this response type contains the specified string value.
205         *
206         * @param value The string value. Must not be {@code null}.
207         *
208         * @return {@code true} if the value is contained, else {@code false}.
209         */
210        public boolean contains(final String value) {
211
212                return contains(new Value(value));
213        }
214        
215        
216        /**
217         * Returns the string representation of this  authorisation response 
218         * type.
219         *
220         * <p>Example serialised response types:
221         *
222         * <pre>
223         * code
224         * token
225         * id_token
226         * id_token token
227         * code token
228         * code id_token
229         * code id_token token
230         * </pre>
231         *
232         * @return Space delimited string representing the authorisation 
233         *         response type.
234         */
235        @Override
236        public String toString() {
237        
238                StringBuilder sb = new StringBuilder();
239
240                for (ResponseType.Value v: this) {
241
242                        if (sb.length() > 0)
243                                sb.append(' ');
244
245                        sb.append(v.getValue());
246                }
247
248                return sb.toString();
249        }
250}