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}