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}