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