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