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 net.jcip.annotations.Immutable; 026import net.jcip.annotations.NotThreadSafe; 027 028import org.apache.commons.lang3.StringUtils; 029 030import com.nimbusds.oauth2.sdk.id.Identifier; 031 032 033/** 034 * Authorisation response type. Can be single-valued or multiple-valued. 035 * 036 * <p>The following helper methods can be used to find out the OAuth 2.0 037 * protocol flow that a particular response type implies: 038 * 039 * <ul> 040 * <li>{@link #impliesImplicitFlow} 041 * <li>{@link #impliesCodeFlow} 042 * </ul> 043 * 044 * <p>Example response type implying an authorisation code flow: 045 * 046 * <pre> 047 * ResponseType() rt = new ResponseType(); 048 * rt.add(ResponseType.Value.CODE); 049 * </pre> 050 * 051 * <p>Example response type from OpenID Connect specifying an ID token and an 052 * access token (implies implicit flow): 053 * 054 * <pre> 055 * ResponseType() rt = new ResponseType(); 056 * rt.add(OIDCResponseTypeValue.ID_TOKEN); 057 * rt.add(ResponseType.Value.TOKEN); 058 * </pre> 059 * 060 * <p>Related specifications: 061 * 062 * <ul> 063 * <li>OAuth 2.0 (RFC 6749), sections 3.1.1 and 4.1.1. 064 * <li>OAuth 2.0 Multiple Response Type Encoding Practices. 065 * </ul> 066 */ 067@NotThreadSafe 068public class ResponseType extends HashSet<ResponseType.Value> { 069 070 071 /** 072 * Authorisation response type value. 073 */ 074 @Immutable 075 public static final class Value extends Identifier { 076 077 /** 078 * Authorisation code. 079 */ 080 public static final Value CODE = new Value("code"); 081 082 083 /** 084 * Access token, with optional refresh token. 085 */ 086 public static final Value TOKEN = new Value("token"); 087 088 089 /** 090 * Creates a new response type value. 091 * 092 * @param value The response type value. Must not be 093 * {@code null} or empty string. 094 */ 095 public Value(final String value) { 096 097 super(value); 098 } 099 100 101 @Override 102 public boolean equals(final Object object) { 103 104 return object instanceof Value && 105 this.toString().equals(object.toString()); 106 } 107 } 108 109 110 /** 111 * Gets the default response type. 112 * 113 * @return The default response type, consisting of the value 114 * {@link ResponseType.Value#CODE}. 115 */ 116 public static ResponseType getDefault() { 117 118 ResponseType defaultResponseType = new ResponseType(); 119 defaultResponseType.add(ResponseType.Value.CODE); 120 return defaultResponseType; 121 } 122 123 124 /** 125 * Creates a new empty response type. 126 */ 127 public ResponseType() { 128 129 } 130 131 132 /** 133 * Creates a new response type with the specified string values. 134 * 135 * @param values The string values. Must not be {@code null}. 136 */ 137 public ResponseType(final String ... values) { 138 139 for (String v: values) 140 add(new Value(v)); 141 } 142 143 144 /** 145 * Creates a new response type with the specified values. 146 * 147 * @param values The values. Must not be {@code null}. 148 */ 149 public ResponseType(final Value ... values) { 150 151 addAll(Arrays.asList(values)); 152 } 153 154 155 /** 156 * Parses a set of authorisation response types. 157 * 158 * <p>Example serialised response type sets: 159 * 160 * <pre> 161 * code 162 * token 163 * id_token 164 * id_token token 165 * code token 166 * code id_token 167 * code id_token token 168 * </pre> 169 * 170 * @param s Space-delimited list of one or more authorisation response 171 * types. 172 * 173 * @return The authorisation response types set. 174 * 175 * @throws ParseException If the parsed string is {@code null} or 176 * empty. 177 */ 178 public static ResponseType parse(final String s) 179 throws ParseException { 180 181 if (StringUtils.isBlank(s)) 182 throw new ParseException("Null or empty response type string"); 183 184 ResponseType rt = new ResponseType(); 185 186 StringTokenizer st = new StringTokenizer(s, " "); 187 188 while (st.hasMoreTokens()) 189 rt.add(new ResponseType.Value(st.nextToken())); 190 191 return rt; 192 } 193 194 195 /** 196 * Returns {@code true} if this response type implies a code flow. 197 * 198 * @return {@code true} if a code flow is implied, else {@code false}. 199 */ 200 public boolean impliesCodeFlow() { 201 202 return this.contains(Value.CODE) && this.size() == 1; 203 } 204 205 206 /** 207 * Returns {@code true} if this response type implies an implicit flow. 208 * 209 * @return {@code true} if an implicit flow is implied, else 210 * {@code false}. 211 */ 212 public boolean impliesImplicitFlow() { 213 214 return ! impliesCodeFlow(); 215 } 216 217 218 /** 219 * Checks if this response type contains the specified string value. 220 * 221 * @param value The string value. Must not be {@code null}. 222 * 223 * @return {@code true} if the value is contained, else {@code false}. 224 */ 225 public boolean contains(final String value) { 226 227 return contains(new Value(value)); 228 } 229 230 231 /** 232 * Returns the string representation of this authorisation response 233 * type. 234 * 235 * <p>Example serialised response types: 236 * 237 * <pre> 238 * code 239 * token 240 * id_token 241 * id_token token 242 * code token 243 * code id_token 244 * code id_token token 245 * </pre> 246 * 247 * @return Space delimited string representing the authorisation 248 * response type. 249 */ 250 @Override 251 public String toString() { 252 253 StringBuilder sb = new StringBuilder(); 254 255 for (ResponseType.Value v: this) { 256 257 if (sb.length() > 0) 258 sb.append(' '); 259 260 sb.append(v.getValue()); 261 } 262 263 return sb.toString(); 264 } 265}