001package com.nimbusds.oauth2.sdk;
002
003
004import java.util.HashSet;
005import java.util.StringTokenizer;
006
007import net.jcip.annotations.Immutable;
008import net.jcip.annotations.NotThreadSafe;
009
010import org.apache.commons.lang3.StringUtils;
011
012import com.nimbusds.oauth2.sdk.id.Identifier;
013
014
015/**
016 * Authorisation response type. Can be single-valued or multiple-valued. This
017 * class is not thread-safe.
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 * @author Vladimir Dzhuvinov
051 */
052@NotThreadSafe
053public class ResponseType extends HashSet<ResponseType.Value> {
054        
055        
056        /**
057         * Authorisation response type value. This class is immutable.
058         */
059        @Immutable
060        public static final class Value extends Identifier {
061
062                /**
063                 * Authorisation code.
064                 */
065                public static final Value CODE = new Value("code");
066
067                
068                /**
069                 * Access token, with optional refresh token.
070                 */
071                public static final Value TOKEN = new Value("token");
072
073                
074                /**
075                 * Creates a new response type value.
076                 *
077                 * @param value The response type value. Must not be
078                 *              {@code null} or empty string.
079                 */
080                public Value(final String value) {
081
082                        super(value);
083                }
084                
085                
086                @Override
087                public boolean equals(final Object object) {
088
089                        return object instanceof Value &&
090                               this.toString().equals(object.toString());
091                }
092        }
093
094        
095        /**
096         *  Gets the default response type.
097         * 
098         * @return The default response type, consisting of the value
099         *         {@link ResponseType.Value#CODE}.
100         */
101        public static ResponseType getDefault() {
102                
103                ResponseType defaultResponseType = new ResponseType();
104                defaultResponseType.add(ResponseType.Value.CODE);
105                return defaultResponseType;
106        }
107
108        
109        /**
110         * Creates a new empty response type.
111         */
112        public ResponseType() {
113                
114        }
115        
116        
117        /**
118         * Parses a set of authorisation response types.
119         *
120         * <p>Example serialised response type sets:
121         *
122         * <pre>
123         * code
124         * token
125         * id_token
126         * id_token token
127         * code token
128         * code id_token
129         * code id_token token
130         * </pre>
131         *
132         * @param s Space-delimited list of one or more authorisation response 
133         *          types.
134         *
135         * @return The authorisation response types set.
136         *
137         * @throws ParseException If the parsed string is {@code null} or 
138         *                        empty.
139         */
140        public static ResponseType parse(final String s)
141                throws ParseException {
142        
143                if (StringUtils.isBlank(s))
144                        throw new ParseException("Null or empty response type string");
145        
146                ResponseType rt = new ResponseType();
147                
148                StringTokenizer st = new StringTokenizer(s, " ");
149
150                while (st.hasMoreTokens())
151                        rt.add(new ResponseType.Value(st.nextToken()));
152                
153                return rt;
154        }
155        
156        
157        /**
158         * Returns {@code true} if this response type implies a code flow. This
159         * is determined by the presence of a {@code code} value.
160         *
161         * @return {@code true} if a code flow is implied, else {@code false}.
162         */
163        public boolean impliesCodeFlow() {
164        
165                if (this.contains(ResponseType.Value.CODE))
166                        return true;
167                else
168                        return false;
169        }
170        
171        
172        /**
173         * Returns {@code true} if this response type implies an implicit flow.
174         * This is determined by the absence of a {@code code} value.
175         *
176         * @return {@code true} if an implicit flow is implied, else 
177         *         {@code false}.
178         */
179        public boolean impliesImplicitFlow() {
180        
181                return ! impliesCodeFlow();
182        }
183        
184        
185        /**
186         * Returns the string representation of this  authorisation response 
187         * type.
188         *
189         * <p>Example serialised response types:
190         *
191         * <pre>
192         * code
193         * token
194         * id_token
195         * id_token token
196         * code token
197         * code id_token
198         * code id_token token
199         * </pre>
200         *
201         * @return Space delimited string representing the authorisation 
202         *         response type.
203         */
204        @Override
205        public String toString() {
206        
207                StringBuilder sb = new StringBuilder();
208
209                for (ResponseType.Value v: this) {
210
211                        if (sb.length() > 0)
212                                sb.append(' ');
213
214                        sb.append(v.getValue());
215                }
216
217                return sb.toString();
218        }
219}