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.Collection; 023import java.util.HashSet; 024import java.util.StringTokenizer; 025 026import net.jcip.annotations.Immutable; 027import net.jcip.annotations.NotThreadSafe; 028 029import com.nimbusds.oauth2.sdk.id.Identifier; 030import com.nimbusds.oauth2.sdk.util.StringUtils; 031import com.nimbusds.openid.connect.sdk.OIDCResponseTypeValue; 032 033 034/** 035 * Authorisation response type. 036 * 037 * <p>Example response type implying an authorisation code flow: 038 * 039 * <pre> 040 * ResponseType rt = ResponseType.CODE; 041 * </pre> 042 * 043 * <p>Example response type from OpenID Connect specifying an ID token and an 044 * access token (implies implicit flow): 045 * 046 * <pre> 047 * ResponseType rt = ResponseType.IDTOKEN_TOKEN); 048 * </pre> 049 * 050 * <p>The following helper methods can be used to find out the implied OAuth 051 * 2.0 protocol flow for a response type: 052 * 053 * <ul> 054 * <li>{@link #impliesImplicitFlow} 055 * <li>{@link #impliesCodeFlow} 056 * </ul> 057 * 058 * <p>Related specifications: 059 * 060 * <ul> 061 * <li>OAuth 2.0 (RFC 6749), sections 3.1.1 and 4.1.1. 062 * <li>OAuth 2.0 Multiple Response Type Encoding Practices. 063 * </ul> 064 */ 065@NotThreadSafe 066public class ResponseType extends HashSet<ResponseType.Value> { 067 068 069 /** 070 * Constant for {@code response_type=code}. 071 */ 072 public static final ResponseType CODE = new ResponseType(true, Value.CODE); 073 074 075 /** 076 * Constant for {@code response_type=token}. 077 */ 078 public static final ResponseType TOKEN = new ResponseType(true, Value.TOKEN); 079 080 081 /** 082 * Constant for {@code response_type=id_token token}. 083 */ 084 public static final ResponseType IDTOKEN_TOKEN = new ResponseType(true, OIDCResponseTypeValue.ID_TOKEN, Value.TOKEN); 085 086 087 /** 088 * Constant for {@code response_type=id_token}. 089 */ 090 public static final ResponseType IDTOKEN = new ResponseType(true, OIDCResponseTypeValue.ID_TOKEN); 091 092 093 /** 094 * Constant for {@code response_type=code id_token}. 095 */ 096 public static final ResponseType CODE_IDTOKEN = new ResponseType(true, Value.CODE, OIDCResponseTypeValue.ID_TOKEN); 097 098 099 /** 100 * Constant for {@code response_type=code token}. 101 */ 102 public static final ResponseType CODE_TOKEN = new ResponseType(true, Value.CODE, Value.TOKEN); 103 104 105 /** 106 * Constant for {@code response_type=code id_token token}. 107 */ 108 public static final ResponseType CODE_IDTOKEN_TOKEN = new ResponseType(true, Value.CODE, OIDCResponseTypeValue.ID_TOKEN, Value.TOKEN); 109 110 111 private static final long serialVersionUID = 1351973244616920112L; 112 113 114 /** 115 * Authorisation response type value. 116 */ 117 @Immutable 118 public static final class Value extends Identifier { 119 120 /** 121 * Authorisation code. 122 */ 123 public static final Value CODE = new Value("code"); 124 125 126 /** 127 * Access token, with optional refresh token. 128 */ 129 public static final Value TOKEN = new Value("token"); 130 131 132 private static final long serialVersionUID = 5339971450891463852L; 133 134 135 /** 136 * Creates a new response type value. 137 * 138 * @param value The response type value. Must not be 139 * {@code null} or empty string. 140 */ 141 public Value(final String value) { 142 143 super(value); 144 } 145 146 147 @Override 148 public boolean equals(final Object object) { 149 150 return object instanceof Value && 151 this.toString().equals(object.toString()); 152 } 153 } 154 155 156 /** 157 * Gets the default response type. 158 * 159 * @return The default response type, consisting of the value 160 * {@link ResponseType.Value#CODE}. 161 */ 162 public static ResponseType getDefault() { 163 164 return ResponseType.CODE; 165 } 166 167 168 /** 169 * If {@code true} flags the response type as unmodifiable. 170 */ 171 private final boolean unmodifiable; 172 173 174 /** 175 * Creates a new empty response type. 176 */ 177 public ResponseType() { 178 super(); 179 unmodifiable = false; 180 } 181 182 183 /** 184 * Creates a new response type with the specified string values. 185 * 186 * @param values The string values. Must not be {@code null}. 187 */ 188 public ResponseType(final String ... values) { 189 190 for (String v: values) { 191 add(new Value(v)); 192 } 193 194 unmodifiable = false; 195 } 196 197 198 /** 199 * Creates a new response type with the specified values. 200 * 201 * @param values The values. Must not be {@code null}. 202 */ 203 public ResponseType(final Value ... values) { 204 this(false, values); 205 } 206 207 208 /** 209 * Creates a new response type with the specified values. 210 * 211 * @param unmodifiable If {@code true} flags the response type as 212 * unmodifiable. 213 * @param values The values. Must not be {@code null}. 214 */ 215 private ResponseType(final boolean unmodifiable, final Value ... values) { 216 super(Arrays.asList(values)); 217 this.unmodifiable = unmodifiable; 218 } 219 220 221 /** 222 * Parses a set of authorisation response types. 223 * 224 * @param s Space-delimited list of one or more authorisation response 225 * types. 226 * 227 * @return The authorisation response types set. 228 * 229 * @throws ParseException If the parsed string is {@code null} or 230 * empty. 231 */ 232 public static ResponseType parse(final String s) 233 throws ParseException { 234 235 if (StringUtils.isBlank(s)) 236 throw new ParseException("Null or empty response type string"); 237 238 ResponseType rt = new ResponseType(); 239 240 StringTokenizer st = new StringTokenizer(s, " "); 241 242 while (st.hasMoreTokens()) 243 rt.add(new ResponseType.Value(st.nextToken())); 244 245 return rt; 246 } 247 248 249 /** 250 * Returns {@code true} if this response type implies an authorisation 251 * code flow. 252 * 253 * <p>Code flow response_type values: code 254 * 255 * @return {@code true} if a code flow is implied, else {@code false}. 256 */ 257 public boolean impliesCodeFlow() { 258 259 return this.equals(new ResponseType(Value.CODE)); 260 } 261 262 263 /** 264 * Returns {@code true} if this response type implies an implicit flow. 265 * 266 * <p>Implicit flow response_type values: token, id_token token, 267 * id_token 268 * 269 * @return {@code true} if an implicit flow is implied, else 270 * {@code false}. 271 */ 272 public boolean impliesImplicitFlow() { 273 274 return 275 this.equals(new ResponseType(Value.TOKEN)) || 276 this.equals(new ResponseType(OIDCResponseTypeValue.ID_TOKEN, Value.TOKEN)) || 277 this.equals(new ResponseType(OIDCResponseTypeValue.ID_TOKEN)); 278 } 279 280 281 /** 282 * Returns {@code true} if this response type implies an OpenID Connect 283 * hybrid flow. 284 * 285 * <p>Hybrid flow response_type values: code id_token, code token, 286 * code id_token token 287 * 288 * @return {@code true} if a hybrid flow is implied, else 289 * {@code false}. 290 */ 291 public boolean impliesHybridFlow() { 292 293 return 294 this.equals(new ResponseType(Value.CODE, OIDCResponseTypeValue.ID_TOKEN)) || 295 this.equals(new ResponseType(Value.CODE, Value.TOKEN)) || 296 this.equals(new ResponseType(Value.CODE, OIDCResponseTypeValue.ID_TOKEN, Value.TOKEN)); 297 } 298 299 300 /** 301 * Checks if this response type contains the specified string value. 302 * 303 * @param value The string value. Must not be {@code null}. 304 * 305 * @return {@code true} if the value is contained, else {@code false}. 306 */ 307 public boolean contains(final String value) { 308 309 return contains(new Value(value)); 310 } 311 312 313 /** 314 * Returns the string representation of this authorisation response 315 * type. 316 * 317 * <p>Example serialised response types: 318 * 319 * <pre> 320 * code 321 * token 322 * id_token 323 * id_token token 324 * code token 325 * code id_token 326 * code id_token token 327 * </pre> 328 * 329 * @return Space delimited string representing the authorisation 330 * response type. 331 */ 332 @Override 333 public String toString() { 334 335 StringBuilder sb = new StringBuilder(); 336 337 for (ResponseType.Value v: this) { 338 339 if (sb.length() > 0) 340 sb.append(' '); 341 342 sb.append(v.getValue()); 343 } 344 345 return sb.toString(); 346 } 347 348 349 @Override 350 public boolean add(Value value) { 351 if (unmodifiable) { 352 throw new UnsupportedOperationException(); 353 } 354 return super.add(value); 355 } 356 357 358 @Override 359 public boolean remove(Object o) { 360 if (unmodifiable) { 361 throw new UnsupportedOperationException(); 362 } 363 return super.remove(o); 364 } 365 366 367 @Override 368 public void clear() { 369 if (unmodifiable) { 370 throw new UnsupportedOperationException(); 371 } 372 super.clear(); 373 } 374 375 376 @Override 377 public boolean removeAll(Collection<?> c) { 378 if (unmodifiable) { 379 throw new UnsupportedOperationException(); 380 } 381 return super.removeAll(c); 382 } 383 384 385 @Override 386 public boolean addAll(Collection<? extends Value> c) { 387 if (unmodifiable) { 388 throw new UnsupportedOperationException(); 389 } 390 return super.addAll(c); 391 } 392 393 394 @Override 395 public boolean retainAll(Collection<?> c) { 396 if (unmodifiable) { 397 throw new UnsupportedOperationException(); 398 } 399 return super.retainAll(c); 400 } 401}