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.openid.connect.sdk; 019 020 021import com.nimbusds.oauth2.sdk.ParseException; 022import com.nimbusds.oauth2.sdk.util.StringUtils; 023import net.jcip.annotations.NotThreadSafe; 024 025import java.util.*; 026 027 028/** 029 * Prompts for end-user re-authentication and consent. 030 * 031 * <p>Related specifications: 032 * 033 * <ul> 034 * <li>OpenID Connect Core 1.0 035 * <li>Initiating User Registration via OpenID Connect 1.0 036 * </ul> 037 */ 038@NotThreadSafe 039public class Prompt extends LinkedHashSet<Prompt.Type> { 040 041 042 private static final long serialVersionUID = -3672900533669609699L; 043 044 045 /** 046 * Enumeration of the prompt types. 047 */ 048 public enum Type { 049 050 051 /** 052 * The authorisation server must not display any authentication 053 * or consent UI pages. An error is returned if the end user is 054 * not already authenticated or the client does not have 055 * pre-configured consent for the requested {@code scope}. This 056 * can be used as a method to check for existing authentication 057 * and / or consent. 058 */ 059 NONE, 060 061 062 /** 063 * The authorisation server must prompt the end-user for 064 * re-authentication. 065 */ 066 LOGIN, 067 068 069 /** 070 * The authorisation server must prompt the end-user for 071 * consent before returning information to the client. 072 */ 073 CONSENT, 074 075 076 /** 077 * The authorisation server must prompt the end-user to select 078 * a user account. This allows a user who has multiple accounts 079 * at the authorisation server to select amongst the multiple 080 * accounts that they may have current sessions for. 081 */ 082 SELECT_ACCOUNT, 083 084 085 /** 086 * The client desires the OpenID provider to present the 087 * end-user with an account creation user interface instead of 088 * the normal login flow. Care must be taken if combining this 089 * value with other prompt values. Mutually exclusive 090 * conditions can arise, so it is RECOMMENDED that create not 091 * be present with any other values. 092 */ 093 CREATE; 094 095 096 /** 097 * Returns the string identifier of this prompt type. 098 * 099 * @return The string identifier. 100 */ 101 @Override 102 public String toString() { 103 104 return super.toString().toLowerCase(); 105 } 106 107 108 /** 109 * Parses a prompt type. 110 * 111 * @param s The string to parse. 112 * 113 * @return The prompt type. 114 * 115 * @throws ParseException If the parsed string is {@code null} 116 * or doesn't match a prompt type. 117 */ 118 public static Type parse(final String s) 119 throws ParseException { 120 121 if (StringUtils.isBlank(s)) 122 throw new ParseException("Null or empty prompt type string"); 123 124 switch (s) { 125 case "none": 126 return NONE; 127 case "login": 128 return LOGIN; 129 case "consent": 130 return CONSENT; 131 case "select_account": 132 return SELECT_ACCOUNT; 133 case "create": 134 return CREATE; 135 default: 136 throw new ParseException("Unknown prompt type: " + s); 137 } 138 } 139 } 140 141 142 /** 143 * Creates a new empty prompt. 144 */ 145 public Prompt() { 146 147 // Nothing to do 148 } 149 150 151 /** 152 * Creates a new prompt with the specified types. 153 * 154 * @param type The prompt types. 155 */ 156 public Prompt(final Type ... type) { 157 158 addAll(Arrays.asList(type)); 159 } 160 161 162 /** 163 * Creates a new prompt with the specified type values. 164 * 165 * @param values The prompt type values. 166 * 167 * @throws java.lang.IllegalArgumentException If the type value is 168 * invalid. 169 */ 170 public Prompt(final String ... values) { 171 172 for (String v: values) { 173 174 try { 175 add(Type.parse(v)); 176 177 } catch (ParseException e) { 178 179 throw new IllegalArgumentException(e.getMessage(), e); 180 } 181 } 182 } 183 184 185 /** 186 * Checks if the prompt is valid. This is done by examining the prompt 187 * for a conflicting {@link Type#NONE} value. 188 * 189 * @return {@code true} if this prompt if valid, else {@code false}. 190 */ 191 public boolean isValid() { 192 193 return !(size() > 1 && contains(Type.NONE)); 194 } 195 196 197 /** 198 * Returns the string list representation of this prompt. 199 * 200 * @return The string list representation. 201 */ 202 public List<String> toStringList() { 203 204 List<String> list = new ArrayList<>(this.size()); 205 206 for (Type t: this) 207 list.add(t.toString()); 208 209 return list; 210 } 211 212 213 /** 214 * Returns the string representation of this prompt. The values are 215 * delimited by space. 216 * 217 * <p>Example: 218 * 219 * <pre> 220 * login consent 221 * </pre> 222 * 223 * @return The string representation. 224 */ 225 @Override 226 public String toString() { 227 228 StringBuilder sb = new StringBuilder(); 229 230 Iterator<Type> it = super.iterator(); 231 232 while (it.hasNext()) { 233 234 sb.append(it.next().toString()); 235 236 if (it.hasNext()) 237 sb.append(" "); 238 } 239 240 return sb.toString(); 241 } 242 243 244 /** 245 * Parses a prompt from the specified string list. 246 * 247 * @param collection The string list to parse, with one or more 248 * non-conflicting prompt types. May be {@code null}. 249 * 250 * @return The prompt, {@code null} if the parsed string list was 251 * {@code null} or empty. 252 * 253 * @throws ParseException If the string list couldn't be parsed to a 254 * valid prompt. 255 */ 256 public static Prompt parse(final Collection<String> collection) 257 throws ParseException { 258 259 if (collection == null) 260 return null; 261 262 Prompt prompt = new Prompt(); 263 264 for (String s: collection) 265 prompt.add(Prompt.Type.parse(s)); 266 267 if (! prompt.isValid()) 268 throw new ParseException("Invalid prompt: " + collection); 269 270 return prompt; 271 } 272 273 274 /** 275 * Parses a prompt from the specified string. 276 * 277 * @param s The string to parse, with one or more non-conflicting space 278 * delimited prompt types. May be {@code null}. 279 * 280 * @return The prompt, {@code null} if the parsed string was 281 * {@code null} or empty. 282 * 283 * @throws ParseException If the string couldn't be parsed to a valid 284 * prompt. 285 */ 286 public static Prompt parse(final String s) 287 throws ParseException { 288 289 if (StringUtils.isBlank(s)) 290 return null; 291 292 Prompt prompt = new Prompt(); 293 294 StringTokenizer st = new StringTokenizer(s, " "); 295 296 while (st.hasMoreTokens()) 297 prompt.add(Prompt.Type.parse(st.nextToken())); 298 299 if (! prompt.isValid()) 300 throw new ParseException("Invalid prompt: " + s); 301 302 return prompt; 303 } 304}