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 java.util.*; 022 023import net.jcip.annotations.NotThreadSafe; 024 025import com.nimbusds.oauth2.sdk.ParseException; 026import com.nimbusds.oauth2.sdk.util.StringUtils; 027 028 029/** 030 * Prompts for end-user re-authentication and consent. 031 * 032 * <p>Related specifications: 033 * 034 * <ul> 035 * <li>OpenID Connect Core 1.0, section 3.1.2.1. 036 * <li>Initiating User Registration via OpenID Connect (draft 04) 037 * </ul> 038 */ 039@NotThreadSafe 040public class Prompt extends LinkedHashSet<Prompt.Type> { 041 042 043 private static final long serialVersionUID = -3672900533669609699L; 044 045 046 /** 047 * Enumeration of the prompt types. 048 */ 049 public enum Type { 050 051 052 /** 053 * The authorisation server must not display any authentication 054 * or consent UI pages. An error is returned if the end user is 055 * not already authenticated or the client does not have 056 * pre-configured consent for the requested {@code scope}. This 057 * can be used as a method to check for existing authentication 058 * and / or consent. 059 */ 060 NONE, 061 062 063 /** 064 * The authorisation server must prompt the end-user for 065 * re-authentication. 066 */ 067 LOGIN, 068 069 070 /** 071 * The authorisation server must prompt the end-user for 072 * consent before returning information to the client. 073 */ 074 CONSENT, 075 076 077 /** 078 * The authorisation server must prompt the end-user to select 079 * a user account. This allows a user who has multiple accounts 080 * at the authorisation server to select amongst the multiple 081 * accounts that they may have current sessions for. 082 */ 083 SELECT_ACCOUNT, 084 085 086 /** 087 * The client desires the OpenID provider to present the 088 * end-user with an account creation user interface instead of 089 * the normal login flow. Care must be taken if combining this 090 * value with other prompt values. Mutually exclusive 091 * conditions can arise so it is RECOMMENDED that create not be 092 * present with any other values. 093 */ 094 CREATE; 095 096 097 /** 098 * Returns the string identifier of this prompt type. 099 * 100 * @return The string identifier. 101 */ 102 @Override 103 public String toString() { 104 105 return super.toString().toLowerCase(); 106 } 107 108 109 /** 110 * Parses a prompt type. 111 * 112 * @param s The string to parse. 113 * 114 * @return The prompt type. 115 * 116 * @throws ParseException If the parsed string is {@code null} 117 * or doesn't match a prompt type. 118 */ 119 public static Type parse(final String s) 120 throws ParseException { 121 122 if (StringUtils.isBlank(s)) 123 throw new ParseException("Null or empty prompt type string"); 124 125 if ("none".equals(s)) { 126 return NONE; 127 } else if ("login".equals(s)) { 128 return LOGIN; 129 } else if ("consent".equals(s)) { 130 return CONSENT; 131 } else if ("select_account".equals(s)) { 132 return SELECT_ACCOUNT; 133 } else if ("create".equals(s)) { 134 return CREATE; 135 } else { 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}