001package com.nimbusds.openid.connect.provider.spi.grants; 002 003 004import java.util.ArrayList; 005import java.util.List; 006 007import net.jcip.annotations.Immutable; 008import net.minidev.json.JSONObject; 009 010import com.nimbusds.oauth2.sdk.ParseException; 011import com.nimbusds.oauth2.sdk.id.Audience; 012import com.nimbusds.oauth2.sdk.id.Subject; 013import com.nimbusds.oauth2.sdk.util.CollectionUtils; 014import com.nimbusds.oauth2.sdk.util.JSONObjectUtils; 015 016 017/** 018 * Base token specification. 019 */ 020@Immutable 021public class TokenSpec { 022 023 024 /** 025 * The token lifetime, in seconds. For access tokens zero and negative 026 * implies not specified (to let the Connect2id server apply the 027 * default configured access token lifetime). For refresh tokens zero 028 * implies permanent (no expiration) and negative not specified (to let 029 * the Connect2id server apply the default configured refresh token 030 * lifetime). 031 */ 032 private final long lifetime; 033 034 035 /** 036 * Explicit list of audiences for the token, {@code null} if none. 037 */ 038 private final List<Audience> audList; 039 040 041 /** 042 * The subject in impersonation and delegation cases, {@code null} if 043 * not applicable. 044 */ 045 private final Subject impersonatedSubject; 046 047 048 /** 049 * Creates a new token specification. No explicit token audience is 050 * specified. No subject in impersonation and delegation cases is 051 * specified. 052 * 053 * @param lifetime The token lifetime, in seconds. For access tokens 054 * zero and negative implies not specified (to let the 055 * Connect2id server apply the default configured 056 * access token lifetime). For refresh tokens zero 057 * implies permanent (no expiration) and negative not 058 * specified (to let the Connect2id server apply the 059 * default configured refresh token lifetime). 060 */ 061 public TokenSpec(final long lifetime) { 062 063 this(lifetime, null, null); 064 } 065 066 067 /** 068 * Creates a new token specification. 069 * 070 * @param lifetime The token lifetime, in seconds. For 071 * access tokens zero and negative implies 072 * not specified (to let the Connect2id 073 * server apply the default configured 074 * access token lifetime). For refresh 075 * tokens zero implies permanent (no 076 * expiration) and negative not specified 077 * (to let the Connect2id server apply the 078 * default configured refresh token 079 * lifetime). 080 * @param audList Explicit list of audiences for the token, 081 * {@code null} if not specified. 082 * @param impersonatedSubject The subject in impersonation and 083 * delegation cases, {@code null} if not 084 * applicable. 085 */ 086 public TokenSpec(final long lifetime, final List<Audience> audList, final Subject impersonatedSubject) { 087 088 this.lifetime = lifetime; 089 this.audList = audList; 090 this.impersonatedSubject = impersonatedSubject; 091 } 092 093 094 /** 095 * Returns the token lifetime. 096 * 097 * @return The token lifetime, in seconds. For access tokens zero and 098 * negative implies not specified (to let the Connect2id server 099 * apply the default configured access token lifetime). For 100 * refresh tokens zero implies permanent (no expiration) and 101 * negative not specified (to let the Connect2id server apply 102 * the default configured refresh token lifetime). 103 */ 104 public long getLifetime() { 105 106 return lifetime; 107 } 108 109 110 /** 111 * Returns the explicit list of audiences for the token. 112 * 113 * @return The explicit list of audiences for the token, {@code null} 114 * if not specified. 115 */ 116 public List<Audience> getAudience() { 117 118 return audList; 119 } 120 121 122 /** 123 * Returns the subject in impersonation and delegation cases. 124 * 125 * @return The subject in impersonation and delegation cases, 126 * {@code null} if not applicable. 127 */ 128 public Subject getImpersonatedSubject() { 129 130 return impersonatedSubject; 131 } 132 133 134 /** 135 * Returns a JSON object representation of this token specification. 136 * 137 * @return The JSON object. 138 */ 139 public JSONObject toJSONObject() { 140 141 JSONObject o = new JSONObject(); 142 143 if (lifetime >= 0L) { 144 o.put("lifetime", lifetime); 145 } 146 147 if (CollectionUtils.isNotEmpty(audList)) { 148 149 List<String> sl = new ArrayList<>(audList.size()); 150 151 for (Audience aud: audList) { 152 sl.add(aud.getValue()); 153 } 154 155 o.put("audience", sl); 156 } 157 158 if (impersonatedSubject != null) { 159 o.put("impersonated_sub", impersonatedSubject.getValue()); 160 } 161 162 return o; 163 } 164 165 166 @Override 167 public String toString() { 168 169 return toJSONObject().toJSONString(); 170 } 171 172 173 /** 174 * Parses a token specification from the specified JSON object. 175 * 176 * @param jsonObject The JSON object. Must not be {@code null}. 177 * 178 * @return The token specification. 179 * 180 * @throws ParseException If parsing failed. 181 */ 182 public static TokenSpec parse(final JSONObject jsonObject) 183 throws ParseException { 184 185 long lifetime = -1L; 186 187 if (jsonObject.containsKey("lifetime")) { 188 lifetime = JSONObjectUtils.getLong(jsonObject, "lifetime"); 189 } 190 191 List<Audience> audList = null; 192 193 if (jsonObject.containsKey("audience")) { 194 audList = Audience.create(JSONObjectUtils.getStringList(jsonObject, "audience")); 195 } 196 197 Subject impersonatedSub = null; 198 199 if (jsonObject.containsKey("impersonated_sub")) { 200 try { 201 impersonatedSub = new Subject(JSONObjectUtils.getString(jsonObject, "impersonated_sub")); 202 } catch (IllegalArgumentException e) { 203 // On invalid subject 204 throw new ParseException(e.getMessage(), e); 205 } 206 } 207 208 return new TokenSpec(lifetime, audList, impersonatedSub); 209 } 210}