001package com.nimbusds.openid.connect.provider.spi.grants; 002 003 004import java.util.ArrayList; 005import java.util.Date; 006import java.util.List; 007 008import com.nimbusds.oauth2.sdk.ParseException; 009import com.nimbusds.oauth2.sdk.id.Subject; 010import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 011import com.nimbusds.openid.connect.sdk.claims.ACR; 012import com.nimbusds.openid.connect.sdk.claims.AMR; 013import net.jcip.annotations.Immutable; 014import net.minidev.json.JSONObject; 015import org.apache.commons.collections4.CollectionUtils; 016 017 018/** 019 * Identity (ID) token specification. 020 */ 021@Immutable 022public class IDTokenSpec extends OptionalTokenSpec { 023 024 025 /** 026 * None (no issue) ID token specification. 027 */ 028 public static final IDTokenSpec NONE = new IDTokenSpec(); 029 030 031 /** 032 * The time of the subject authentication. If {@code null} it will be 033 * set to now. 034 */ 035 private final Date authTime; 036 037 038 /** 039 * The Authentication Context Class Reference (ACR), {@code null} if 040 * not specified. 041 */ 042 private final ACR acr; 043 044 045 /** 046 * The Authentication Methods Reference (AMR) list, {@code null} if not 047 * specified. 048 */ 049 private final List<AMR> amrList; 050 051 052 /** 053 * Creates a new default ID token specification (no issue). 054 */ 055 public IDTokenSpec() { 056 057 this(false, 0L, null, null, null, null); 058 } 059 060 061 /** 062 * Creates a new ID token specification. 063 * 064 * @param issue Controls the ID token issue. If 065 * {@code true} an ID token must be issued, 066 * {@code false} to prohibit issue. 067 * @param lifetime The ID token lifetime, in seconds, zero 068 * if not specified (to let the Connect2id 069 * server apply the default configured 070 * lifetime for ID tokens). 071 * @param impersonatedSubject The subject in impersonation and 072 * delegation cases, {@code null} if not 073 * applicable. 074 */ 075 public IDTokenSpec(final boolean issue, 076 long lifetime, 077 final Subject impersonatedSubject) { 078 079 this(issue, lifetime, null, null, null, impersonatedSubject); 080 } 081 082 083 /** 084 * Creates a new ID token specification. 085 * 086 * @param issue Controls the ID token issue. If 087 * {@code true} an ID token must be issued, 088 * {@code false} to prohibit issue. 089 * @param lifetime The ID token lifetime, in seconds, zero 090 * if not specified (to let the Connect2id 091 * server apply the default configured 092 * lifetime for ID tokens). 093 * @param authTime The time of the subject authentication. 094 * If {@code null} it will be set to now. 095 * Applies only if an ID token is issued. 096 * @param acr The Authentication Context Class 097 * Reference (ACR), {@code null} if not 098 * specified. Applies only if an ID token is 099 * issued. 100 * @param amrList The Authentication Methods Reference 101 * (AMR) list, {@code null} if not 102 * specified. Applies only if an ID token is 103 * issued. 104 * @param impersonatedSubject The subject in impersonation and 105 * delegation cases, {@code null} if not 106 * applicable. 107 */ 108 public IDTokenSpec(final boolean issue, 109 final long lifetime, 110 final Date authTime, 111 final ACR acr, 112 final List<AMR> amrList, 113 final Subject impersonatedSubject) { 114 115 super(issue, lifetime, null, impersonatedSubject); 116 117 if (issue) { 118 // Applies only if a token is issued 119 this.authTime = authTime; 120 this.acr = acr; 121 this.amrList = amrList; 122 } else { 123 this.authTime = null; 124 this.acr = null; 125 this.amrList = null; 126 } 127 } 128 129 130 /** 131 * Returns the time of the subject authentication. 132 * 133 * @return The time of the subject authentication. If {@code null} it 134 * will be set to now. Applies only if an ID token is issued. 135 */ 136 public Date getAuthTime() { 137 138 return authTime; 139 } 140 141 142 /** 143 * Returns the Authentication Context Class Reference (ACR). 144 * 145 * @return The Authentication Context Class Reference (ACR), 146 * {@code null} if not specified. Applies only if an ID token 147 * is issued. 148 */ 149 public ACR getACR() { 150 151 return acr; 152 } 153 154 155 /** 156 * Returns The Authentication Methods Reference (AMR) list. 157 * 158 * @return The Authentication Methods Reference (AMR) list, 159 * {@code null} if not specified. Applies only if an ID token 160 * is issued. 161 */ 162 public List<AMR> getAMRList() { 163 164 return amrList; 165 } 166 167 168 @Override 169 public JSONObject toJSONObject() { 170 171 JSONObject o = super.toJSONObject(); 172 173 if (authTime != null) { 174 o.put("auth_time", authTime.getTime() / 1000L); 175 } 176 177 if (acr != null) { 178 o.put("acr", acr.getValue()); 179 } 180 181 if (CollectionUtils.isNotEmpty(amrList)) { 182 183 List<String> sl = new ArrayList<>(amrList.size()); 184 185 for (AMR amr: amrList) { 186 sl.add(amr.getValue()); 187 } 188 189 o.put("amr", sl); 190 } 191 192 return o; 193 } 194 195 196 /** 197 * Parses an ID token specification from the specified JSON object. 198 * 199 * @param jsonObject The JSON object. Must not be {@code null}. 200 * 201 * @return The ID token specification. 202 * 203 * @throws ParseException If parsing failed. 204 */ 205 public static IDTokenSpec parse(final JSONObject jsonObject) 206 throws ParseException { 207 208 OptionalTokenSpec optionalTokenSpec = OptionalTokenSpec.parse(jsonObject); 209 210 Date authTime = null; 211 212 if (jsonObject.containsKey("auth_time")) { 213 authTime = new Date(JSONObjectUtils.getLong(jsonObject, "auth_time") * 1000L); 214 } 215 216 ACR acr = null; 217 218 if (jsonObject.containsKey("acr")) { 219 acr = new ACR(JSONObjectUtils.getString(jsonObject, "acr")); 220 } 221 222 List<AMR> amrList = null; 223 224 if (jsonObject.containsKey("amr")) { 225 String[] sa = JSONObjectUtils.getStringArray(jsonObject, "amr"); 226 amrList = new ArrayList<>(sa.length); 227 for (String s: sa) { 228 amrList.add(new AMR(s)); 229 } 230 } 231 232 return new IDTokenSpec( 233 optionalTokenSpec.issue(), 234 optionalTokenSpec.getLifetime(), 235 authTime, 236 acr, 237 amrList, 238 optionalTokenSpec.getImpersonatedSubject()); 239 } 240}