001/*
002 * oauth2-oidc-sdk
003 *
004 * Copyright 2012-2016, Connect2id Ltd and contributors.
005 *
006 * Licensed under the Apache License, Version 2.0 (the "License"); you may not use
007 * this file except in compliance with the License. You may obtain a copy of the
008 * License at
009 *
010 *    http://www.apache.org/licenses/LICENSE-2.0
011 *
012 * Unless required by applicable law or agreed to in writing, software distributed
013 * under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR
014 * CONDITIONS OF ANY KIND, either express or implied. See the License for the
015 * specific language governing permissions and limitations under the License.
016 */
017
018package com.nimbusds.oauth2.sdk;
019
020
021import java.util.Arrays;
022import java.util.HashSet;
023import java.util.StringTokenizer;
024
025import com.nimbusds.oauth2.sdk.id.Identifier;
026import com.nimbusds.oauth2.sdk.util.StringUtils;
027import com.nimbusds.openid.connect.sdk.OIDCResponseTypeValue;
028import net.jcip.annotations.Immutable;
029import net.jcip.annotations.NotThreadSafe;
030
031
032/**
033 * Authorisation response type. Can be single-valued or multiple-valued.
034 *
035 * <p>The following helper methods can be used to find out the OAuth 2.0
036 * protocol flow that a particular response type implies:
037 *
038 * <ul>
039 *     <li>{@link #impliesImplicitFlow}
040 *     <li>{@link #impliesCodeFlow}
041 * </ul>
042 *
043 * <p>Example response type implying an authorisation code flow:
044 *
045 * <pre>
046 * ResponseType() rt = new ResponseType();
047 * rt.add(ResponseType.Value.CODE);
048 * </pre>
049 *
050 * <p>Example response type from OpenID Connect specifying an ID token and an 
051 * access token (implies implicit flow):
052 *
053 * <pre>
054 * ResponseType() rt = new ResponseType();
055 * rt.add(OIDCResponseTypeValue.ID_TOKEN);
056 * rt.add(ResponseType.Value.TOKEN);
057 * </pre>
058 *
059 * <p>Related specifications:
060 *
061 * <ul>
062 *     <li>OAuth 2.0 (RFC 6749), sections 3.1.1 and 4.1.1.
063 *     <li>OAuth 2.0 Multiple Response Type Encoding Practices.
064 * </ul>
065 */
066@NotThreadSafe
067public class ResponseType extends HashSet<ResponseType.Value> {
068        
069        
070        /**
071         * Authorisation response type value.
072         */
073        @Immutable
074        public static final class Value extends Identifier {
075
076                /**
077                 * Authorisation code.
078                 */
079                public static final Value CODE = new Value("code");
080
081                
082                /**
083                 * Access token, with optional refresh token.
084                 */
085                public static final Value TOKEN = new Value("token");
086
087                
088                /**
089                 * Creates a new response type value.
090                 *
091                 * @param value The response type value. Must not be
092                 *              {@code null} or empty string.
093                 */
094                public Value(final String value) {
095
096                        super(value);
097                }
098                
099                
100                @Override
101                public boolean equals(final Object object) {
102
103                        return object instanceof Value &&
104                               this.toString().equals(object.toString());
105                }
106        }
107
108        
109        /**
110         *  Gets the default response type.
111         * 
112         * @return The default response type, consisting of the value
113         *         {@link ResponseType.Value#CODE}.
114         */
115        public static ResponseType getDefault() {
116                
117                ResponseType defaultResponseType = new ResponseType();
118                defaultResponseType.add(ResponseType.Value.CODE);
119                return defaultResponseType;
120        }
121
122        
123        /**
124         * Creates a new empty response type.
125         */
126        public ResponseType() {
127                
128        }
129
130
131        /**
132         * Creates a new response type with the specified string values.
133         *
134         * @param values The string values. Must not be {@code null}.
135         */
136        public ResponseType(final String ... values) {
137
138                for (String v: values)
139                        add(new Value(v));
140        }
141
142
143        /**
144         * Creates a new response type with the specified values.
145         *
146         * @param values The values. Must not be {@code null}.
147         */
148        public ResponseType(final Value ... values) {
149
150                addAll(Arrays.asList(values));
151        }
152        
153        
154        /**
155         * Parses a set of authorisation response types.
156         *
157         * <p>Example serialised response type sets:
158         *
159         * <pre>
160         * code
161         * token
162         * id_token
163         * id_token token
164         * code token
165         * code id_token
166         * code id_token token
167         * </pre>
168         *
169         * @param s Space-delimited list of one or more authorisation response 
170         *          types.
171         *
172         * @return The authorisation response types set.
173         *
174         * @throws ParseException If the parsed string is {@code null} or 
175         *                        empty.
176         */
177        public static ResponseType parse(final String s)
178                throws ParseException {
179        
180                if (StringUtils.isBlank(s))
181                        throw new ParseException("Null or empty response type string");
182        
183                ResponseType rt = new ResponseType();
184                
185                StringTokenizer st = new StringTokenizer(s, " ");
186
187                while (st.hasMoreTokens())
188                        rt.add(new ResponseType.Value(st.nextToken()));
189                
190                return rt;
191        }
192        
193        
194        /**
195         * Returns {@code true} if this response type implies an authorisation
196         * code flow.
197         *
198         * <p>Code flow response_type values: code
199         *
200         * @return {@code true} if a code flow is implied, else {@code false}.
201         */
202        public boolean impliesCodeFlow() {
203                
204                return this.equals(new ResponseType(Value.CODE));
205        }
206        
207        
208        /**
209         * Returns {@code true} if this response type implies an implicit flow.
210         *
211         * <p>Implicit flow response_type values: token, id_token token,
212         * id_token
213         *
214         * @return {@code true} if an implicit flow is implied, else 
215         *         {@code false}.
216         */
217        public boolean impliesImplicitFlow() {
218        
219                return
220                        this.equals(new ResponseType(Value.TOKEN)) ||
221                        this.equals(new ResponseType(OIDCResponseTypeValue.ID_TOKEN, Value.TOKEN)) ||
222                        this.equals(new ResponseType(OIDCResponseTypeValue.ID_TOKEN));
223        }
224        
225        
226        /**
227         * Returns {@code true} if this response type implies an OpenID Connect
228         * hybrid flow.
229         *
230         * <p>Hybrid flow response_type values: code id_token, code token,
231         * code id_token token
232         *
233         * @return {@code true} if a hybrid flow is implied, else
234         *         {@code false}.
235         */
236        public boolean impliesHybridFlow() {
237        
238                return
239                        this.equals(new ResponseType(Value.CODE, OIDCResponseTypeValue.ID_TOKEN)) ||
240                        this.equals(new ResponseType(Value.CODE, Value.TOKEN)) ||
241                        this.equals(new ResponseType(Value.CODE, OIDCResponseTypeValue.ID_TOKEN, Value.TOKEN));
242        }
243
244
245        /**
246         * Checks if this response type contains the specified string value.
247         *
248         * @param value The string value. Must not be {@code null}.
249         *
250         * @return {@code true} if the value is contained, else {@code false}.
251         */
252        public boolean contains(final String value) {
253
254                return contains(new Value(value));
255        }
256        
257        
258        /**
259         * Returns the string representation of this  authorisation response 
260         * type.
261         *
262         * <p>Example serialised response types:
263         *
264         * <pre>
265         * code
266         * token
267         * id_token
268         * id_token token
269         * code token
270         * code id_token
271         * code id_token token
272         * </pre>
273         *
274         * @return Space delimited string representing the authorisation 
275         *         response type.
276         */
277        @Override
278        public String toString() {
279        
280                StringBuilder sb = new StringBuilder();
281
282                for (ResponseType.Value v: this) {
283
284                        if (sb.length() > 0)
285                                sb.append(' ');
286
287                        sb.append(v.getValue());
288                }
289
290                return sb.toString();
291        }
292}